延续上一篇iOS |知识点整理(9)
alignmentRectInsets
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var xAxis: UIView!
@IBOutlet weak var yAxis: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// create the custom view
let customView = BadgedRoundedButton() // *
customView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(customView)
customView.backgroundColor = UIColor.redColor()
NSLayoutConstraint.activateConstraints([
customView.widthAnchor.constraintEqualToConstant(100),
customView.heightAnchor.constraintEqualToConstant(100),
])
// add the content view (image) to the custom view
let contentView = UIView()
contentView.backgroundColor=UIColor.yellowColor()
contentView.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(contentView)
NSLayoutConstraint.activateConstraints([
contentView.leadingAnchor.constraintEqualToAnchor(customView.leadingAnchor),
contentView.bottomAnchor.constraintEqualToAnchor(customView.bottomAnchor),
contentView.heightAnchor.constraintEqualToAnchor(customView.heightAnchor), // *
contentView.widthAnchor.constraintEqualToAnchor(customView.widthAnchor) // *
])
// add the badge (label) to the custom view
let badge = UILabel()
badge.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(badge)
badge.backgroundColor = UIColor.greenColor().colorWithAlphaComponent(0.5)
badge.font = UIFont(name: "GillSans", size: 14)
badge.textAlignment = .Center
badge.text = "567"
NSLayoutConstraint.activateConstraints([
badge.centerXAnchor.constraintEqualToAnchor(contentView.trailingAnchor),
badge.centerYAnchor.constraintEqualToAnchor(contentView.topAnchor),
])
// position the whole thing with respect to the custom view
NSLayoutConstraint.activateConstraints([
customView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor), // *
customView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor), // *
])
view.bringSubviewToFront(xAxis)
view.bringSubviewToFront(yAxis)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//BadgedRoundedButton 代码
import UIKit
class BadgedRoundedButton: UIView {
override func alignmentRectInsets() -> UIEdgeInsets {
return UIEdgeInsetsMake(10, 0, 0, 10)
}
}
当设置了alignmentRectInsets以后,红色的大小变成了 110 110
layoutMargins的使用
在iOS 8中,可以使用layoutMargins去定义view之间的间距,该属性只对AutoLayout布局生效。 因此AutoLayout中NSLayoutAttribute的枚举值有了相应的更新:
NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
通过在Xcode中测试打印,发现UIView默认的layoutMargins的值为 {8, 8, 8, 8},我们可以通过修改这个值来改变View之间的距离。 可以通过改变layoutMargin来实现一些动画效果:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
yellowView.layoutMargins=UIEdgeInsetsMake(50, 50, 50, 50);
[UIView animateWithDuration:0.5 animations:^{
[yellowView setNeedsLayout];
[yellowView layoutIfNeeded];
}];
});
关于消息转发机制
结合NSObject文档可以知道,_objc_msgForward消息转发做了如下几件事:
1.调用resolveInstanceMethod:方法,允许用户在此时为该Class动态添加实现。如果有实现了,则调用并返回。如果仍没实现,继续下面的动作。
2.调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接转发给它。如果返回了nil,继续下面的动作。
3.调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。
4.调用forwardInvocation:方法,将地3步获取到的方法签名包装成Invocation传入,如何处理就在这里面了。
5.若没有实现- (void)forwardInvocation:(NSInvocation *)anInvocation方法,那么会进入- (void)doesNotRecognizeSelector:(SEL)aSelector方法。若我们没有实现这个方法,那么就会crash,然后提示打不到响应的方法。到此,动态解析的流程就结束了。
上面这4个方法均是模板方法,开发者可以override,由runtime来调用。最常见的实现消息转发,就是重写方法3和4,吞掉一个消息或者代理给其他对象都是没问题的。
5.其他注意事项
由上面介绍可以知道,一个objc程序启动后,需要进行类的初始化、调用方法时的cache初始化,所以会有一段"热身"的时间。之后,再发送消息的时候就直接走缓存了,所以消息发送的效率非常高,且没有牺牲动态特性。
如果希望避免方法查找带来的那一丁点开销,可以用methodForSelector 手动获得IMP来直接调用。用 methodForSelector 获取IMP时,会尝试forward机制, 所以在没有对应方法时,返回的是 _objc_msgForward ,不会返回NULL。
用 respondsToSelector 判断对象是否能响应消息时, 会避开forward机制 ,但是该方法会尝试一次resolveInstanceMethod。
关于UITableViewCell的使用中遇到的问题
1.碰到如下情况,有个别的cell没有显示出来,发现是如下原因:
相就cell的高度在tableView:heightForRowAtIndexPath:返回为零.所以这个cell就直接不显示出来了.
My first suggestion would be to investigate your tableView:heightForRowAtIndexPath: method, and 1) check if it's getting called with an index path row of 0, and 2) make sure something > 0 is being returned.
2.注意方法的调用顺序:
tableView:heightForRowAtIndexPath: 先调用,然后才调用
tableView:cellForRowAtIndexPath:
所以高度的计算不应和特定的实例相关联.
3.自定义cell的一般步骤是:
重写initWithStyle:style reuseIdentifier:方法,在这个方法中添加自定义的这个cell中自定义的view. 然后当给cell设置数据的时候,再改变相应view的属性.
4.关于多行UILable中使用约束的问题.
当对字符串使用 size= boundingRectWithSize:options: attributes: context:
的时候,求出的size会被四舍五入,这样就会造成文字可能会在UILabel中不会正常显示. 一般做法是ceil(size.height)
,求出最接近size的最大整数.即可.
I had the same Problem. I found the following in the Documentation:
To correctly draw and size multi-line text, pass NSStringDrawingUsesLineFragmentOrigin in the options parameter. This method returns fractional sizes (in the size component of the returned CGRect); to use a returned size to size views, you must raise its value to the nearest higher integer using the ceil function.
So this solved it for me:
CGRect rect = [myLabel.text boundingRectWithSize:CGSizeMake(myLabel.frame.size.width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: myLabel.font}
context:nil];
rect.size.width = ceil(rect.size.width);
rect.size.height = ceil(rect.size.height);
ref:stackoverflow.com/questions/1…
UITableViewCell 和 layoutSubviews
layoutSubviews在以下情况下会被调用:
1、init初始化不会触发layoutSubviews
2、addSubview会触发layoutSubviews
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4、滚动一个UIScrollView会触发layoutSubviews
5、旋转Screen会触发父UIView上的layoutSubviews事件
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
layoutSubviews is called at some point after tableView:willDisplayCell:, which is called after tableView:cellForRowAtIndexPath:. You can verify this by setting breakpoints in the relevant methods and seeing the order in which they get hit. For example, set a breakpoint at the end of tableView:cellForRowAtIndexPath:. Then add the following method to FiltersTableViewCell
- (void)layoutSubviews
{
[super layoutSubviews];
}
and set a breakpoint there. Then run the app and see what happens.
But note, even if layoutSubviews is called after willDisplayCell, that doesn't mean that's when the layout pass for sake of self-sizing is done. I'm finding that a self-sizing cell that isn't populated untilwillDisplayCell doesn't get the correct height, even if it also forces a layout pass by callinglayoutIfNeeded. I had to change my code to populate in cellForRowAtIndexPath instead for self-sizing to work. – smallduck Mar 21 at 17:28
在动画中附加额外信息
You can set key/value objects for CAAnimation instance like this:
CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[theAnimation setValue:@"animation1" forKey:@"id"];
theAnimation.delegate = self;
CABasicAnimation *theAnimation2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
[theAnimation2 setValue:@"animation2" forKey:@"id"];
theAnimation2.delegate = self;
Check which one was called in delegate method:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if([[anim valueForKey:@"id"] isEqual:@"animation1"]) {
NSLog(@"animation1");
}
if([[anim valueForKey:@"id"] isEqual:@"animation2"]) {
NSLog(@"animation2");
}
}
UIActionSheet在IOS7下取消按钮会跑到上面去,解决方法是把取消按钮,当作其它按钮放在最后面. 然后设置一下cancelButtonIndex
actionSheet.cancelButtonIndex=otherButtonTitles.count;
将UISearchDisplayController局限在UIViewController里,而不让搜索框在UINavigationBar里显示的方法:
网上也有其它的方法,比如,重写UISearchDisplayController:
@interface MySearchDisplayController : UISearchDisplayController
@end
@implementation MySearchDisplayController
- (void)setActive:(BOOL)visible animated:(BOOL)animated
{
[super setActive: visible animated: animated];
[self.searchContentsController.navigationController setNavigationBarHidden: NO animated: NO];
}
@end
还有一个方法,就是将引用UISearchDisplayController的UIViewController,当作一个ChildViewController放在另外一个Parent UIViewController 当中. 这样UISearchDisplayController就只会在Parent UIViewController中显示了,而不会覆盖在NavigationBar上. 究其原因,主要是因为,这时,ChildViewController的NavigationController为空了,所以不会覆盖在NavigationBar上了.
获取当前VC
-(id)getCurrentViewController
{
id WindowRootVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
id currentViewController = [self findTopViewController:WindowRootVC];
return currentViewController;
}
-(id)findTopViewController:(id)inController
{
/* if ur using any Customs classes, do like this.
* Here SlideNavigationController is a subclass of UINavigationController.
* And ensure you check the custom classes before native controllers , if u have any in your hierarchy.
if ([inController isKindOfClass:[SlideNavigationController class]])
{
return [self findTopViewController:[inController visibleViewController]];
}
else */
if ([inController isKindOfClass:[UITabBarController class]])
{
return [self findTopViewController:[inController selectedViewController]];
}
else if ([inController isKindOfClass:[UINavigationController class]])
{
return [self findTopViewController:[inController visibleViewController]]; //获取当前可视的VC
}
else if ([inController isKindOfClass:[UIViewController class]])
{
return inController;
}
else
{
NSLog(@"Unhandled ViewController class : %@",inController);
return nil;
}
}
-(void)someMethod
{
id currentVC = [self getCurrentViewController];
if (currentVC)
{
NSLog(@"currentVC :%@",currentVC);
}
}
ref:stackoverflow.com/questions/2…
- (UIViewController *)appRootViewController
{
UIViewController *appRootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
UIViewController *topVC = appRootVC;
while (topVC.presentedViewController) {
topVC = topVC.presentedViewController;
}
while (topVC.presentingViewController) {
topVC = topVC.presentedViewController;
}
return topVC;
}
在一个由TabBarViewController为框架的app中,如果要获取当前的NavigationController.那么可以使用下面的方法.
navcon = (UINavigationController*)myTabBarController.selectedViewController;
[navcon pushViewController:someViewController animated:YES];
无法在UITableViewCell中直接修改cell.textLable的frame的,只能通过继承UITableViewCell,然后在layoutSubview方法中,改变cell.textLabel的frame.
或者是:
override layoutSubviews for your UITableViewCell...
- (void)layoutSubviews {
[super layoutSubviews];
CGSize size = self.bounds.size;
CGRect frame = CGRectMake(4.0f, 4.0f, size.width, size.height);
self.textLabel.frame = frame;
self.textLabel.contentMode = UIViewContentModeScaleAspectFit;
}
or you can simply make your own UILabel object, and add it to cell.contentView as a subview.
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(4, 4, 30, 30)];
[cell.contentView addSubview:label];
[label release];
当使用下面的predicate进行filter的时候,
如果属性为整形,那么predicateWithFormat,必须有%d之类的.
如果相关属性为字符串类型, 那么那么predicateWithFormat,必须有’%d'之类的,或者是%@
今天犯得一个错误:
NSString* pckid= item[@"pckId"];
value.pckId 为整形:
//下面结果为空
NSArray* f=[_array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"value.pckId==%
@",pckid]];
//下面的结果正常
NSArray* f=[_array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@“value.pckId==%d",pckid.integerValue]];
//////// departmentLevel 为字符串///////////////
//////// 注意后面的类型一定要和相应属性对应的类型一致/////////
//结果正常
NSLog(@"%@",[self.allData filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"departmentLevel = '%ld'",self.depth]]]);
//当departmentLevel整形时
NSLog(@"%@",[self.allData filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"departmentLevel = %ld",self.depth]]]);
//得到空的NSArray
NSLog(@"%@",[self.allData filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"departmentLevel = %@",@(self.depth)]]);
//////// departmentLevel 为整数///////////////
//结果正常
NSLog(@"%@",[self.allData filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"departmentLevel = %ld",self.depth]]]);
////////////// IN 的使用////////////////////////////
filterArray=[filterArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K IN %@",@"value.itemId",itemArray]];
////////////////////// %K的使用 //////////////////
//如果想让属性作为参数,也动态变化,那么就得使用%K
filterArray=[filterArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K IN %@",@"value.itemId",itemArray]];
//注意在使用占位符的时候后面$后面的属性要大写,在使用的时候也要用和定义的时候一样的大写。
NSPredicate *predicateTemplate = [NSPredicate predicateWithFormat:@"name == $NAME"];
NSDictionary *varDict = [NSDictionary dictionaryWithObjectsAndKeys:@"Herbie",@"NAME", nil];
predicate = [predicateTemplate predicateWithSubstitutionVariables:varDict];
myFavCars = [cars filteredArrayUsingPredicate:predicate];
NSLog(@"-----%@",myFavCars);
//engine.horsepower为NSInteger类型
predicateTemplate = [NSPredicate predicateWithFormat:@"engine.horsepower > $POWER"];
varDict = @{@"POWER":@150};
predicate = [predicateTemplate predicateWithSubstitutionVariables:varDict];
myFavCars = [cars filteredArrayUsingPredicate:predicate];
NSLog(@"+++++++%@",myFavCars);
Xcode可以自动附加到进程
With your device connected to your Mac, select Product -> Attach to Process -> By Process Identifier (PID) or Name... in Xcode. In the dialog sheet, enter the name of your app as it appears in the Debug navigator when started via Xcode.
If the app is already running, the debugger will attach to the running process. If it isn't running, it will wait for the app to launch and then attach.
直接将static library的工程添加到项目中引用的方法:
1.在项目中新建立一个目录,将整个static library的所有文件拷贝到这个文件夹里面
2.将上面新建目录中的xcodeproj文件拖到项目里.
3.在下面两个选项中分别添加static library对应的工程和库文件
Link Binaries with Libraries.
Target Dependencies,
4.在Header Search Path另添加static library对外的头文件.
ref:www.jianshu.com/p/656838ae9…
打印环境变量
You can also get this information from the command line using the xcodebuild command with the -showBuildSettings switch:
$ xcodebuild -project myProj.xcodeproj -target "myTarg" -showBuildSettings
Since we use an xcworkspace I am putting the equivalent command for that: xcodebuild -workspace WORKSPACE.xcworkspace -scheme SCHEME -showBuildSettings
继承基于NIB的VC的写法:
@interface FDAdviceResidentSelectVC : FDResidentSelectVC
@end
@implementation FDAdviceResidentSelectVC
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:@"FDResidentSelectVC" bundle:[NSBundle mainBundle]];
return self;
}
@end
Like Jaanus told :
Calling this (-selectRowAtIndexPath:animated:scrollPosition:) method does not cause the delegate to receive a tableView:willSelectRowAtIndexPath: or tableView:didSelectRowAtIndexPath: message, nor will it send UITableViewSelectionDidChangeNotification notifications to observers. So you just have to call the delegate method yourself.
For example :
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:1 inSection:0];
[self.tableView selectRowAtIndexPath:indexPath
animated:YES
scrollPosition:UITableViewScrollPositionNone];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
Swift version:
let indexPath = NSIndexPath(forRow: 0, inSection: 0);
self.tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: UITableViewScrollPosition.None)
self.tableView(self.tableView, didSelectRowAtIndexPath: indexPath)
(lldb) po (CGRect)[[self view] frame]
(origin = (x = 0, y = 0), size = (width = 320, height = 480))
(origin = (x = 0, y = 0), size = (width = 320, height = 480))
(lldb) po (CGRect)[[self view] frame]
(origin = (x = 0, y = 0), size = (width = 375, height = 667))
(origin = (x = 0, y = 0), size = (width = 375, height = 667))
ViewDidLoad
This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView() method. You usually override this method to perform additional initialization on views that were loaded from nib files.
loadView
You should never call this method directly. The view controller calls this method when its view property is requested but is currently nil. This method loads or creates a view and assigns it to the view property.
If the view controller has an associated nib file, this method loads the view from the nib file. A view controller has an associated nib file if the nibName property returns a non-nil value, which occurs if the view controller was instantiated from a storyboard, if you explicitly assigned it a nib file using the init(nibName:bundle:) method, or if iOS finds a nib file in the app bundle with a name based on the view controller's class name. If the view controller does not have an associated nib file, this method creates a plain UIView object instead.
If you use Interface Builder to create your views and initialize the view controller, you must not override this method.
You can override this method in order to create your views manually. If you choose to do so, assign the root view of your view hierarchy to the view property. The views you create should be unique instances and should not be shared with any other view controller object. Your custom implementation of this method should not call super.
If you want to perform any additional initialization of your views, do so in the viewDidLoad() method.
NSLocale
若你只开发中国区的应用,需要保证用户修改当前语言环境时应用的显示不发生变化。而像NSDateFormatter这样的类,会根据设备的设置,自动返回不同语言的数据。为了保证返回数据的语言一致,我们需要设置NSLocale。 下面的代码就可以保证在任何语言环境下,只返回中文的数据:
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh"];
NSDateFormatter *secondDateFormatter = [[NSDateFormatter alloc] init];
[secondDateFormatter setDateFormat:@"cccc"];
secondDateFormatter.locale = locale;
NSDate *date = [NSDate date];
NSLog(@"%@", [secondDateFormatter stringFromDate:date]);
关于bitcode
在bitcode出现之前,app中包含兼容arm和arm64架构的代码,然后在特定的机器上,只使用其中一个cpu架构的代码,其它架构的代码只会浪费用户宝贵的硬盘空间. 在使用bitcode之后呢,苹果会把它分别编译成支持特定cpu架构的程序.这样在用户下载的时候,就可以根据用户所使用机器的cpu架构,来下载特定的程序.从而节省了用户的硬盘空间.
Summary
- Bitcode is a bitstream file format for LLVM IR
- one of its goals is to decrease size of an app by eliminating unused object code
- malefactor can obtain your app or library, retrieve the IR from it and steal your ‘secret algorithm’ Bitcode Demystified - Low Level Bits
跨平台编译的宏定义选择(区分ios和mac)
* #if TARGET_OS_MAC
* #if TARGET_OS_IPHONE
* #if TARGET_IPHONE_SIMULATOR
* #if TARGET_OS_EMBEDDED
* #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
* #if defined(__IPHONE_OS_MIN_VERSION_REQUIRED)
//当status bar 隐藏的时候,下面的代码返回的CGRectZero
CGRect rectStatus = [[UIApplication sharedApplication] statusBarFrame];
关于weak link
引入weak link的目的:
One challenge faced by developers is that of taking advantage of new features introduced in new versions of OS X while still supporting older versions of the system. Normally, if an application uses a new feature in a framework, it is unable to run on earlier versions of the framework that do not support that feature. Such applications would either fail to launch or crash when an attempt to use the feature was made. Apple has solved this problem by adding support for weakly-linked symbols. When a symbol in a framework is defined as weakly linked, the symbol does not have to be present at runtime for a process to continue running. The static linker identifies a weakly linked symbol as such in any code module that references the symbol. The dynamic linker uses this same information at runtime to determine whether a process can continue running. If a weakly linked symbol is not present in the framework, the code module can continue to run as long as it does not reference the symbol. However, if the symbol is present, the code can use it normally. If you are updating your own frameworks, you should consider making new symbols weakly linked. Doing so can make it easier for clients of your framework to support it. You should also make sure that your own code checks for the existence of weakly-linked symbols before using them.
To weak link a class e.g. MyUploadManager in your own executable:
-
To keep the linker happy, add this to Other Linker Flags in the project:
-Wl,-U,OBJC_CLASS$_MyUploadManager This allows the class symbol to be undefined even if it is not built into your executable. It will be considered for dynamic lookup instead, effectively the same as a dynamic library symbol.
-
To keep the runtime happy, add this to your class header:
attribute((weak_import)) @interface MyUploadManager When the dynamic linker runs, it substitutes a nil for the class symbol rather than crashing.
Now you can run this without either linker or runtime errors:
if ([MyUploadManager class]) {
self.uploadButton.hidden = NO;
}
Note: As of Xcode 7, the -U linker options conflicts with BitCode, so you may not be able to use this technique for future projects.
Summary
-
Bitcode is a bitstream file format for LLVM IR
-
one of its goals is to decrease size of an app by eliminating unused object code
-
malefactor can obtain your app or library, retrieve the IR from it and steal your ‘secret algorithm’ Bitcode Demystified - Low Level Bits
NSDateFormatter *formatter2 = [[NSDateFormatter alloc]init]; [formatter2 setDateFormat:@"EEE MMM dd HH:mm:ss z yyyy"]; //下面是必须的 [formatter2 setLocale:[[NSLocale alloc]initWithLocaleIdentifier:@"en_US"]];
// [formatter2 setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"CST"]]; NSDate *time = [formatter2 dateFromString:@"Mon Jun 27 10:30:10 CST 2016"]; { //以当前时区显示时间 NSDateFormatter *format = [[NSDateFormatter alloc] init]; [format setDateFormat:@"yyyy年MM月dd HH:mm:ss"]; NSString *dateString = [format stringFromDate:time]; NSLog(@"%@",dateString); }
NSDate 没有时区概念,可以理解为时区就是标准时区。中国大陆使用的时间为东八区的时间,即中国大陆时间比标准时间快 8 个小时,因此由 NSDate 转换成的字符串应该比 NSLog 输出的 NSDate 对象快 8 小时。我们在将 NSDate 对象转化成对应中国大陆时区的字符串时,应该设置 timeZone 为东八区,设置代码为 dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT: 3600 * 8]; NSDate 和 NSString 对象相互转换,及 NSDateFormatter 的使用 | 疯清扬的博客
关于Cocopods中module的理解.
当我们安装一个module的时候,一个module通常分为core module和其它的些child module. 在默认情况下是安装该module下所有的child module的. 当然如果指定了 s.default_subspec = ‘Core’,那就就只安装Core模块. 如果只安装其中一个child module的时候,那么:
pod 'ShareKit/Twitter', '2.0'
只安装ShareKit中的Twitter child module以及它的dependency
AFNetWorking的pod spec文件
建立xcconfig文件,分别针对debug和release,
如debug版的xcconfig文件对应如下:
APP_NAME = 测家庭医-医生
如release版的xcconfig文件对应如下:
APP_NAME = 家庭医-医生
那么在plist文件中的bundle display name的值中填写如下:
${APP_NAME}
就可以针对不同的target生成不同的程序名称.
ref:Xcode 使用 Configuration 来配置不同的项目环境 - isaced
用dispatch semaphore控制并发数量:
在同一时间,只有一个处理 block 的实例被分配,如果这个处理方法还没有执行完毕,另一个事件就发生了,事件会以指定方式(ADD或 OR)进行累积。DispatchSource能通过合并事件(block)的方式确保在高负载下正常工作。当处理事件被最终执行时,计算后的数据可以通过 dispatch_source_get_data 来获取。
用户事件有两种: DISPATCH_SOURCE_TYPE_DATA_ADD 和 DISPATCH_SOURCE_TYPE_DATA_OR.用户事件源有个 unsigned long data属性,我们将一个 unsigned long传入 dispatch_source_merge_data。当使用 _ADD版本时,事件在联结时会把这些数字相加。当使用 _OR版本时,事件在联结时会把这些数字逻辑与运算。当事件句柄执行时,我们可以使用dispatch_source_get_data函数访问当前值,然后这个值会被重置为0。 让我假设一种情况。假设一些异步执行的代码会更新一个进度条。因为主线程只不过是GCD的另一个dispatch queue而已,所以我们可以将GUI更新工作push到主线程中。然而,这些事件可能会有一大堆,我们不想对GUI进行频繁而累赘的更新,理想的情况是当主线程繁忙时将所有的改变联结起来。
用dispatch source就完美了,使用DISPATCH_SOURCE_TYPE_DATA_ADD,我们可以将工作拼接起来,然后主线程可以知道从上一次处理完事件到现在一共发生了多少改变,然后将这一整段改变一次更新至进度条。
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(source, ^{
[progressIndicator incrementBy:dispatch_source_get_data(source)];
});
dispatch_resume(source);
dispatch_apply([array count], globalQueue, ^(size_t index) {
// do some work on data at index
dispatch_source_merge_data(source, 1);
});
现在你可能会想,听起来倒是不错,但是要是我不想让事件被联结呢?有时候你可能想让每一次信号都会引起响应,什么后台的智能玩意儿统统不要。啊。。其实很简单的,别把自己绕进去了。如果你想让每一个信号都得到响应,那使用dispatch_async函数不就行了。实际上,使用的dispatch source而不使用dispatch_async的唯一原因就是利用联结的优势。
dispatch_group_t group=dispatch_group_create();
// dispatch_queue_t queue=dispatch_get_main_queue();//死锁
dispatch_queue_t queue=dispatch_queue_create("myqueeu", NULL);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_group_leave(group);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"3");
输出:
1
2
3
//下面这种情况也是死锁
dispatch_group_t group=dispatch_group_create();
dispatch_queue_t queue=dispatch_queue_create("myqueeu", NULL);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"1");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_group_leave(group);
});
});
//在这里主线程被阻塞
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"3");
21.dispatch_barrier_async:可以用这个函数,来保证同一时间,dispath_queue里,只有这个函数里block对应的任务在进行.当这个任务操作完成之后,才可以进行其它任务.
应用场景:对于一个文件,可以有多个读操作,但是在写操作的时候,就不能有读操作.
dispatch_async(queue, blk0_for_reading);
dispatch_async(queue, blk1_for_reading);
dispatch_async(queue, blk2_for_reading);
dispatch_async(queue, blk3_for_reading);
dispatch_barrier_async(queue, blk_for_writing); //Task that is added by dispatch_barrier_async ,means that Only one task can run at the same time
dispatch_async(queue, blk4_for_reading);
dispatch_async(queue, blk5_for_reading);
dispatch_async(queue, blk6_for_reading);
dispatch_async(queue, blk7_for_reading);
只有与自定义的并行队列一起使用,才能让 barrier 达到我们所期望的栅栏功能。 与 串行队列或者 global 队列 一起使用,barrier 的表现会和 dispatch_sync 方法一样。
HTTP Caching is way to optimize the response time and speed up the data delivery where the data is delivered from the load balancer once cached. HTTP Post requests are supposed to update the data on the server thus there is no point in caching the response as every Post request in real life could be different.
The corresponding RFC 2616 in section 9.5 (POST) allows the caching of the response to a POST message, if you use the appropriate headers.
Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource. Note that the same RFC states explicitly in section 13 (Caching in HTTP) that a cache must invalidate the corresponding entity after a POST request.
Some HTTP methods MUST cause a cache to invalidate an entity. This is either the entity referred to by the Request-URI, or by the Location or Content-Location headers (if present). These methods are:
- PUT
- DELETE
- POST
It's not clear to me how these specifications can allow meaningful caching.
- 如果借助了 Last-Modified 和 ETag,那么缓存策略则必须使用 NSURLRequestReloadIgnoringCacheData 策略,忽略缓存,每次都要向服务端进行校验。
在UIViewController中添加了两个子viewController,在运行时崩溃,解决方法:
[self.view layoutIfNeeded];
as the last line of the viewDidLayoutSubviews method on my VC fixed it for me.