1 什麼是block
iOS SDK 4.0開始,Apple引入了block這一特性。字面上說,block就是一個代碼塊,但是它的神奇之處在於在內聯(inline)執行的時候(這和C++很像)還可以傳遞參數。同時block本身也可以被作為參數在方法和函數間傳遞,這就給予了block無限的可能。
對於閉包(block),有很多定義,其中閉包就是能夠讀取其它函數內部變量的函數,這個定義即接近本質又較好理解。對於剛接觸Block的同學,會覺得有些繞,因為我們習慣寫這樣的程序main(){ funA();} funA(){funB();} funB(){.....}; 就是函數main調用函數A,函數A調用函數B... 函數們依次順序執行,但現實中不全是這樣的,例如項目經理M,手下有3個程序員A、B、C,當他給程序員A安排實現功能F1時,他並不等著A完成之後,再去安排B去實現F2,而是安排給A功能F1,B功能F2,C功能F3,然後可能去寫技術文檔,而當A遇到問題時,他會來找項目經理M,當B做完時,會通知M,這就是一個異步執行的例子。在這種情形下,Block便可大顯身手,因為在項目經理M,給A安排工作時,同時會告訴A若果遇到困難,如何能找到他報告問題(例如打他手機號),這就是項目經理M給A的一個回調接口,要回掉的操作,比如接到電話,百度查詢後,返回網頁內容給A,這就是一個Block,在M交待工作時,已經定義好,並且取得了F1的任務號(局部變量),卻是在當A遇到問題時,才調用執行,跨函數在項目經理M查詢百度,獲得結果後回調該block。
block是一個特殊的OC對象, 它建立在棧上, 而不是堆上, 這麼做一個是為性能考慮,還有就是方便訪問局部變量.
默認情況下block使用到的局部變量都會被復制,而不是保留.
所以它無法改變局部變量的值.
如果在變量面前加上__block, 那麼編譯器回去不會復制變量, 而是去找變量的地址, 通過地址來訪問變量, 實際上就是直接操作變量.
另外塊是在棧上分配的, 所以一旦離開作用域, 就會釋放, 因此如果你要把快用在別的地方, 必須要復制一份.
所以在屬性定義一個快的時候需要使用copy: @property (nonatomic, copy) void (^onTextEntered)(NSString *enteredText);
塊是不能保留的, retain對塊沒有意義.
2 block 實現原理
Objective-C是對C語言的擴展,block的實現是基於指針和函數指針。
從計算語言的發展,最早的goto,高級語言的指針,到面向對象語言的block,從機器的思維,一步步接近人的思維,以方便開發人員更為高效、直接的描述出現實的邏輯(需求)。
iOS中block實現的探究
談Objective-C Block的實現
3 block的使用
使用實例
A:cocoaTouch框架下動畫效果的Block的調用
動畫效果是IOS界面重要的特色之一,其中CAAnimation是所有動畫對象的抽象父類,作為新人,使用較多的是UIView下的動畫方法(類方法)。使用UIView下的動畫,有下面幾個方法。
方法一:設置beginAnimations
其中memberView為需要添加的子視圖的視圖,mivc.view為子視圖,在使用的時候,需要將這兩個地方替換
需要注意的是,一定要使用[UIView commitAnimations];動畫才會生效
通過[UIView setAnimationDuration:1]; 設置持續時間。
方法二:
在IOS4.0後,我們有了新的方法,+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion,依然是UIView的類方法,但使用到了Block對象,Block對象是一組指令,可以傳遞(像變量一樣),可以把它想像成C語言的函數指針。
[UIView transitionWithView:self.view
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{ [self.view addSubview:yellowView.view]; }
completion:NULL];
B:
使用typed聲明block
typedef void(^didFinishBlock) (NSObject *ob);
這就聲明了一個didFinishBlock類型的block,
然後便可用
@property (nonatomic,copy) didFinishBlock finishBlock;
聲明一個block對象,注意對象屬性設置為copy,接到block 參數時,便會自動復制一份。
__block是一種特殊類型,
使用該關鍵字聲明的局部變量,可以被block所改變,並且其在原函數中的值會被改變。
C:1 使用block和使用delegate完成委托模式有什麼優點?
block不同其它變量的原因在於它不是一個單一變量, 而是一個方法,
我們要傳遞的是一個代碼塊,並且這個代碼塊可以存在參數,
這個參數並不是在定義block的時候就賦予值, 而是我們在實際運行block的時候才賦予值.
因此對於有參數的block,當我們傳遞過去的時候, 它的需要接收方提供相應的參數才能運行,
這麼做我們就可以在A類為B類將來會發生的事件提前做好處理的方法,即使我們還沒有這些事件的具體參數.
某種意義上將這樣就不需要兩者之間的委托關系.
委托關系就是B類發生一個事件後,通知A類,讓A類再針對這個事件進行一些處理
而使用block,則是A已經提前將這個事件的處理方法告訴了B類, 等時間發生的時候, B類無需通知A類, 直接運行實現設置好的處理方法(block)即可.
如果你在運行一個方法的時候又想告訴這個方法在某一特定情況你還要怎麼做的話, 就可以使用Block.
D:
GCD:
GCD主要使用block來代替委托模式,使程序變得簡潔,同時運行效率也得到提高.
復制代碼
static int clickNum = 0;
self.Mylabel = [[UILabel alloc]init];
while (clickNum <20) {
dispatch_async(dispatch_get_main_queue(), ^{
self.Mylabel.text = [NSString stringWithFormat:@"%d",clickNum++];//UI的繪制必須在主線程中
});
[NSThread sleepForTimeInterval:1];
}
復制代碼
關於block和GCD編程可以參考這篇文章:
還有這篇文章
ARC和非ARC中block的區別:
ARC下Block何時會從棧自動被復制到推, 以及__block和__weak的使用問題
由於Block是默認建立在棧上, 所以如果離開方法作用域, Block就會被丟棄, 在非ARC情況下, 我們要返回一個Block ,需要 [Block copy];
在ARC下, 以下幾種情況, Block會自動被從棧復制到堆:
1.被執行copy方法
2.作為方法返回值
3.將Block賦值給附有__strong修飾符的id類型的類或者Blcok類型成員變量時
4.在方法名中含有usingBlock的Cocoa框架方法或者GDC的API中傳遞的時候.
對於非ARC下, 為了防止循環引用, 我們使用__block來修飾在Block中實用的對象:
__block id blockSelf=self;
self.blk=^{
NSLog(@"%@",blockSelf); //在非ARC下對於棧上的_block對象, Block不會對其復制, 僅僅使用, 不會增加引用計數.
};
對於ARC下, 為了防止循環引用, 我們使用__weak來修飾在Block中實用的對象:
__weak id weakSelf=self;
self.blk=^{
NSLog(@"%@",weakSelf);
};
如果要在ARC下, 為了防止循環引用, 使用__block來修飾在Block中實用的對象,仍然會被retain, 所以需要多做一些設置
__block id blockSelf=self;
self.blk=^{
NSLog(@"%@",blockSelf);
self.blk=nil; //blk被釋放, blk只有的blockSelf也就被釋放了
};
blk(); //並且一定要運行一次, 否則不能被釋放
這樣就使blk斷開了與blockSelf的持有關系.
這麼多好處是可以自己控制對self的持有時間.
不過在最新的ios版本中, 這些會始終被已歎號形式提示存在循環引用問題.
這種書寫方式不被推薦. 除非你要在block中修改__block的指針指向.
其實我們用使用__weak修飾符, 只是不能修改對象本身, 但是可以修改對象的屬性.