1. block的最大用處:回調(callback)
雖然接觸iOS已經8個月了,block 作為Objective C中對于回調(callback)的實現,理解起來還是有點模棱兩可。在《Pro Multithreading and Memory Management for iOS and OS X》書中,Kazuki Sakamoto 對block的定義是:
擁有自動變量(可以在block聲明的語義環境里捕捉變量的狀態)的匿名(使函數體(code)成為和數據(data)一樣的一等公民,作為函數調用時輸入的實參(argument))函數。
我發現,在大部分應用block的場景,這樣的定義不夠sharp,使人模棱兩可。我用我自己的理解重定義block:
擁有自動變量(可以在block聲明的語義環境里捕捉變量的狀態)的匿名(使函數體(code)成為和數據(data)一樣的一等公民,作為函數調用時輸入的實參(argument))回調(callback,等待被調用)函數。
回調(callback)兩個字把block在大部分場景應用時的扮演的角色給準確描述。下面我舉一個簡單的例子來解釋回調。NSArray的實例方法 *- enumerateObjectsUsingBlock:(void (^ )(id obj, NSInteger idx, BOOL *stop))block 是一個用block實現數組枚舉的方法。下面是該方法的簡單應用:
NSArray *cities = @[@"Beijing", @"Shanghai", @"Guangzhou", @"Shenzhen",@"Hong Kong"];
[cities enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
NSLog(@"index: %lu, obj: %@, stop: %d",(unsigned long)idx,obj,*stop);
if(idx == 3){
*stop = YES;
}
}];
//這是一個再簡單不過的數組枚舉。輸出為:
2016-01-06 22:52:18.143 Chapter00_Block implmenetaion[18596:780185] index: 0, obj: Beijing, stop: 0
2016-01-06 22:52:18.144 Chapter00_Block implmenetaion[18596:780185] index: 1, obj: Shanghai, stop: 0
2016-01-06 22:52:18.144 Chapter00_Block implmenetaion[18596:780185] index: 2, obj: Guangzhou, stop: 0
2016-01-06 22:52:18.144 Chapter00_Block implmenetaion[18596:780185] index: 3, obj: Shenzhen, stop: 0
那么,如何理解上面block的新定義里的回調呢。由于apple 自己里大部分類的實現是保密的,我們來自己定義一個方法來簡單實現下這個- enumerateObjectsUsingBlock:方法的功能。首先來給NSArray添加一個名為Enumeration的category,加入以下代碼。
NSArray+Enumeration.h
#import <Foundation/Foundation.h>
@interface NSArray (Enumeration)
- (void)customizedEnumerateWithBlock:(void(^)(id obj, NSInteger index, BOOL *stop))block;
@end
NSArray+Enumeration.m
#import "NSArray+Enumeration.h"
@implementation NSArray (Enumeration)
-(void)customizedEnumerateWithBlock:(void (^)(id, NSInteger, BOOL *))block{
BOOL stop = false; for(int i = 0; i < self.count; ++i){
block(i,[self objectAtIndex:i],&stop);
if(stop) break;
}
}
@end
如果你把cities的數組枚舉方法換成這個,那么輸出是一樣的。
NSArray *cities = @[@"Beijing", @"Shanghai", @"Guangzhou", @"Shenzhen", @"Hong Kong"];
[cities customizedEnumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
NSLog(@"index: %lu, obj: %@, stop: %d",(unsigned long)idx,obj,*stop);
if(idx == 3){
*stop = YES;
}
}];
//輸出為:
2016-01-06 22:52:18.143 Chapter00_Block implmenetaion[18596:780185] index: 0, obj: Beijing, stop: 0
2016-01-06 22:52:18.144 Chapter00_Block implmenetaion[18596:780185] index: 1, obj: Shanghai, stop: 0
2016-01-06 22:52:18.144 Chapter00_Block implmenetaion[18596:780185] index: 2, obj: Guangzhou, stop: 0
2016-01-06 22:52:18.144 Chapter00_Block implmenetaion[18596:780185] index: 3, obj: Shenzhen, stop: 0
由此可見,我們自己實現的數組枚舉能實現原本的數組枚舉方法。在- customizedEnumerateWithBlock: 的實現里,回調了block這個函數。因此若有方法里有block體作為參數,則意味著這個方法在調用其他函數的同時,這個block在等待將來被調用。那么該block什么時候被調用呢(這里是for 循環語句里),在實際應用block場景里,block的調用更多地是簡化網絡請求,delegate等。
因此“回調”是block我個人認為最大的用處。
2. ReactiveCocoa的subscription原理
作為響應式函數編程的iOS實現,ReactiveCocoa將iOS編程帶入了一個新紀元,其最大的貢獻在于:
統一消息傳遞機制:iOS 開發中有著各種消息傳遞機制,包括 KVO、Notification、delegation、block 以及 target-action 方式。
ReactiveCocoa的入門可以參考
http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1
http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2
這里我著重要說的是ReactiveCocoa (訂閱)subscription的實現原理--block回調的應用。在ReactiveCocoa里,RACSignal, RACSubscriber, RACDisposal 是最核心的三個類。下面我們就一個簡單的例子深入理解subscription的實現原理,見下面代碼:
-(RACSignal *)signInSignal {
// part 1:[RACSignal createSignal]來獲得signal
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[self.signInService signInWithUsername:self.usernameTextField.text
password:self.passwordTextField.text
complete:^(BOOL success) {
// part 3: 進入didSubscribe,通過[subscriber sendNext:]來執行next block
[subscriber sendNext:@(success)];
[subscriber sendCompleted];
}];
return nil;
}];
}
// part 2 : [signal subscribeNext:]來獲得subscriber,然后進行subscription
[[self signInSignal] subscribeNext:^(id x) {
NSLog(@"Sign in result: %@", x);
}];