<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      主要內容:

      一、block相關的題目

      二、block的定義

      三、block的實現

      四、捕獲自動變量值

      五、block存儲區域

      六、截獲對象

      一、block相關的題目

      這是一篇比較長的博文,前部分是block的測試題目,中間是block的語法、特性,block講解block內部實現和block存儲位置,請讀者耐心閱讀。具備block基礎的同學,直接調轉到block的實現

      下面列出了五道題,看看能否答對兩三個。主要涉及block棧上、還是堆上、怎么捕獲變量。答案在博文最后一行

      1. //-----------第一道題:--------------  
      2. void exampleA() {  
      3.   char a = 'A';  
      4.   ^{ printf("%c\n", a);};  
      5. }  
      6. A.始終能夠正常運行          B.只有在使用ARC的情況下才能正常運行  
      7. C.不使用ARC才能正常運行     D.永遠無法正常運行  
      8. //-----------第二道題:答案同第一題--------------  
      9. void exampleB_addBlockToArray(NSMutableArray *array) {  
      10.   char b = 'B';  
      11.   [array addObject:^{printf("%c\n", b);}];  
      12. }   
      13. void exampleB() {  
      14.   NSMutableArray *array = [NSMutableArray array];  
      15.   exampleB_addBlockToArray(array);  
      16.   void (^block)() = [array objectAtIndex:0];  
      17.   block();  
      18. }  
      19. //-----------第三道題:答案同第一題--------------  
      20. void exampleC_addBlockToArray(NSMutableArray *array) {  
      21.   [array addObject:^{printf("C\n");}];  
      22. }   
      23. void exampleC() {  
      24.   NSMutableArray *array = [NSMutableArray array];  
      25.   exampleC_addBlockToArray(array);  
      26.   void (^block)() = [array objectAtIndex:0];  
      27.   block();  
      28. }  
      29. //-----------第四道題:答案同第一題--------------  
      30. typedef void (^dBlock)();   
      31. dBlock exampleD_getBlock() {  
      32.   char d = 'D';  
      33.   return ^{printf("%c\n", d);};  
      34. }  
      35. void exampleD() {  
      36.   exampleD_getBlock()();  
      37. }  
      38. //-----------第五道題:答案同第一題--------------  
      39. typedef void (^eBlock)();   
      40. eBlock exampleE_getBlock() {  
      41.   char e = 'E';  
      42.   void (^block)() = ^{printf("%c\n", e);};  
      43.   return block;  
      44. }  
      45. void exampleE() {  
      46.   eBlock block = exampleE_getBlock();  
      47.   block();  
      48. }  

      二、block的定義

       Block是C語言的擴充功能。可以用一句話來表示Blocks的擴充功能:帶有自動變量(局部變量)的匿名函數。命名就是工作的本質,函數名、變量名、方法名、屬性名、類名和框架名都必須具備。而能夠編寫不帶名稱的函數對程序員來說相當有吸引力。

          例如:我們要進行一個URL的請求。那么請求結果以何種方式通知調用者呢?通常是經過代理(delegate)但是,寫delegate本身就是成本,我們需要寫類、方法等等。
          這時候,我們就用到了block。block提供了類似由C++和OC類生成實例或對象來保持變量值的方法。像這樣使用block可以不聲明C++和OC類,也沒有使用靜態變量、靜態全局變量或全局變量,僅用編寫C語言函數的源碼量即可使用帶有自動變量值的匿名函數。
          其他語言中也有block概念。點擊查看官方block語法文檔

      三、block的實現

          block的語法看上去好像很特別,但實際上是作為極為普通的C語言代碼來處理的。這里我們借住clang編譯器的能力:具有轉化為我們可讀源代碼的能力。
      控制臺命令是: clang -rewrite-objc 源代碼文件名。 
      1. int main(){  
      2.     void (^blk)(void) = ^{printf("block\n");};  
      3.     blk();  
      4.     return 0;  
      5. }  
      經過 clang -rewrite-objc 之后,代碼編程這樣了(簡化后代碼,讀者可以搜索關鍵字在生成文件中查找):
      1. struct __block_impl{  
      2.     voidvoid *isa;  
      3.     int Flags;  
      4.     int Reserved;  
      5.     voidvoid *FuncPtr;  
      6. };  
      7. static struct __main_block_desc_0{  
      8.     unsigned long reserved;  
      9.     unsigned long Block_size  
      10. }__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};  
      11.   
      12. struct __main_block_impl_0{  
      13.     struct __block_impl impl;  
      14.     struct __main_block_desc_0 *Desc;  
      15. }  
      16. static struct __main_block_func_0(struct __main_block_impl_0 *__cself)  
      17. {  
      18.     printf("block\n");  
      19. }  
      20. int main(){  
      21.     struct __main_block_impl_0 *blk =  &__main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);  
      22.     (*blk->impl.FuncPtr)(blk);  
      23. }  
      很多結構體,很多下劃線的變量和函數名。我們一個個來:
      __block_impl:更像一個block的基類,所有block都具備這些字段。
      __main_block_impl_0:block變量。
      __main_block_func_0:雖然,block叫,匿名函數。但是,這個函數還是被編譯器起了個名字。
      __main_block_desc_0:block的描述,注意,他有一個實例__main_block_desc_0_DATA
          上述命名是有規則的:main是block所在函數的名字,后綴0則是這個函數中的第0個block。由于上面是C++的代碼,可以將__main_block_impl_0的結構體總結一下,得到如下形式:
      1. __main_block_impl_0{  
      2.     voidvoid *isa;  
      3.     int Flags;  
      4.     int Reserved;  
      5.     voidvoid *FuncPtr;  
      6.     struct __main_block_desc_0 *Desc;  
      7. }  
      總結:所謂block就是Objective-C的對象

      四、捕獲自動變量值

      1. int val = 10;  
      2. void (^blk)(void) = ^{printf("val=%d\n",val);};  
      3. val = 2;  
      4. blk();  

          上面這段代碼,輸出值是:val = 10.而不是2,點擊這里查看【block第二篇】block捕獲變量和對象。

      那么這個block的對象結構是什么樣呢,請看下面:
      1. __main_block_impl_0{  
      2.     voidvoid *isa;  
      3.     int Flags;  
      4.     int Reserved;  
      5.     voidvoid *FuncPtr;  
      6.     struct __main_block_desc_0 *Desc;  
      7.     int val;  
      8. }  
      這個val是如何傳遞到block結構體中的呢?
      1. int main(){  
      2.     struct __main_block_impl_0 *blk =  &__main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA,val);  
      3. }  
      注意函數調用最后一個參數,即val參數。
      那么函數調用的代碼頁轉化為下面這樣了.這里的cself跟C++的this和OC的self一樣。
      1. static struct __main_block_func_0(struct __main_block_impl_0 *__cself)  
      2. {  
      3.     printf("val=%d\n",__cself-val);  
      4. }  

      __block說明符

          前面講過block所在函數中的,捕獲自動變量。但是不能修改它,不然就是編譯錯誤。但是可以改變全局變量、靜態變量、全局靜態變量。
          其實這兩個特點不難理解:第一、為何不讓修改變量:這個是編譯器決定的。理論上當然可以修改變量了,只不過block捕獲的是自動變量的副本,名字一樣。為了不給開發者迷惑,干脆不讓賦值。道理有點像:函數參數,要用指針,不然傳遞的是副本。
          第二、可以修改靜態變量的值。靜態變量屬于類的,不是某一個變量。所以block內部不用調用cself指針。所以block可以調用。
          解決block不能保存值這一問題的另外一個辦法是使用__block修飾符。
      1. __block int val = 10;  
      2. void (^blk)(void) = ^{val = 1;};  
      該源碼轉化后如下:
      1. struct __block_byref_val_0{  
      2.     voidvoid *__isa;  
      3.     __block_byref_val_0 *__forwarding;  
      4.     int _flags;  
      5.     int __size;  
      6.     int val;  
      7. }  
      __main_block_impl_0中自然多了__block_byreg_val_0的一個字段。注意:__block_byref_val_0結構體中有自身的指針對象,難道要
      _block int val = 10;這一行代碼,轉化成了下面的結構體
      __block)byref_val_0 val = {0,&val,0,sizeof(__block_byref_val_0),10};//自己持有自己的指針。
      它竟然變成了結構體了。之所以為啥要生成一個結構體,后面在詳細講講。反正不能直接保存val的指針,因為val是棧上的,保存棧變量的指針很危險。

      五、block存儲區域

      這就需要引入三個名詞:
      ● _NSConcretStackBlock
      ● _NSConcretGlobalBlock
      ● _NSConcretMallocBlock
      正如它們名字說的那樣,說明了block的三種存儲方式:棧、全局、堆。__main_block_impl_0結構體中的isa就是這個值。
      【要點1】如果是定義在函數外面的block是global的,另外如果函數內部的block但是,沒有捕獲任何自動變量,那么它也是全局的。比如下面這樣的代碼:
      1. typedef int (^blk_t)(int);  
      2. for(...){  
      3.     blk_t blk = ^(int count) {return count;};  
      4. }  
      雖然,這個block在循環內,但是blk的地址總是不變的。說明這個block在全局段。
      【要點2】一種情況在非ARC下是無法編譯的:
      typedef int(^blk_t)(int);
      blk_t func(int rate){
          return ^(int count){return rate*count;}
      }
      這是因為:block捕獲了棧上的rate自動變量,此時rate已經變成了一個結構體,而block中擁有這個結構體的指針。即如果返回 block的話就是返回局部變量的指針。而這一點恰是編譯器已經斷定了。在ARC下沒有這個問題,是因為ARC使用了autorelease了。
      【要點3】有時候我們需要調用block 的copy函數,將block拷貝到堆上。看下面的代碼:
      1. -(id) getBlockArray{  
      2.     int val =10;  
      3.     return [[NSArray alloc]initWithObjects:  
      4.         ^{NSLog(@"blk0:%d",val);},  
      5.         ^{NSLog(@"blk1:%d",val);},nil];  
      6. }  
      7.   
      8. id obj = getBlockArray();  
      9. typedef void (^blk_t)(void);  
      10. blk_t blk = (blk_t){obj objectAtIndex:0};  
      11. blk();  
      這段代碼在最后一行blk()會異常,因為數組中的block是棧上的。因為val是棧上的。解決辦法就是調用copy方法。
      【要點4】不管block配置在何處,用copy方法復制都不會引起任何問題。在ARC環境下,如果不確定是否要copy block盡管copy即可。ARC會打掃戰場。
      注意:在棧上調用copy那么復制到堆上,在全局block調用copy什么也不做,在堆上調用block 引用計數增加
      【注意】本人用Xcode 5.1.1 iOS sdk 7.1 編譯發現:并非《Objective-C》高級編程這本書中描述的那樣
      int val肯定是在棧上的,我保存了val的地址,看看block調用前后是否變化。輸出一致說明是棧上,不一致說明是堆上。
      1. typedef int (^blkt1)(void) ;  
      2. -(void) stackOrHeap{  
      3.     __block int val =10;  
      4.     intint *valPtr = &val;//使用int的指針,來檢測block到底在棧上,還是堆上  
      5.     blkt1 s= ^{  
      6.         NSLog(@"val_block = %d",++val);  
      7.         return val;};  
      8.     s();  
      9.     NSLog(@"valPointer = %d",*valPtr);  
      10. }  
      在ARC下——block捕獲了自動變量,那么block就被會直接生成到堆上了。 val_block = 11 valPointer = 10
      在非ARC下——block捕獲了自動變量,該block還是在棧上的。 val_block = 11 valPointer = 11

      調用copy之后的結果呢:

      1. -(void) stackOrHeap{  
      2.     __block int val =10;  
      3.     intint *valPtr = &val;//使用int的指針,來檢測block到底在棧上,還是堆上  
      4.     blkt1 s= ^{  
      5.         NSLog(@"val_block = %d",++val);  
      6.         return val;};  
      7.     blkt1 h = [s copy];  
      8.     h();  
      9.     NSLog(@"valPointer = %d",*valPtr);  
      10. }  

      在ARC下>>>>>>>>>>>無效果。 val_block = 11 valPointer = 10

      在非ARC下>>>>>>>>>確實復制到堆上了。 val_block = 11 valPointer = 10
      用這個表格來表示
       

      在ARC下:似乎已經沒有棧上的block了,要么是全局的,要么是堆上的
      在非ARC下:存在這棧、全局、堆這三種形式。
      更詳細的描述專題點擊打開鏈接
       

      __block變量存儲區域

      當block被復制到堆上時,他所捕獲的對象、變量也全部復制到堆上。
      回憶一下block捕獲自動變量的時候,自動變量將編程一個結構體,結構體中有一個字段叫__forwarding,用于指向自動這個結構體。那么有了這個__forwarding指針,無論是棧上的block還是被拷貝到堆上,那么都會正確的訪問自動變量的值。

      六、截獲對象

      block會持有捕獲的對象。編譯器為了區分自動變量和對象,有一個類型來區分。
      1. static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src){  
      2.     _Block_objct_assign(&dst->val,src->val,BLOCK_FIELD_IS_BYREF);  
      3. }  
      4. static void __main_block_dispose_0(struct __main_block_impl_0 *src){  
      5.     _block_object_dispose(src->val,BLOCK_FIELD_IS_BYREF);  
      6. }  
      BLOCK_FIELD_IS_BYREF代表是變量。BLOCK_FIELD_IS_OBJECT代表是對象
      【__block變量和對象】
          __block修飾符可用于任何類型的自動變量
      【__block循環引用】
      根據上面講的內容,block在持有對象的時候,對象如果持有block,會造成循環引用。解決辦法有兩種:
      1. 使用__weak修飾符。id __weak obj = obj_
      2. 使用__block修飾符。__block id tmp = self;然后在block中tmp = nil;這樣就打破循環了。這個辦法需要記得將tmp=nil。不推薦!
      posted on 2015-06-26 10:45  轉身看見海  閱讀(275)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 777奇米四色成人影视色区| 99精品国产成人一区二区| 99久久免费精品色老| 四房播色综合久久婷婷| 欧美日韩一区二区三区视频播放| 人人妻人人澡人人爽曰本| 国产无遮挡又黄又爽免费网站| 91亚洲国产成人久久蜜臀| 亚洲人妻精品一区二区| 日韩精品中文女同在线播放| 爆乳日韩尤物无码一区| 国产桃色在线成免费视频| 国产偷国产偷亚洲高清人| 亚洲婷婷综合色高清在线 | 国产成人综合久久亚洲精品| 欧美日韩高清在线观看| 国产99在线 | 亚洲| 97视频精品全国免费观看| 精品国产中文字幕av| 玉树县| 麻豆一区二区三区精品蜜桃| 国产美女高潮流白浆视频| 日韩中文字幕av有码| 国产怡春院无码一区二区| 中文字幕精品人妻av在线| 久久精品国产男包| 九九热精品免费视频| 中文无码热在线视频| 美女内射毛片在线看3d| 久久夜色精品亚洲国产av| 97久久综合亚洲色hezyo| 苍井空毛片精品久久久| 国产精品男女爽免费视频| 91国在线啪精品一区| 丁香五月亚洲综合深深爱| 国产精品爽爽v在线观看无码| 精品人妻av综合一区二区| 人妻少妇偷人精品一区| 卡一卡2卡3卡精品网站| 国产av丝袜旗袍无码网站| 最近中文字幕日韩有码|