成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

iOS進(jìn)階—— Block

移動(dòng)開(kāi)發(fā) iOS
說(shuō)明還是有很多 iOS 的朋友對(duì)于 Block 并沒(méi)有透徹理解。本篇博文會(huì)對(duì) Block 進(jìn)行詳細(xì)的解說(shuō)。

花幾分鐘時(shí)間看下面三個(gè)小題目,寫(xiě)下你的答案。 

 

 

 

這個(gè)三個(gè)小題目,我在整理此片博文之前給了三位朋友去解答,***的結(jié)果,除了一位朋友 3 題全部正確,其他兩個(gè)朋友均只答中 1 題。

說(shuō)明還是有很多 iOS 的朋友對(duì)于 Block 并沒(méi)有透徹理解。本篇博文會(huì)對(duì) Block 進(jìn)行詳細(xì)的解說(shuō)。

1 Block 使用的簡(jiǎn)單規(guī)則

先了解簡(jiǎn)單規(guī)則,再去分析原理和實(shí)現(xiàn):

Block 中,Block 表達(dá)式截獲所使用的自動(dòng)變量的值,即保存該自動(dòng)變量的瞬間值。

修飾為 __block 的變量,在捕獲時(shí),獲取的不再是瞬間值。

至于 Why,后面將會(huì)繼續(xù)說(shuō)。

2 Block 的實(shí)現(xiàn)

Block 是帶有自動(dòng)變量(局部變量)的匿名函數(shù)。

Block 表達(dá)式很簡(jiǎn)單,總體可以描述為:『^ 返回值類(lèi)型 參數(shù)列表 表達(dá)式』。

但是 Block 并不是 Objective-C 中才有的語(yǔ)法,這是怎么一回事?

clang 編譯器提供給程序員了解 Objective-C 背后機(jī)制的方法,通過(guò) clang 的轉(zhuǎn)換可以看到 Block 的實(shí)現(xiàn)原理。

通過(guò) clang -rewrite-objc yourfile.m clang 將會(huì)把 Objective-C 的代碼轉(zhuǎn)換成 C 語(yǔ)言的代碼。

2.1 Block 基本實(shí)現(xiàn)剖析

用 Xcode 創(chuàng)建 Command Line 項(xiàng)目,寫(xiě)如下代碼:

  1. int main(int argc, const char * argv[]) { 
  2.  
  3. void (^blk)(void) = ^{NSLog(@"Block")}; 
  4.  
  5. blk(); 
  6.  
  7. return 0; 
  8.  
  9.  

用 clang 轉(zhuǎn)換: 

 

 

 

以上是轉(zhuǎn)換后的代碼,不要方,一段一段看。

可以看到,Block 內(nèi)部的內(nèi)容,被轉(zhuǎn)換成了一個(gè)普通的靜態(tài)函數(shù) __main_func_0。

再看其他部分:

main.cpp __block_impl:

  1. struct __block_impl { 
  2.  
  3. void *isa; 
  4.  
  5. int Flags; 
  6.  
  7. int Reserved; 
  8.  
  9. void *FuncPtr; 
  10.  
  11. };  

__block_impl 結(jié)構(gòu)體包括了一些標(biāo)志、今后版本升級(jí)預(yù)留的變量、函數(shù)指針。

main.cpp __main_block_desc_0:

  1. static struct __main_block_desc_0 { 
  2.  
  3. size_t reserved; 
  4.  
  5. size_t Block_size; 
  6.  
  7. } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};  

__main_block_desc_0 結(jié)構(gòu)體包括了今后版本升級(jí)預(yù)留的變量、block 大小。

main.cpp __main_block_impl_0:

__main_block_impl_0 結(jié)構(gòu)體含有兩個(gè)成員變量,分別是 __block_impl 和 __main_block_desc_0實(shí)例變量。

此外,還含有一個(gè)構(gòu)造方法。該構(gòu)造方法在 main 函數(shù)中被如下調(diào)用:

main.cpp __main_block_impl_0 構(gòu)造函數(shù)的調(diào)用:

  1. void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, 
  2.  
  3. &__main_block_desc_0_DATA));  

去掉各種強(qiáng)制轉(zhuǎn)換,做簡(jiǎn)化:

main.cpp __main_block_impl_0 構(gòu)造函數(shù)的調(diào)用 簡(jiǎn)化:

  1. struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA); 
  2.  
  3. struct __main_block_impl_0 *blk = &tmp; 

 

以上代碼即:將 __main_block_impl_0 結(jié)構(gòu)體實(shí)例的指針,賦值給 __main_block_impl_0 結(jié)構(gòu)體指針類(lèi)型的變量 blk。也就是我們最初的結(jié)構(gòu)體定義:

  1. void (^blk)(void) = ^{NSLog(@"Block");}; 

另外,main 函數(shù)中還有另外一段:

  1. ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); 

去掉各種轉(zhuǎn)換:

  1. (*blk->impl.FuncPtr)(blk); 

實(shí)際就是最初的:

  1. blk(); 

本節(jié)所有代碼在 block_implementation (https://github.com/summertian4/iOS-ObjectiveC/tree/master/ObjcMemory/ObjcMemory-Test-Code/block_implementation)中

2.2 Block 截獲外部變量瞬間值的實(shí)現(xiàn)剖析

2.1 中對(duì)最簡(jiǎn)單的 無(wú)參數(shù) Block 聲明、調(diào)用 進(jìn)行了 clang 轉(zhuǎn)換。接下來(lái)再看一段『截獲自動(dòng)變量』的代碼(可以使用命令 clang -rewrite-objc -fobjc-arc -fobjc-runtime=macosx-10.7 main.m):

  1. int main(int argc, const char * argv[]) {  
  2.   
  3.  
  4. int val = 10; 
  5.  
  6. const char *fmt = "val = %d\n"
  7.  
  8. void (^blk)(void) = ^{printf(fmt, val);};  
  9.   
  10.  
  11. val = 2; 
  12.  
  13. fmt = "These values were changed, val = %d\n" 
  14.   
  15.  
  16. blk();  
  17.   
  18.  
  19. return 0; 
  20.  
  21.  

clang 轉(zhuǎn)換之后: 

 

 

 

和 2.1 節(jié)中的轉(zhuǎn)換代碼對(duì)比,可以發(fā)現(xiàn)多了一些代碼。

首先,__main_block_impl_0 多了一個(gè)變量 val,并在構(gòu)造函數(shù)的參數(shù)中加入了 val 的賦值:

main.cpp __main_block_impl_0:

  1. struct __main_block_impl_0 { 
  2.  
  3. struct __block_impl impl; 
  4.  
  5. struct __main_block_desc_0* Desc
  6.  
  7. const char *fmt; 
  8.  
  9. int val; 
  10.  
  11. __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) { 
  12.  
  13. impl.isa = &_NSConcreteStackBlock; 
  14.  
  15. impl.Flags = flags; 
  16.  
  17. impl.FuncPtr = fp; 
  18.  
  19. Desc = desc
  20.  
  21.  
  22. };  

而在 main 函數(shù)中,對(duì) Block 的聲明變?yōu)榇司洌?/p>

main.cpp __main_block_impl_0 構(gòu)造函數(shù)的調(diào)用:

  1. void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val)); 

去掉轉(zhuǎn)換:

main.cpp __main_block_impl_0 構(gòu)造函數(shù)的調(diào)用 簡(jiǎn)化:

  1. struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, val); 
  2.  
  3. struct __main_block_impl_0 *blk = &tmp;  

_所以,在 Block 被聲明時(shí),Block 已經(jīng)將 val 作為 __main_block_impl_0 的內(nèi)部變量保存下來(lái)了。無(wú)論在在聲明之后怎樣更改 val 的值,都不會(huì)影響,Block 調(diào)用時(shí)訪問(wèn)的內(nèi)部 val 值。這就是 Block 捕獲變量瞬間值的原理。_

本節(jié)所有代碼在 EX05 中

2.3 __block 變量的訪問(wèn)實(shí)現(xiàn)剖析

我們知道,Block 中能夠讀取,但是不能更改一個(gè)局部變量,如果去更改,Xcode 會(huì)提示你無(wú)法在 Block 內(nèi)部更改變量。

Block 內(nèi)部只是對(duì)局部變量只讀,但是 Block 能讀寫(xiě)以下幾種變量:

  1. 靜態(tài)變量
  2. 靜態(tài)全局變量
  3. 全局變量

也就是說(shuō)以下代碼是沒(méi)有問(wèn)題的:

  1. int global_val = 1; 
  2.  
  3. static int static_global_val = 2; 
  4.   
  5.  
  6. int main(int argc, const char * argv[]) { 
  7.  
  8. static int static_val = 3; 
  9.   
  10.  
  11. void (^blk)(void) = ^ { 
  12.  
  13. global_val = 1 * 2; 
  14.  
  15. static_global_val = 2 * 2; 
  16.  
  17. static_val = 3 * 2; 
  18.  
  19. }      
  20.  
  21. return 0; 
  22.  
  23.  

如果想在 Block 內(nèi)部寫(xiě)局部變量,需要對(duì)訪問(wèn)的局部變量增加 __block 修飾。

__block 修飾符其實(shí)類(lèi)似于 C 語(yǔ)言中 static、auto、register 修飾符。用于指定將變量值設(shè)置到哪個(gè)存儲(chǔ)域中。

具體 __block 之后究竟做了哪些變化我們可以寫(xiě)代碼測(cè)試:

EX07:

  1. int main(int argc, const char * argv[]) {  
  2.   
  3.  
  4. __block int val = 10; 
  5.  
  6. void (^blk)(void) = ^{val = 1;};  
  7.   
  8.  
  9. return 0; 
  10.  
  11.  

clang 轉(zhuǎn)換之后: 

 

 

 

跟 2.2 對(duì)比,似乎又加了非常代碼。發(fā)現(xiàn)多了兩個(gè)結(jié)構(gòu)體。

main.cpp __Block_byref_val_0:

  1. struct __Block_byref_val_0 { 
  2.  
  3. void *__isa; 
  4.  
  5. __Block_byref_val_0 *__forwarding; 
  6.  
  7. int __flags; 
  8.  
  9. int __size; 
  10.  
  11. int val; 
  12.  
  13. };  

很驚奇的發(fā)現(xiàn),block 類(lèi)型的 val 變成了結(jié)構(gòu)體 Block_byref_val_0的實(shí)例。這個(gè)實(shí)例內(nèi),包含了isa指針、一個(gè)標(biāo)志位flags、一個(gè)記錄大小的size。最最重要的,多了一個(gè)forwarding指針和val 變量。這是怎么回事?

在 main 函數(shù)部分,實(shí)例化了該結(jié)構(gòu)體:

main.cpp main.m 部分:

  1. __Block_byref_val_0 val = {(void*)0, 
  2.  
  3. (__Block_byref_val_0 *)&val, 
  4.  
  5. 0, 
  6.  
  7. sizeof(__Block_byref_val_0), 
  8.  
  9. 10};  

我們可以看出該結(jié)構(gòu)體對(duì)象初始化時(shí):

  1. __forwarding 指向了結(jié)構(gòu)體實(shí)例本身在內(nèi)存中的地址
  2. val = 10

而在 main 函數(shù)中,val = 1 這句賦值語(yǔ)句變成了:

main.cpp val = 1; 對(duì)應(yīng)的函數(shù)

  1. (val->__forwarding->val) = 1; 

這里就可以看出其精髓,val = 1,實(shí)際上更改的是 __Block_byref_val_0 結(jié)構(gòu)體實(shí)例 val 中的 __forwarding 指針(也就是本身)指向的 val 變量。 

 

 

 

而對(duì) val 訪問(wèn)也是如此。你可以理解為通過(guò)取地址改變變量的值,這和 C 語(yǔ)言中取地址改變變量類(lèi)似。

所以,聲明 block 的變量可以被改變。至于 forwarding 的其他巨大作用,會(huì)繼續(xù)分析。

本節(jié)代碼在 EX05 中

3 Block 的存儲(chǔ)域

Block 有三種類(lèi)型,分別是:

  1. __NSConcreteStackBlock ————————棧中
  2. __NSConcreteGlobalBlock ————————數(shù)據(jù)區(qū)域中
  3. __NSConcreteMallocBlock ————————堆中

__NSConcreteGlobalBlock 出現(xiàn)的地方有:

  1. 設(shè)置全局變量的地方有 Block 語(yǔ)法時(shí)
  2. Block 語(yǔ)法的表達(dá)式中不使用任何外部變量時(shí)

設(shè)置在棧上的 Block,如果所屬的變量作用域結(jié)束,Block 就會(huì)被廢棄。如果其中用到了 block,block 所屬的變量作用域結(jié)束也會(huì)被廢棄。

為了解決這個(gè)問(wèn)題,Block 在必要的時(shí)候就需要從棧中移到堆中。ARC 有效時(shí),很多情況下,編譯器會(huì)幫助完成 Block 的 copy,但很多情況下,我們需要手動(dòng) copy Block。

對(duì)不同存儲(chǔ)域的 Block copy 時(shí),影響如下: 

 

 

 

copy 時(shí),對(duì)訪問(wèn)到的 __block 類(lèi)型對(duì)象影響如下: 

 

 

 

此時(shí)可以看出 __forwarding 的巨大作用——無(wú)論 Block 此時(shí)在堆中還是在棧中,由于 __forwarding 指向局部變量轉(zhuǎn)換成的結(jié)構(gòu)體實(shí)例的真是地址,所以都能確保正確的訪問(wèn)。

具體的來(lái)說(shuō):

  1. 當(dāng) block 變量被一個(gè) Block 使用時(shí),Block 從棧復(fù)制到堆,block 變量也會(huì)被復(fù)制到,并被該 Block 持有。
  2. 在 block 變量被多個(gè) Block 使用時(shí),在任何一個(gè) Block 從棧復(fù)制到堆時(shí), block 變量也會(huì)被復(fù)制到堆,并被該 Block 持有。但由于 __forwarding 指針的存在,無(wú)論 block 變量和 Block 在不在同一個(gè)存儲(chǔ)域,都可以正確的訪問(wèn) block 變量。
  3. 如果堆上的 Block 被廢棄,那么它所使用的 __block 變量也會(huì)被釋放。 

 

 

 

前面說(shuō)到編譯器會(huì)幫助完成一些 Block 的 copy,也有手動(dòng) copy Block。那么 Block 被復(fù)制到堆上的情況有(此段摘自于『Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理』):

  1. 調(diào)用 Block 的 copy 方法時(shí)
  2. Block 作為返回值時(shí)
  3. 將 Block 賦值給附有 __strong 修飾符的成員變量時(shí)(id類(lèi)型或 Block 類(lèi)型)時(shí)
  4. 在方法名中含有 usingBlock 的 Cocoa 框架方法或 GCD 的 API 中傳遞 Block 時(shí)

4 Block 循環(huán)引用

Block 循環(huán)引用,是在編程中非常常見(jiàn)的問(wèn)題,甚至很多時(shí)候,我們并不知道發(fā)生了循環(huán)引用,直到我們突然某一天發(fā)現(xiàn)『怎么這個(gè)對(duì)象沒(méi)有調(diào)用 delloc』,才意識(shí)到有問(wèn)題存在。

在『Block 存儲(chǔ)域』中也說(shuō)明了 Block 在 copy 后對(duì) __block 對(duì)象會(huì) retain 一次。

那么對(duì)于如下情況就會(huì)發(fā)生循環(huán)引用: 

  1. block_retain_cycle: 
  2.  
  3.  
  4. @interface MyObject : NSObject  
  5.   
  6.  
  7. @property (nonatomic, copy) blk_t blk; 
  8.  
  9. @property (nonatomic, strong) NSObject *obj;  
  10.   
  11.  
  12. @end  
  13.   
  14.  
  15. @implementation MyObject  
  16.   
  17.  
  18. - (instancetype)init { 
  19.  
  20. self = [super init]; 
  21.  
  22. _blk = ^{NSLog(@"self = %@", self);}; 
  23.  
  24. return self; 
  25.  
  26.  
  27.   
  28.  
  29. - (void)dealloc { 
  30.  
  31. NSLog(@"%@ dealloc", self.class); 
  32.  
  33.  
  34.   
  35.  
  36. @end  
  37.   
  38.  
  39. int main(int argc, const char * argv[]) { 
  40.  
  41. id myobj = [[MyObject alloc] init]; 
  42.  
  43. NSLog(@"%@", myobj); 
  44.  
  45. return 0; 
  46.  
  47.  

由于 self -> blk,blk -> self,雙方都無(wú)法釋放。

但要注意的是,對(duì)于以下情況,同樣會(huì)發(fā)生循環(huán)引用:

  1. block_retain_cycle 
  2.   
  3.  
  4. @interface MyObject : NSObject 
  5.   
  6.  
  7. @property (nonatomic, copy) blk_t blk; 
  8.   
  9.  
  10. // 下面是多加的一句 
  11.  
  12. @property (nonatomic, strong) NSObject *obj; 
  13.   
  14.  
  15. @end 
  16.   
  17.  
  18. @implementation MyObject 
  19.   
  20.  
  21. - (instancetype)init { 
  22.  
  23. self = [super init]; 
  24.   
  25.  
  26. // 下面是多加的一句 
  27.  
  28. _blk = ^{NSLog(@"self = %@", _obj);}; 
  29.   
  30.  
  31. return self; 
  32.  
  33.   
  34.  
  35. - (void)dealloc { 
  36.  
  37. NSLog(@"%@ dealloc", self.class); 
  38.  
  39.   
  40.  
  41. @end 
  42.   
  43.  
  44. int main(int argc, const char * argv[]) { 
  45.  
  46. id myobj = [[MyObject alloc] init]; 
  47.  
  48. NSLog(@"%@", myobj); 
  49.  
  50. return 0; 
  51.  
  52.  

這是由于 self -> obj,self -> blk,blk -> obj。這種情況是非常容易被忽視的。

5 重審問(wèn)題

我們?cè)賮?lái)看看最初的幾個(gè)小題目: 

 

 

 

***題:

由于 Block 捕獲瞬間值,所以輸出為 in block val = 0

第二題:

由于 val 為 __block,外部更改會(huì)影響到內(nèi)部訪問(wèn),所以輸出為 in block val = 1

第三題:

和第二題類(lèi)似,val = 1 能影響到 Block 內(nèi)部訪問(wèn),所以先輸出 in block val = 1,之后在 Block 內(nèi)部更改 val 值,再次訪問(wèn)時(shí)輸出 after block val = 2。

Other

我寫(xiě)這篇文章是在我閱讀了『Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理』一書(shū)之后,博文中也有很內(nèi)容源于『Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理』。

非常向大家推薦此書(shū)。這本書(shū)里記錄了關(guān)于 iOS 內(nèi)存管理的深入內(nèi)容。但要注意的是,此書(shū)中的多處知識(shí)點(diǎn)并不是很詳細(xì),需要你以拓展的心態(tài)去學(xué)習(xí)。在有解釋不詳細(xì)的地方,自己主動(dòng)去探索,去拓展,找更多的資料,***,你會(huì)發(fā)現(xiàn)你對(duì) iOS 內(nèi)存管理有了更多的深入的理解。

對(duì)于文章中的測(cè)試代碼,全部在(https://github.com/summertian4/iOS-ObjectiveC/tree/master/ObjcMemory)。

責(zé)任編輯:龐桂玉 來(lái)源: iOS大全
相關(guān)推薦

2013-06-04 15:41:31

iOS開(kāi)發(fā)移動(dòng)開(kāi)發(fā)block

2025-01-10 09:47:43

blockSDKiOS

2017-03-07 10:15:35

iOS內(nèi)存管理開(kāi)發(fā)

2013-07-19 12:52:50

iOS中BlockiOS開(kāi)發(fā)學(xué)習(xí)

2011-08-08 18:11:45

IOS 4Block UIActionShe

2013-07-19 14:00:13

iOS中BlockiOS開(kāi)發(fā)學(xué)習(xí)

2013-07-19 14:35:59

iOS中BlockiOS開(kāi)發(fā)學(xué)習(xí)

2017-01-19 19:07:28

iOS進(jìn)階性能優(yōu)化

2013-07-19 13:16:26

iOS中BlockiOS開(kāi)發(fā)學(xué)習(xí)內(nèi)存管理

2010-09-16 09:13:09

CSS display

2014-07-30 11:12:09

block

2016-03-07 09:09:35

blockios開(kāi)發(fā)實(shí)踐

2014-07-31 16:47:10

block

2015-09-18 09:12:08

2011-07-29 16:16:30

Objective-c block

2010-04-07 16:54:55

Oracle性能

2010-09-03 12:55:15

CSSblockinline

2013-07-21 18:09:21

iOS開(kāi)發(fā)ASIHttpRequ創(chuàng)建和執(zhí)行reques

2010-09-03 10:18:06

CSSdisplay:inl

2010-09-14 15:32:51

CSSdisplay:inl
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美aaaaaaaaaa| 欧美激情黄色 | 欧美日韩一区二区在线 | 中文字幕国产精品 | 精品国产31久久久久久 | 亚洲精品久久区二区三区蜜桃臀 | 精品日本中文字幕 | 久久99精品国产自在现线小黄鸭 | 国产专区免费 | 国产视频久久久 | 中文字幕91av | 五月婷婷视频 | 九九福利 | 夜夜夜久久久 | 欧美精品在欧美一区二区 | 成人免费黄色片 | 深夜福利影院 | 国产精品久久久久久久久久不蜜臀 | 亚洲第一在线 | 不卡在线视频 | 久久久在线视频 | 中文字幕日韩av | 天天夜碰日日摸日日澡 | 久久伊人精品 | 中文字幕在线免费视频 | 91视频大全 | 日韩一区二区在线视频 | 国内精品久久久久久 | 日日操日日干 | 99综合在线 | 在线电影日韩 | 黄色免费三级 | 羞羞在线视频 | 精品国产黄色片 | 亚洲一区精品在线 | 久久久久久av | 久久精品色视频 | 中国一级特黄真人毛片免费观看 | 国产一区成人 | 久久国产精品久久久久久 | www.黄色在线观看 |