線程時運行時執行的一組指令序列,每一個進程至少包含一個線程。在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