Block基本用法
1.作為方法時
- (void)testGlobalBlock:(NSString*) url
? ? ? ? ? ? ? ? ?success:(void(^)(BOOLisSuccess))success
? ? ? ? ? ? ? ? ? failure:(void(^)(BOOLisSuccess))failure;
2.作為成員變量進行聲明時
typedefvoid(^SUCCESSBLOCK)(BOOLisSuccess);
typedefvoid(^FAILEDBLOCK)(BOOLisSuccess);
@interfaceXXEngine()
@property(nonatomic,copy) SUCCESSBLOCK successBlock;
@property(nonatomic,copy) FAILEDBLOCK failedBlock;
@end
3.寫在函數(shù)內(nèi)部
void(^_block)() = ^{
_a =10;
};
內(nèi)存五個區(qū)認識
這里先普及一個基本的5個概念扶镀,內(nèi)存的5個區(qū)。否則在具體講堆棧的時候怕大家會不理解。
作用名稱
棧區(qū)(stack)由編譯器自動分配釋放运挫,存放函數(shù)的參數(shù)值烛恤,局部變量的值等
堆區(qū)(heap)一般由程序員分配釋放崎逃, 若程序員不釋放失尖,程序結(jié)束時可能由OS回收
全局區(qū)(靜態(tài)區(qū))(static)全局變量和靜態(tài)變量的存儲是放在一塊的妹窖,初始化的全局變量和靜態(tài)變量在一塊區(qū)域纬朝,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。程序結(jié)束后由系統(tǒng)釋放
文字常量區(qū)常量字符串就是放在這里的骄呼。程序結(jié)束后由系統(tǒng)釋放
程序代碼區(qū)存放函數(shù)體的二進制代碼
使用場景
任務完成時回調(diào)處理
消息監(jiān)聽回調(diào)處理
錯誤回調(diào)處理
枚舉回調(diào)
視圖動畫共苛、變換
排序
Block原理
Block三種類型
作用名稱
NSConcreteGlobalBlock全局靜態(tài)判没,不訪問外部變量的時候就是NSConcreteGlobalBlock
NSConcreteStackBlock保存在棧中,出花括號會被銷毀
NSConcreteMallocBlock保存在堆中的block,當引用計數(shù)為0的時候會被銷毀
ARC下判斷原則
造成循環(huán)引用的實例
@implementationPerson
{
int_a;
void(^_block)();
}
- (void)test
{
void(^_block)() = ^{
_a =10;
};}
@end
此時_a在用clang編譯后可以很明顯看到有會self的身影俄讹,所以此時我們可以將其理解成self.a(便于記憶),此時Person持有block,block持有self哆致,造成了循環(huán)引用
解決方式
- (void)test
{
__weaktypeof(self) weakSelf =self;
void(^_block)() = ^{
weakSelf->a =10;
};}
經(jīng)驗談之GCD/UIView的系統(tǒng)動畫的Block中绕德,為何可以寫self患膛?
因為self并沒有對其進行持有,循環(huán)引用的原理是兩個對象之間相互有持有關(guān)系耻蛇,現(xiàn)在僅僅是GCD持有self踪蹬,但是self并沒有持有GCD,所以是沒有問題的臣咖。(當GCD對象為其成員變量時才具有持有的關(guān)系)
結(jié)論
我們觀察Block_byref_a_0結(jié)構(gòu)體跃捣,這個結(jié)構(gòu)體中含有isa指針,所以也是一個對象夺蛇,它是用來包裝局部變量a的疚漆。當block被copy到堆中時,Persontest_block_impl_0的拷貝輔助函數(shù)Persontest_block_copy_0會將Block_byref_a_0拷貝至堆中刁赦,所以即使局部變量所在堆被銷毀娶聘,block依然能對堆中的局部變量進行操作。其中Block_byref_a_0成員指針forwarding用來指向它在堆中的拷貝
利用對象做Block
![](http://www.iosxxx.com/images/block/4.png)
現(xiàn)在有RequestObject和XXNetEngine兩個文件.
1.RequestObject
#import
typedefvoid(^ReqSuccessBlock)(BOOLisSuccess);
typedefvoid(^FailedBlock)(BOOLisSuccess);
@interfaceRequestObject:NSObject
@property(nonatomic,copy) ReqSuccessBlock successBlock;
@property(nonatomic,copy) FailedBlock failedBlock;
@property(nonatomic,assign)NSIntegerCMD;
@property(nonatomic,assign)NSIntegerseq;
@end
2.XXNetEngine
#import"XXNetEngine.h"
#import"RequestObject.h"
@interfaceXXNetEngine()
@property(nonatomic,strong)NSMutableDictionary*requestDict;
@end
@implementationXXNetEngine
-(instancetype)init
{
if(self= [superinit]){
self.requestDict = [NSMutableDictionarydictionary];
}
returnself;
}
+(instancetype) sharedInstance
{
staticXXNetEngine* instance =nil;
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
instance = [[XXNetEngine alloc] init];
});
returninstance;
}
-(void)requestData:(RequestObject*) reqObject
successBlock:(void(^)(BOOLisSuccess))successBlock
failureBlock:(void(^)(BOOLisSuccess))failureBlock
{
if( reqObject && successBlock && failureBlock ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
//requestSeq是一個隨機數(shù)
intrequestSeq = arc4random() %100;;
//網(wǎng)絡操作
reqObject.successBlock = successBlock;
reqObject.failedBlock = failureBlock;
reqObject.seq = requestSeq;
[selfaddRequestItem:reqObject seq:requestSeq];
//模擬一個2秒的網(wǎng)絡操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[selfdidReceiveData:reqObject.seq];
});});}}
- (void) didReceiveData:(NSInteger) seq
{
RequestObject *request = [selfgetRequestItem:seq];
if( request ) {
[selfremoveRequestItem:seq];
dispatch_async(dispatch_get_main_queue(), ^{
if(NULL!= request.successBlock ) {
request.successBlock(YES);
}});}}
//用命令字做參數(shù)傳遞
- (RequestObject *) getRequestItem:(NSInteger)seq
{
RequestObject *request =nil;
@synchronized(self) {
NSNumber*numSeq = [NSNumbernumberWithInteger:seq];
request = [self.requestDict objectForKey:numSeq];
}
returnrequest;
}
- (void) addRequestItem:(RequestObject *)request seq:(NSInteger)seq
{
@synchronized(self) {
if( request && (0!=seq) ) {
NSNumber*numSeq = [NSNumbernumberWithInteger:seq];
[self.requestDict setObject:request forKey:numSeq];
}}}
- (void) removeRequestItem:(NSInteger)seq
{
@synchronized(self) {
NSNumber*numSeq = [NSNumbernumberWithInteger:seq];
[self.requestDict removeObjectForKey:numSeq];
}}
@end
Delegate甚脉、Notification丸升、Block的比較
名稱優(yōu)點缺點
Notification1.使用簡單,代碼精簡牺氨。2.解決了同時向多個對象監(jiān)聽相應的問題狡耻。3.傳值方便快捷,Context自身攜帶相應的內(nèi)容猴凹。1.使用完畢后夷狰,要時刻記得注銷通知,否則將出現(xiàn)不可預見的crash郊霎。2.key不夠安全沼头,編譯器不會監(jiān)測是否被通知中心正確處理。3.調(diào)試的時候動作的跟蹤將很難進行歹篓。4.當使用者向通知中心發(fā)送通知的時候瘫证,并不能獲得任何反饋信息。5.需要一個第三方的對象來做監(jiān)聽者與被監(jiān)聽者的中介庄撮。
Delegate1.減少代碼的耦合性背捌,使事件監(jiān)聽和事件處理相分離。2.清晰的語法定義洞斯,減少維護成本毡庆,較強的代碼可讀性坑赡。3.不需要創(chuàng)建第三方來監(jiān)聽事件和傳輸數(shù)據(jù)。1.實現(xiàn)委托的代碼過程比較繁瑣么抗。2.當實現(xiàn)跨層傳值監(jiān)聽的時候?qū)⒓哟蟠a的耦合性毅否,并且程序的層次結(jié)構(gòu)將變的混亂。3.當對多個對象同時傳值響應的時候蝇刀,委托的易用性將大大降低螟加。
Block1.語法簡潔,實現(xiàn)回調(diào)不需要顯示的調(diào)用方法吞琐,代碼更為緊湊捆探。2.增強代碼的可讀性和可維護性。3.配合GCD優(yōu)秀的解決多線程問題站粟。1.Block中得代碼將自動進行一次retain操作黍图,容易造成內(nèi)存泄露2.Block內(nèi)默認引用為強引用,容易造成循環(huán)引用奴烙。
使用原則
如果方法過多助被,一般大于3個的時候,用delegate切诀,因為此時用block揩环,代碼將很難維護。比如UITableViewDelegate等等UI組建趾牧,都是用的delegate检盼。其他方式用block。
Notification能不用的時候盡量不用翘单,缺點太多明顯吨枉,增加調(diào)試難度。在工程大的情況下哄芜,極其難以維護貌亭。當然有些情況下也是必不可少的,比如觀察者模式.
感謝:向晨宇Block入門
Block測試題:http://blog.parse.com/learn/engineering/objective-c-blocks-quiz/