线程时运行时执行的一组指令序列,每一个进程至少包含一个线程。在iOS中,进程启动时的主要线程称做主线程。全部UI元素都须要在主线程中建立和管理。html
每一个线程都有必定的开销,从而影响到应用的性能。线程不只仅有建立时的时间开销,还会消耗内核的内存,即应用的内存空间。git
每一个线程大约消耗1KB的内核内存空间。这块内存用于存储于线程有关的数据结构和属性。这块内存时联动内存(联动内存(Wired Memory)中的内容必须保留在内存中而不能被移动到磁盘或其余外部存储中,联动内存的容量取决于当前使用的应用软件)。github
主线程的栈空间大小为1M,并且没法修改。全部的二级线程默认分配512KB的栈空间。 在线程启动前,栈空间的大小能够被改变。栈空间的最小值是16KB,并且其数值必须是4KB的倍数。编程
//修改栈空间 +(NSThread *)createThreadWithTarget:(id)target selector:(SEL)selector object:(id)argument stackSize:(NSUInteger)size{ if ((size % 4096) != 0 ){ return nil; } NSThread *t = [[NSThread alloc]initWithTarget:target selector:selector object:argument]; t.stackSize = size; return t; }
GCD API:https://developer.apple.com/documentation/dispatch?language=objc GCD提供的功能列表:swift
GCD一样解决了线程的建立和管理,它帮助咱们跟踪应用中的线程的总数,且不会形成任何泄露。缓存
大多数状况下,应用单独使用GCD就能够很好的工做,但仍有特定的状况须要考虑使用NSThread或NSOperationQueue。当应用中有多个长耗时的任务须要并行执行时,最好,对线程的建立过程加以口控制。若是代码执行的时间过长,颇有可能达到线程的限制64个,即GCD的线程池上限安全
NSOperation封装了一个任务以及和任务相关的数据和代码,而NSOperationQueue以先入先出的顺序控制了一个或多个这类任务的执行。 NSOperation和NSOperation都提供了控制线程个数的能力。可用maxConcurrentOperation属性控制队列的个数,也能够控制每一个队列的线程个数。 NSThread,NSOperation和GCD API的快速比较:服务器
GCDcookie
NSOperation网络
NSThread
NSOperation是多核安全的。能够放心地分享队列,从不一样的线程中提交任务,而无需担忧损坏队列
@property(atomic) NSString *atomic;//原子属性 @property(nonatomic) NSString *Nonatomic;//非原子属性
由于原子属性存在开销,因此过分使用它们并不明智。若是能保证某个属性在任什么时候刻都不会被多个线程访问,最好仍是标记为nonatomic。
原子属性并不能保证代码必定是线程安全的。 全部相关的状态都应该再同一个事物中批量更新。 使用@synchronized指令能够建立一个信号量,并进入临界区,临界区在任什么时候刻都只能被一个线程执行。
-(void)updateUser:(HPUser *)user properties:(NSDictionary *)properties{ @synchronized(user){//取得针对user对象的锁。一切相关的修改都会被一同处理,而不会发生竞争状态。 NSString *fn = [properties objectForKey:@"firstName"]; if (fn != nil){ user.firstName = fn; } NSString *ln = [properties objectForKey:@"lastName"]; if (ln != nil){ user.lastName = ln; } } }
过渡使用@synchronized指令会拖慢应用的运行速度,由于任什么时候间都只有一个线程在临界区内执行。 经过uesr对象获取锁。所以updateUser: properties:方法能够从多个线程被调用,不一样的用户用不一样的线程。当user对象不一样时,该方法仍然可以高并发地执行。 获取锁的对象是良好定义的临界区的关键。做为经验法则,能够选择状态会被访问和修改的对象做为信号量的引用
锁是进入临界区的基础构件。atomic属性和@synchronized块是为了实现便捷实用的高级别抽象。
@interface ThreadSafeClass : NSObject { NSLock *lock; } @end @implementation ThreadSafeClass -(instancetype)init{ if (self = [super init]){ self -> lock = [NSLock new]; } return self; } -(void)safeMethod{ [self -> lock lock];//获取锁,进入临界区 //线程安全代码,在临界区,任意时刻最多只容许一个线程执行 [self -> lock unlock];//释放锁标记着临界区的结束。其余线程如今可以获取锁了。 } @end
@interface ThreadSafeClass : NSObject { NSRecursiveLock *rLock; } @end @implementation ThreadSafeClass -(instancetype)init{ if (self = [super init]){ self -> rLock = [NSRecursiveLock new]; } return self; } -(void)safeMethod{ [self -> rLock lock];//safeMethod获取锁 [self safeMethod2];//调用safeMethod2方法 [self -> rLock unlock];//safeMethod释放了锁。由于每一个锁定操做都有一个相应的解锁操做与之匹配,因此锁如今被释放,并能够被其余线程所获取。 } -(void)safeMethod2{ [self -> rLock lock];//safeMethod2从已经获取到的锁再次获取了锁 //线程安全的代码 [self -> rLock unlock];//safeMethod2释放了锁 }
NSCondition
NSCondition能够原子性地释放锁,从而使得其余等待的线程能够获取锁,而初始的线程继续等待。
@implementation Producer //1生产者的初始化器须要用于协调配合的NSCondition对象和用于存放产品的collector -(instancetype)initWithCondition:(NSCondition *) condition collector:(NSMutableArray *)collector{ if (self = [super init]){ self.condition = condition; self.collector = collector; self.shouldProduce = NO; self.item = nil; } return self; } -(void)produce{ self.shouldProduce = YES; while (self.shouldProduce) {//2.生产者会在shouldProduce为YES时进行生产。其余线程须要将设置为NO以中止生产者的生产 [self.condition lock]; if (self.collector.count > 0 ){ [self.condition wait];//4.若是collector中有未消费的产品,则等待,这会阻塞当前线程的执行直到condition被通知(signal)为止 } // [self.collector addObject:[self nextItem]];//5. [self.condition signal];//6.通知其余等待的线程。这里是产品完成生产的标志,并将产品将入到了collector中,可供消费 [self.condition unlock]; } } @implementation Consumer -(instancetype)initWithCondition:(NSCondition *) condition collector:(NSMutableArray *)collector{//8. if (self = [super init]){ self.condition = condition; self.collector = collector; self.shouldConsume = NO; self.item = nil; } return self; } -(void)consume{ self.shouldConsume = YES; while (self.shouldConsume) {//9.shouldConsume为YES,消费者进行消费。其余线程能够将其设置为NO来中止消费者的消费 [self.condition lock]; if (self.collector.count == 0){ [self.condition wait];//11若是collector没有产品则等待 } } id item = [self.collector objectAtIndex:0]; NSLog(@"%@",item); //处理产品 [self.collector removeObjectAtIndex:0];//12消费collector的中的下一个产品。确保已从collector中移除它。 [self.condition signal];//13通知其余等待的线程。这里标识一个产品被消费并从collector中移除了 [self.condition unlock]; } @implementation Coordinator -(void)start{ NSMutableArray *prprline = [NSMutableArray array]; NSCondition *condition = [NSCondition new];//Coordinator类为生产者和消费者准备好了输入数据 Producer *p = [Producer new]; p = [p initWithCondition:condition collector:prprline]; Consumer *c = [Consumer new]; c = [c initWithCondition:condition collector:prprline];//16 NSThread *t = [NSThread new]; //在不一样的线程中开启生产和消费任务 [[t initWithTarget:self selector:@selector(startProducer) object:p]start]; [[t initWithTarget:self selector:@selector(startCollector) object:c]start]; p.shouldProduce = NO; c.shouldConsume = NO;//18.一旦完成,分别设置生产者和消费者中止生产和消费 [condition broadcast];//19.由于生产者和消费者线程可能会等待,因此broadcast本质上会通知全部等待中的线程。不一样的是,signal方法只会影响一个等待的线程。 }
避免并发写入的最佳实践: 若是有多个线程视图读取一个属性,同步的代码在同一时刻只容许单个线程进行访问。所以,使用atomic属性会拖慢应用的性能。这多是个严重的瓶颈,尤为是当某个状态须要在多个线程间共享,且须要被多个线程访问时。cookie或登陆后的访问令牌就是这样的例子。它能够周期性的变化,但会被全部访问服务器的网络请求所调用。 GCD屏障容许在并行分发队列上建立一个同步的点。当遇到屏障时,GCD会延迟执行提交的代码块,直到队列中全部在屏障以前提交的代码块都执行完毕。随后,经过屏障提交的代码块会单独地执行。
代码实现步骤: (1)建立一个并行队列 (2)使用dispatch_sync执行全部的读操做 (3)在相同的队列上使用dispatch_barrier_sync执行全部的写操做
@interface HPCache : NSObject +(HPCache *)shareInstance; -(id)objectForKey:(id)key; -(void)setObject:(id)object forKey:(id)key; @end @interface HPCache() @property(nonatomic,readonly) NSMutableDictionary *cacheObjects; @property(nonatomic,readonly) dispatch_queue_t queue; @end @implementation HPCache -(instancetype) init{ if (self = [super init]){ _cacheObjects = [NSMutableDictionary dictionary]; _queue = dispatch_queue_create(kCacheQueueName, DISPATCH_QUEUE_CONCURRENT);//1.建立自定义的DISPATCH_QUEUE_CONCURRENT队列 } return self; } +(HPCache *)shareInstance{ static HPCache *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[HPCache alloc]init]; }); return instance; } -(id)objectForKey:(id)key{ __block id rv = nil; dispatch_sync(self.queue, ^{//2.将dispatch_sync用于不修改状态的操做 rv = [self.cacheObjects objectForKey:key]; }); return rv; } -(void)setObject:(id)object forKey:(id<NSCopying>)key{ dispatch_barrier_sync(self.queue, ^{//3.将dispatch_barrier_sync用于可能修改状态的操做 [self.cacheObjects setObject:object forKey:key]; }); } @end
遵循的最佳实践:
@interface HPUser : NSObject @property (nonatomic,copy) NSString *userId; @property (nonatomic,copy) NSString *firstName; @property (nonatomic,copy) NSString *lastName; @property (nonatomic,copy) NSString *gender; @property (nonatomic,copy) NSDate *dateOfBirth; @property (nonatomic,strong) NSArray *albums; //+(instancetype) userWithBlock:(void(^)(HPUserBuilder *)) block; @end @class HPPhoto; @interface HPAlbum @property (nonatomic , copy) NSString *albumId; @property (nonatomic , strong) HPUser *owner; @property (nonatomic , copy) NSString *name; @property (nonatomic , copy) NSString *descripiton; @property (nonatomic , copy) NSDate *creationTime; @property (nonatomic , copy) HPPhoto *coverPhoto; @end @interface HPPhoto @property (nonatomic , copy) NSString *photoId; @property (nonatomic , strong) HPAlbum *album; @property (nonatomic , strong) HPUser *user; @property (nonatomic , copy) NSString *caption; @property (nonatomic , strong) NSURL *url; @property (nonatomic , assign) CGSize size; @end @interface HPUserBuilder : NSObject //生成器 @property (nonatomic , copy) NSString *userId; @property (nonatomic , copy) NSString *firstName; @property (nonatomic , copy) NSString *lastName; @property (nonatomic , copy) NSString *gender; @property (nonatomic , copy) NSDate *dateOfBirth; @property (nonatomic , copy) NSArray *albums; -(HPUser *)build; @end @interface HPUser ()//模型的私有扩展--自定义的初始化设置 -(instancetype) initWithBuilder:(HPUserBuilder *)builder; @end @implementation HPUserBuilder -(HPUser *)build{ return [[HPUser alloc]initWithBuilder:self]; } @end @implementation HPUser -(instancetype) initWithBuilder:(HPUserBuilder *)builder{//模型自定义初始化器的实现 if (self = [super init]){ self.userId = builder.userId; self.firstName = builder.firstName; self.lastName = builder.lastName; self.gender = builder.gender; self.dateOfBirth = builder.dateOfBirth; self.albums = [NSArray arrayWithArray:_albums]; } return self; } +(instancetype) userWithBlock:(void(^)(HPUserBuilder *)) block{//6 HPUserBuilder *builder = [HPUserBuilder new]; block(builder); return [builder build]; } //生成器建立对象的使用示例 -(HPUser *)createUser{ HPUser *rv = [HPUser userWithBlock:^(HPUserBuilder *builder){ builder.userId = @"id001"; builder.firstName = @"Zou"; builder.lastName = @"Jie"; builder.gender = @"F"; NSCalendar *cal = [NSCalendar currentCalendar]; NSDateComponents *components = [[NSDateComponents alloc]init]; [components setYear:2017]; [components setMonth:10]; [components setDay:1]; builder.dateOfBirth = [cal dateFromComponents:components]; builder.albums = [NSArray array]; }]; return rv; } @end
响应式编程是基于异步数据流的编程方式。流是廉价且无处不在的,一切均可以是流:变量,用户输入,属性,缓存,数据结构,等等。 ReactiveCocoa库(https://github.com/ReactiveCocoa/ReactiveObjC)实现了Objective-c中进行响应式编程。它不只能够实现对任意状态的观察,还提供了高级的分类扩展,以便同步更新UI元素或响应视图的交互。 Cocoapods集成(http://www.jianshu.com/p/7c786eee1705)后,引入ReactiveCocoa头文件ReactiveObjC.h。
@interface HPUserService () @property (nonatomic , strong) NSMutableDictionary *userCache; @end @implementation HPUserService -(RACSignal *)signalForUserWithId:(NSString *)userid{//1 @weakify(self); return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {//2. @strongify(self); HPUser *userFromCache = [self.userCache objectForKey:userid]; if (userFromCache){ [subscriber sendNext:userFromCache]; [subscriber sendCompleted]; }else{ } return nil; }]; } -(RACSignal *)signalForUpateUser:(HPUser *)user{//更新HPUser对象 // @weakify(self); return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {//4. //更新服务器 // @strongify(self); [NSNotificationCenter.defaultCenter postNotificationName:@"userUpdated" object:nil]; return nil; }]; } -(RACSignal *)signalForUserUpdates:(id)object{ return [[NSNotificationCenter.defaultCenter rac_addObserverForName:@"userUpdateed" object:object] flattenMap:^__kindof RACSignal * _Nullable(NSNotification * _Nullable value) { return value.object; }];//rac_addObserverForName 订阅userUpdated通知 }
在实际场景中使用dispatch_sync
//场景A dispatch_sync(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"nested sync call"); }) }); //场景B -(void) methodA1{ dispatch_sync(queue1, ^{ // [objB methodB]; }); } -(void)methodA2{ dispatch_sync(queue1, ^{ NSLog(@"indirect nested dispatch_sync"); }); } -(void)methodB{ // [objA methodA2]; }
场景A演示了一个假设的场景,在这个场景中使用分发队列调用了一个嵌套的dispatch_sync。这会致使死锁。嵌套的dispatch_sync不能分发到队列中,由于当前线程已经在队列中且不会释放锁。 场景B演示了更类似的场景。类A有两个使用了相同队列的方法(methodA1和methodA2)。前一个方法对某个对象调用了methodB方法,后续会反调回来。最终结果仍是死锁。 要想实现线程安全,不死锁且易于维护的代码,使用异步风格。使用Promise是最好的方式。ReactiveCocoa为Objective-C引入了FRP风格。dispatch_async不受这一行为影响。
PromiseKit: githubhttps://github.com/mxcl/PromiseKit
处理地狱回调:http://www.jianshu.com/p/f060cfd52f17, http://www.cocoachina.com/swift/20160719/17085.html