塊和大中樞派發(fā)
第37條:理解”塊“這一概念
Block的了解還是很有必要的,它和函數(shù)類似,只不過是直接定義在另一個函數(shù)里的,和定義它的那個函數(shù)共享同一個范圍的東西。用
^來表示,后面接一對花括號,括號里是blcok的實(shí)現(xiàn)代碼。
void(^someBlcok)() = ^{
NSLog(@"there has someBlock");
};
someBlcok(); // print:“there has someBlock”
格式: 返回類型 (^Block Name)(參數(shù)){};
int (^addBlock)(int a, int b) = ^(int a ,int b){
return a + b;
};
int testAdd = addBlock(7,5);
NSLog(@"testAdd === %d",testAdd); // print: “testAdd === 12”
int additonal = 5;
int (^minusBlock)(int a, int b) = ^(int a,int b){
return additonal - a - b;
};
int testMinus = minusBlock(2,6);
NSLog(@"minus === %d",testMinus); //print :"minus === -3"
注意當(dāng)要改變塊里面的變量的時候,需要加上
__block修飾符
NSArray * array = @[@0,@1,@2,@3,@4,@5];
__block NSInteger count = 0;
[array enumerateObjectsUsingBlock:^(NSNumber * number,NSUInteger idx,BOOL *stop){
if([number compare:@4] == NSOrderedAscending)
{
count++;
}
}];
NSLog(@"count ==== %lu",count); // print : "count ==== 4"
注意還可以使用反序和順序
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts
usingBlock:(void (^)(ObjectType obj, NSUInteger idx, BOOL *stop))block
typedef NS_OPTIONS(NSUInteger, NSEnumerationOptions) {
NSEnumerationConcurrent = (1UL << 0), // 順序
NSEnumerationReverse = (1UL << 1), // 倒序
};
定義
block的時候,所占的內(nèi)存區(qū)域是分配在棧中的,也就是block只在定它的那個范圍內(nèi)有效。所以我們我們需要對其對象發(fā)送copy消息以拷貝值,這樣的話,就可以把塊從棧復(fù)制到堆了,塊這樣就成了帶引用計數(shù)的對象啦。
第38條:為常用的塊類型創(chuàng)建typedef
由于在定義塊變量時,需要把變量名放在類型之中,而不要放在右側(cè),這樣非常難記,也非常難懂,鑒于此,我們應(yīng)該為常用的塊類型起個別名,此時
typedef關(guān)鍵字就用到了。
typedef int (^YPQBlcok)(BOOL flag,int value);
第39條:用handler塊降低代碼分散程度
當(dāng)用Handler塊的時候,可以直接將塊和相關(guān)對象放在一起。這樣代碼更清晰而緊湊。
#import <Foundation/Foundation.h>
typedef void(^YPQNetworkCompletionHandler)(NSData * data);
@interface YPQNetworkFetcher: NSObject
@property (nonatomic, readonly, strong) NSURL * url;
- (id)initWithURL:(NSURL *)url;
- (void)startWithCompletionHandler:(YPQNetworkCompletionHandler)comletion;
@end
注意實(shí)現(xiàn)文件中 的 copy
#import "YPQNetworkFetcher.h"
@interface YPQNetworkFetcher()
@property (nonatomic, readwrite, strong)NSURL * url;
@property (nonatomic, copy)YPQNetworkCompletionHandler completionHandle;
@property (nonatomic, strong) NSData * downloadedData;
@end
@implementation YPQNetworkFetcher
- (id)initWithURL:(NSURL *)url
{
if(self = [super init])
{
_url = url;
}
return self;
}
- (void)startWithCompletionHandler:(YPQNetworkCompletionHandler)comletion
{
self.completionHandle = comletion;
// 開始 request
// downloadedData 獲值
// 當(dāng)申請完成,調(diào)用 [self p_requestCompleted];
}
- (void)p_requestCompleted
{
if(_completionHandle)
{
_completionHandle(_downloadedData);
}
}
@end
第40條:用塊引用其所屬對象時不要出現(xiàn)保留環(huán)
就是我們使用Block的時候很容易出現(xiàn)循環(huán)引用。
#import "ViewController.h"
#import "YPQNetworkFetcher.h"
@interface ViewController ()
{
YPQNetworkFetcher * _networkFetcher;
}
@property (nonatomic ,strong) NSData * downloadData;
@end
- (void)downloadTheData
{
NSURL * url = [NSURL URLWithString:@"https://www.example.com/...."];
_networkFetcher = [[YPQNetworkFetcher alloc] initWithURL:url];
[_networkFetcher startWithCompletionHandler:^(NSData * data){
self.downloadData = data;
}];
}
這樣看起來沒問題,但是已經(jīng)形成循環(huán)引用啦

-
block保留了viewController實(shí)例; -
viewController實(shí)例則通過_networkFetcher保留YPQNetworkFetcher; -
_completionHandle又保留了block。
這樣環(huán)就形成了,打破環(huán)就得打破其中某一個環(huán)節(jié),比較好用的方法可以通過;
// 打破上述的第一環(huán)節(jié)
__weak __typeof(self) weakSelf = self;
[_networkFetcher startWithCompletionHandler:^(NSData * data){
weakSelf.downloadData = data;
}];
或者
// 打破上述的第二環(huán)節(jié)
[_networkFetcher startWithCompletionHandler:^(NSData * data){
self.downloadData = data;
_networkFetcher = nil;
}];
第41條:多用派發(fā)隊列,少用同步鎖
如果多個線程要執(zhí)行同一份代碼的時候,通常都要使用鎖實(shí)現(xiàn)某種同步機(jī)制。
@synchronized(self) {
//safe
}
_lock = [[NSLock alloc] init];
[_lock lock];
//safe
[_lock unlock];
但是這兩種方式效率不高,而且也無法提供絕對的線程安全。有種簡單而高效的辦法可以代替他們,那即是使用”串行同步隊列“,將讀取操作和寫入操作都安排在同一個隊列中。
_syncQuene = dispatch_queue_create("com.yang.test", NULL); - (NSString *)testGCDString
{
__block NSString * localString;
dispatch_sync(_syncQuene, ^{
localString = _testGCDString;
});
return localString;
}
- (void)setTestGCDString:(NSString *)testGCDString
{
dispatch_sync(_syncQuene, ^{
_testGCDString = testGCDString;
});
}
當(dāng)然還可以繼續(xù)優(yōu)化, 將同步派發(fā)改為異步派發(fā),并且由于多個獲取方法可以并發(fā)執(zhí)行,而獲取方法和設(shè)置方法之間不能并發(fā)執(zhí)行,此時改用并發(fā)隊列。
_syncQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- (NSString *)testGCDString
{
__block NSString * localString;
dispatch_sync(_syncQuene, ^{
localString = _testGCDString;
});
return localString;
}
- (void)setTestGCDString:(NSString *)testGCDString
{
dispatch_async(_syncQuene, ^{
_testGCDString = testGCDString;
});
}
但是像上面這樣,還是無法正確實(shí)現(xiàn)同步。讀取和寫入可以隨時執(zhí)行,為了不讓其任意執(zhí)行,此時dispatch_barrier_async就出現(xiàn)啦
_syncQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- (NSString *)testGCDString
{
__block NSString * localString;
dispatch_sync(_syncQuene, ^{
localString = _testGCDString;
});
return localString;
}
- (void)setTestGCDString:(NSString *)testGCDString
{
dispatch_barrier_async(_syncQuene, ^{
_testGCDString = testGCDString;
});
}
dispatch_barrier_async必須單獨(dú)執(zhí)行,不能與其他塊并行。這只對并發(fā)隊列有意義,因為串行隊里中塊總是按順序逐個來執(zhí)行的。并發(fā)隊列如果發(fā)現(xiàn)接下來要處理的塊是個barrier block,那么就一直要等到當(dāng)前所有并發(fā)塊都執(zhí)行完畢,才會單獨(dú)執(zhí)行這個barrier block。待barrier block執(zhí)行過后,再按正常方式繼續(xù)向下處理。
在上面這個隊列中,在寫入操作用了
dispatch_barrier_async來實(shí)現(xiàn)后,對屬性的讀取操作依然可以并行,但寫入操作必須單獨(dú)執(zhí)行。當(dāng)然設(shè)置函數(shù)中,我們也可以用dispatch_barrier_sync同步來實(shí)現(xiàn),有時可能更高效,看具體場景吧。
將同步與異步派發(fā)結(jié)合起來,實(shí)現(xiàn)與普通加鎖機(jī)制一樣的同步行為,也不會阻塞執(zhí)行異步派發(fā)的線程,所以是 OK 的。
第42條:多用GCD,少用performSelector
在我們用到推遲執(zhí)行方法的時候,我們可以有種選擇。
// performSelector
[self performSelector:@selector(afterThreeSecondBeginAction)
withObject:nil
afterDelay:3.0f];
// dispatch after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^(void){
[self afterThreeSecondBeginAction];
});
performSelector系列方法在內(nèi)存管理方面容易疏失,并且所處理的選擇子太過局限了,GCD 作為純C的API,一般擔(dān)憂還是沒必要的,所以說少用performSelector還是有理由的。
第43條:掌握GCD及操作隊列的使用時機(jī)
簡單的說,不要過度使用GCD,要合理使用它,這個可能需要項目經(jīng)驗的積累的。像
NSOperationQueue類也可以多了解下。
第44條:通過Dispatch Group 機(jī)制,根據(jù)系統(tǒng)資源狀況執(zhí)行任務(wù)
dispatch group是GCD的一項特性,能夠把任務(wù)分組。
這里是GCD的詳細(xì)介紹,推薦一篇文章@ 少君的GCD
第45條:使用dispatch_once 來執(zhí)行只需要運(yùn)行一次的線程安全代碼
static id _instace;
+ (instancetype)sharedInstanceWithSome
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [[self alloc] init];
});
return _instace;
}
使用
dispatch_once可以簡化代碼并且徹底保證線程安全,我們根本無須擔(dān)心加鎖或同步,另外它沒有使用重量級的同步機(jī)制,所以也更高效。
第46條:不要使用dispatch_get_current_queue
其實(shí)這個已經(jīng)在iOS6.0之后被棄用了,它可以做調(diào)試,但實(shí)際也用的不多,所以一般我想應(yīng)該是不會用到吧。
持續(xù)筆記中····