Block 是將函數(shù)及其執(zhí)行上下文封裝起來的對象。 比如:
成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供武鳴網(wǎng)站建設(shè)、武鳴做網(wǎng)站、武鳴網(wǎng)站設(shè)計、武鳴網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、武鳴企業(yè)網(wǎng)站模板建站服務(wù),10多年武鳴做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
通過 clang -rewrite-objc WYTest.m 命令編譯該 .m 文件,發(fā)現(xiàn)該 block 被編譯成這個形式:
其中 WYTest 是文件名, blockTest 是方法名,這些可以忽略。其中 WYTest blockTest_block_impl_0 結(jié)構(gòu)體為
--block_impl 結(jié)構(gòu)體為
block 內(nèi)部有 isa 指針,所以說其本質(zhì)也是 OC 對象
block 內(nèi)部則為:
所以說 Block 是將函數(shù)及其執(zhí)行上下文封裝起來的對象
既然 block 內(nèi)部封裝了函數(shù),那么它同樣也有參數(shù)和返回值。
如果你正在跳槽或者正準(zhǔn)備跳槽不妨動動小手,添加一下咱們的交流群1012951431來獲取一份詳細的大廠面試資料為你的跳槽多添一份保障。
這里的輸出是 6 而不是 2,原因就是對局部變量 num 的截獲是值截獲。同樣,在 block 里如果修改變量 num ,也是無效的,甚至編譯器會報錯。
打印為 1,2,3
局部對象變量也是一樣,截獲的是值,而不是指針,在外部將其置為 nil ,對 block 沒有影響,而該對象調(diào)用方法會影響
輸出為 2,意味著 num = 1 這里的修改 num 值是有效的,即是指針截獲。同樣,在 block 里去修改變量 m ,也是有效的。
編譯后
( impl.isa = _NSConcreteStackBlock ;這里注意到這一句,即說明該 block 是棧 block )
可以看到局部變量被編譯成值形式,而靜態(tài)變量被編成指針形式,全局變量并未截獲。而 --block 修飾的變量也是以指針形式截獲的,并且生成了一個新的結(jié)構(gòu)體對象:
該對象有個屬性: num5 ,即我們用 --block 修飾的變量。這里 --forwarding 是指向自身的(棧 block )。
一般情況下,如果我們要對 block 截獲的局部變量進行賦值操作需添加 --block 修飾符,而對全局變量,靜態(tài)變量是不需要添加 --block 修飾符的。
另外, block 里訪問 self 或成員變量都會去截獲 self 。
分為全局 Block(_NSConcreteGlobalBlock) 、棧 Block(_NSConcreteStackBlock) 、堆
Block(_NSConcreteMallocBlock) 三種形式
其中棧 Block 存儲在棧 (stack) 區(qū),堆 Block 存儲在堆 (heap) 區(qū),全局 Block 存儲在已初始化數(shù)據(jù) (.data) 區(qū)
輸出:
比如:
輸出:
日常開發(fā)常用于這種情況:
比如堆 1中的全局進行 copy 操作,即賦值:
輸出:
仍是全局 block
而對 2中的棧 block 進行賦值操作:
輸出:
對棧 blockcopy 之后,并不代表著棧 block 就消失了,左邊的 mallock 是堆 block ,右邊被 copy 的仍是棧 block 比如:
輸出:
即如果對棧 Block 進行 cop ,將會 copy 到堆區(qū),對堆 Block 進行 copy ,將會增加引用計數(shù),對全局 Block 進行 copy ,因為是已經(jīng)初始化的,所以什么也不做。
另外, --block 變量在 copy 時,由于 --forwarding 的存在,棧上的 --forwarding 指針會指向堆上的-- forwarding 變量,而堆上的 --forwarding 指針指向其自身,所以,如果對 --block 的修改,實際上是在修改堆上的 --block 變量。
即 --forwarding 指針存在的意義就是,無論在任何內(nèi)存位置,都可以順利地訪問同一個 --block 變量 。
另外由于 block 捕獲的 --block 修飾的變量會去持有變量,那么如果用 --block 修飾 self ,且 self 持有
block ,并且 block 內(nèi)部使用到 --block 修飾的 self 時,就會造成多循環(huán)引用,即 self 持有 block , block 持有 --block 變量,而 --block 變量持有 self ,造成內(nèi)存泄漏。
比如:
如果要解決這種循環(huán)引用,可以主動斷開 --block 變量對 self 的持有,即在 block 內(nèi)部使用完 weakself 后, 將其置為 nil ,但這種方式有個問題,如果 block 一直不被調(diào)用,那么循環(huán)引用將一直存在。
所以,我們最好還是用 --weak 來修飾 self
以上就是 block 篇的面試題合集了,感謝觀看~!
? ?在平時的開發(fā)中,對于block,只是知道主要用于數(shù)據(jù)回調(diào)上,對于其內(nèi)部實現(xiàn)方式,以及一些使用時的注意事項,還不是很了解。在網(wǎng)上找個一些關(guān)于block的文章,做了一個簡單的整理。希望對于想學(xué)習(xí)研究block的同學(xué)有幫助。另外,非常感謝這些文章的作者,感謝你們把自己的知識經(jīng)驗分享出來。
首先是一篇唐巧的文章,對于唐巧,作為iOS開發(fā)者應(yīng)該不陌生。
談Objective-C block的實現(xiàn)
下邊這篇文章是block的學(xué)習(xí)使用
OS開發(fā)-由淺至深學(xué)習(xí)block
下邊這篇文章是在唐巧的文章中摘出來的,寫的也很詳盡。作者也引用了很多其他優(yōu)秀的文章,可以作為學(xué)習(xí)參考。
對Objective-C中Block的追探
下邊這篇文章也對block的原理做了很詳細的介紹,
Objective-C中的Block
再次感謝以上開發(fā)者的分享,如果大家有其他優(yōu)秀的文章,歡迎留言分享,一起進步學(xué)習(xí)。
然而若定義一個block屬性,并在block的實現(xiàn)中做如下操作:
此時當(dāng)調(diào)用ModalViewController的dismiss方法的時候不會調(diào)用dealloc方法中的打印語句,說明ModalViewController沒有被真正的銷毀。因為ModalViewController強引用一個block屬性,block會對內(nèi)部的強指針self進行一次強引用。所以造成循環(huán)引用
如果這樣呢?
這樣呢?
這樣呢?
如果想在block內(nèi)部修改從外部捕獲的auto變量的值,可以在該auto變量定義的時候,加上關(guān)鍵字__block
__block只可以用來作用于auto變量,它的目的就是為了能夠讓auto變量能夠在block內(nèi)部內(nèi)修改。而全局變量和static變量本來就可以從block內(nèi)部進行修改,因此__block對它們來說沒有意義,所以__block被規(guī)定只能用于修飾auto變量
我們把main.m 文件 改成main.cpp文件看下block到底是什么。
為了比較,我特意加了一個int b作為對比。
基本類型的auto變量被block捕獲的時候,就是通過值拷貝的形式把值賦給block內(nèi)部相對應(yīng)的基本類型變量。而例子里的__block int a = 10,我們可以看到在底層,系統(tǒng)是把int a包裝到了一個叫__Block_byref_a_0的對象里面。
看下__block_byref_a_0的對象里有啥:
這樣能看出來,外面使用__block 修飾的int a 在__block_byref_a_0對象里,就是最后的int a 這個結(jié)構(gòu)體里的int a 也就是我們在block里需要調(diào)用的a。
1、在block外部 使用__block修飾變量。
2、在block內(nèi)部使用被修飾的變量時,會創(chuàng)建一個__Block_byref_xx_0 *_xx對象。(此處的xx為變量名,下同)
3、被創(chuàng)建的對象,會生成__Block_byref_xx_0 *__forwarding;//指向自身類型對象的指針
4、最后__forwarding這個指針指向最新的值,然后再指回__Block_byref_xx_0 *_xx
5、最終當(dāng)我們打印的時候,如果變量被__block修飾了,會被內(nèi)部修改。
聲明一個無返回值的block
typedef?void(^onSearch)(NSString?*searchText);?////////onSearch?就是這個block的函數(shù)指針
2.在控制器中頭文件.h中定義block,分兩步:
1定義成屬性
@property?(nonatomic)??onSearch?searchBlock;
2定義一個block的setter方法
-(void)setSearchBlock:(void(^)(NSString?*searchText))?block;
3.在控制器的實現(xiàn)文件.m中來實現(xiàn)
-(void)setSearchBlock:(void(^)(NSString?*searchText))?block?{
if?(block)?{
self.searchBlock?=?block;
}
}
4.在控制器中使用block實現(xiàn)回調(diào). 當(dāng)點擊search按鈕時,將search的文本傳遞給訂閱者
-(void)searchHandler:(id)sender?{
if?(self.searchBlock)?{
self.searchBlock(@"your?search?keyword")
}
}
5.在訂閱者中,接收過來的參數(shù),并做其它操作
YourVC??*vc?=?[YourVC?new];
[vc?setSearchBlock:^(NSString?*searchText)?{
NSLog(@"get?search?keyword")???????
}]
聲明和使用Block:
Apple文檔中介紹了如何將一個Block聲明為變量,并將其作為一個函數(shù)使用:
int (^oneFrom)(int) = ^(int anInt) {
return anInt - 1;
};
// 創(chuàng)建了一個內(nèi)聯(lián)塊^(int anInt)... ,其函數(shù)體和結(jié)果被傳到了另外一個名為OneFrom的Block。
printf("1 from 10 is %d", oneFrom(10));
// 打印出: "1 from 10 is 9"
// 這個block函數(shù)(distanceTraveled)傳入3個float型參數(shù),返回float值。
float (^distanceTraveled) (float, float, float) =
^(float startingSpeed, float acceleration, float time) {
float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
return distance;
};
也可以傳入一個Block作為一個參數(shù),而不要以如上的方式聲明,這樣就可以在需要將block作為參數(shù)的時候以內(nèi)聯(lián)代碼的方式簡單地實現(xiàn)。
NSArray *anArray = [NSArray arrayWithObjects: @"cat", @"dog",nil];
sortFunction(anArray, ^(string *a string *b){
if ( a == @"cat") return TRUE; });