Item 37: Understand Blocks
《Ry’s Objective-C Tutorial》# Blocksjavascript
Item 38: Create typedefs for Common Block Types
當咱們程序中要使用一些具備共性的Block時(返回值類型、參數個數和類型相同),咱們能夠給這種Block定義一個類型:html
typedef NSComparisonResult (^NSComparator)(id obj1, id obj2); //... - (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr; - (void)sortUsingComparator:(NSComparator)cmptr; //...
// Simplified with typedef typedef void(^EOCCompletionHandler)(NSData *data, NSError *error); - (void)startWithCompletionHandler:(EOCCompletionHandler)completion;
國內比較有名的開源框架BeeFramework中就大量應用到Block,並經過類型定義的Block做爲屬性,實現相似於不少腳本語言方法調用:self.HTTP_GET(URL).PARAM(postDict);
, 筆者以前在TouchXML基礎上封裝了一層W3C標準DOM API時也嘗試過這種實現,最後在Objective-C中能夠直接這樣調用:document.getElementById(@"xxx").setAttribute(@"class", @"xxx");
是否是有點寫JS的趕腳。java
Item 39: Use Handler Blocks to Reduce Code Separation
當咱們要執行一個異步操做,好比異步請求時,一般須要在操做(或請求)完成後將結果返回,在Objective-C中通常有兩種實現方式:代理和Block回調。ios
代理使用起來比較麻煩,有定義協議,申明代理方法,代理回調、設置代理、實現代理方法等一些列流程,而使用Block回調要簡潔得多,咱們一般能夠申明一個Block類型的屬性,在異步操做執行完後調用一下該Block。git
//CXMLHttpRequest.h typedef void (^CXMLHttpRequestCallbackBlock) (CXMLHttpRequest *request); @interface CXMLHttpRequest : NSObject //... @property (nonatomic, copy) CXMLHttpRequestCallbackBlock onreadystatechange; //... @end //CXMLHttpRequest.m //call when request state changed. _onreadystatechange(self); //User CXMLHttpRequest CXMLHttpRequest *request = [CXMLHttpRequest new]; request.onreadystatechange = ^(CXMLHttpRequest *req) { if (req.state == 4 && req.statusCode == 200) { //get req.responseText. } }; //...
推薦項目:BlocksKit。github
Item 40: Avoid Retain Cycles Introduced by Blocks Referencing the Object Owning Them
因爲Block會強引用裏面出現的對象,若是Block中使用成員變量,則self自己會被Block強引用,因此稍不注意就會出現Retain Cycle。因此一般避免的方法是在Block中引用對象的值而非對象自己,在非ARC下,可使用__block
關鍵字來申明須要在Block中引用的對象,這樣該對象就不會被Block retain,而後在Block結束時將引用對象設爲nil:objective-c
MyViewController * __block myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; myController = nil; };
在ARC模式下,則也能夠用__weak
(iOS5.0一下版本用__unsafe_unretained
)關鍵字申明一個弱引用對象:編程
MyViewController *__weak weakSelf = self; self.completionHandler = ^(NSData *data) { //... [weakSelf clearUp]; };
Item 41: Prefer Dispatch Queues to Locks for Synchronization
在多線程環境下,爲了保證某些資源操做的可控性,須要給一些方法加鎖,保證同時只響應一個對象的調用,一般能夠用@synchronized()
和NSLock
:多線程
// @synchronized block - (void)synchronisedMethod { @synchronized(self) { // Safe } }
// NSLock _lock = [[NSLock alloc] init]; - (void)synchronisedMethod { [_lock lock]; // Safe [_lock unlock]; }
咱們還可使用dispatch queue來保證同步操做,首先建立一個dispatch queue,而後將同步操做在該queue中執行:app
// Using GCD queue for synchronisation _syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL); // … - (NSString*)someString { __block NSString *localSomeString; dispatch_sync(_syncQueue, ^{ localSomeString = _someString; }); return localSomeString; } - (void)setSomeString:(NSString*)someString { dispatch_sync(_syncQueue, ^{ _someString = someString; }); }
Item 42: Prefer GCD to performSelector and Friends
不在使用GCD時,若是一項任務須要分別在主線程和非主線程中執行,咱們須要經過performSelector
方法來改變執行的線程,咱們還不得不把任務分解成不一樣的方法,某些方法內的代碼在主線程執行,某些在非主線執行:
- (void)pulldown { _indicator.hidden = NO; [_indicator startAnimating]; [self performSelectorInBackground:@selector(download) withObject:nil]; } - (void)download { NSURL *URL = [NSURL URLWithString:@"http://xxx."]; NSString *data = [NSString stringWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:nil]; if (data) { [self performSelectorOnMainThread:@selector(reloadData:) withObject:data waitUntilDone:NO]; } } - (void)reloadData { [_indicator stopAnimating]; _indicator.hidden = YES; //refresh view with data. }
而若是使用GCD,全部的操做就要簡潔不少:
- (void)pulldown { _indicator.hidden = NO; [_indicator startAnimating]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSURL *URL = [NSURL URLWithString:@"http://xxx"]; NSString *data = [NSString stringWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:nil]; if (data) { dispatch_async(dispatch_get_main_queue(), ^{ [_indicator stopAnimating]; _indicator.hidden = YES; //refresh view with data. }); } }; }
Item 43: Know When to Use GCD and When to Use Operation Queues
Item 44: Use Dispatch Groups to Take Advantage of Platform Scaling
不少狀況下咱們使用GCD來執行一些異步操做,可是異步操做就存在一個返回順序問題,如咱們須要異步下載3個數據,只有當3個數據都下載完成後才刷新視圖,而3個異步下載返回順序是未知的,這是咱們可使用dispatch group來管理這三個任務:
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ //下載數據1 }); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ //下載數據2 }); dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ //下載數據3 }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ //刷新視圖 });
其實熟悉JS或者說熟悉Node.js的人都瞭解,異步編程下的協同問題一直是比較受關注的話題,其中 Node大牛 @樸靈的EventProxy,我的感受和dispatch group有殊途同歸之妙:
var ep = EventProxy.create("template", "data", "l10n", function (template, data, l10n) { _.template(template, data, l10n); }); $.get("template", function (template) { // something ep.emit("template", template); }); $.get("data", function (data) { // something ep.emit("data", data); }); $.get("l10n", function (l10n) { // something ep.emit("l10n", l10n); });
Item 45: Use dispatch_once for Thread-Safe Single-Time Code Execution
// `dispatch_once' singleton initialisation + (id)sharedInstance { static EOCClass *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; }
Item 46: Avoid dispatch_get_current_queue
Chapter 7: The System Frameworks
Item 47: Familiarize Yourself with the System Frameworks
《iOS Technology Overview》# Cocoa Touch Frameworks
Item 48: Prefer Block Enumeration to for Loops
// Block enumeration NSArray *anArray = /* … */; [anArray enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop){ // Do something with `object’ if (shouldStop) { *stop = YES; } }]; NSDictionary *aDictionary = /* … */; [aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, NSUInteger idx, BOOL *stop){ // Do something with `key’ and `object’ if (shouldStop) { *stop = YES; } }]; NSSet *aSet = /* … */; [aSet enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop){ // Do something with `object’ if (shouldStop) { *stop = YES; } }];
Item 49: Use Toll-Free Bridging for Collections with Custom Memory-Management Semantics
// No-ops for non-retaining objects. static const void* EOCRetainNoOp(CFAllocatorRef allocator, const void *value) { return value; } static void EOCReleaseNoOp(CFAllocatorRef allocator, const void *value) { } NSMutableArray* EOCNonRetainArray(){ CFArrayCallBacks callbacks = kCFTypeArrayCallBacks; callbacks.retain = EOCRetainNoOp; callbacks.release = EOCReleaseNoOp; return (NSMutableArray *)CFArrayCreateMutable(nil, 0, &callbacks); } NSMutableDictionary* EOCNonRetainDictionary(){ CFDictionaryKeyCallBacks keyCallbacks = kCFTypeDictionaryKeyCallBacks; CFDictionaryValueCallBacks callbacks = kCFTypeDictionaryValueCallBacks; callbacks.retain = EOCRetainNoOp; callbacks.release = EOCReleaseNoOp; return (NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, &keyCallbacks, &callbacks); }
Item 50: Use NSCache Instead of NSDictionary for Caches
Item 51: Keep initialize and load Implementations Lean
+ (void)load;
Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.
+ (void)initialize;
Initializes the receiver before it’s used (before it receives its first message).
Item 52: Remember that NSTimer Retains Its Target
NSTimer會對retain它的Target,因此不要在Target的dealloc中銷燬(invalidate)NSTimer對象,由於Timer和Target之間已經造成了Retain cycle,須要在dealloc前就破壞這個Retain cycle。
咱們能夠對NSTimer拓展,讓它支持調用Block方法:
// Block support for NSTimer #import <Foundation/Foundation.h> @interface NSTimer (EOCBlocksSupport) + (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats; @end @implementation NSTimer (EOCBlocksSupport) + (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats { return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(eoc_blockInvoke:) userInfo:[block copy] repeats:repeats]; } + (void)eoc_blockInvoke:(NSTimer*)timer { void (^block)() = timer.userInfo; if (block) { block(); } } @end
總結
到這裏,所有的代碼都過了一遍了,網友@Alfred_Kwong說原書不少內容沒有在代碼中體現,建議仍是讀一讀原書。其實也是,即便原書全部的內容在代碼中都有體現,我也不可能兩篇博文就把全部東西總結出來。我更多的是經過該書的52個主題,結合代碼,本身對Objective-C內容進行一遍梳理,因此不要由於我這兩篇文章來決定你該不應買本書看看,我不想作推銷,更不想黑。