整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          Block中嵌套block需要注意的事項

          Block中嵌套block需要注意的事項

          近在看[Blocks Programming Topics](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html#//apple_ref/doc/uid/TP40007502-CH1-SW1)中[Blocks](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW7)有如下的這段話:

          When you copy a block, any references to other blocks from within that block are copied if necessary—an entire tree may be copied (from the top). If you have block variables and you reference a block from within the block, that block will be copied.

          大致翻譯如下:

          當(dāng)你復(fù)制一個block的時候,在這個block里的其他block任何引用也會被復(fù)制,如果有必要的話,一顆完整的樹也許被復(fù)制(從頂部開始)。如果你有block變量并且你在這個block引用了另一個block,另一個block也會被復(fù)制。

          測試對象:`RXBlockBlockObject`

          @interface RXBlockBlockObject : NSObject
          - (void)test;
          @end
          

          `RXBlockBlockObject.m`文件中:

          @interface RXBlockBlockObject()
          @property (nonatomic, copy) int(^block)(int);
          @property (nonatomic, assign) int tmpValue;
          @end
          @implementation RXBlockBlockObject
          - (void)test
          {
           [self _test_block_normal];
          // [self _test_block_nested_retain_cycle];
          // [self _test_block_nested];
          }
          - (void)dealloc
          {
           NSLog(@"RXBlockBlockObject dellloc");
          }
          @end
          

          測試類中:

          - (void)_test_block
          {
           RXBlockBlockObject *tmp=[RXBlockBlockObject new];
           [tmp test];
           // tmp 會被釋放的
          }
          

          `_test_block_normal` method

          - (void)_test_block_normal
          {
           self.tmpValue=10;
           self.block=^(int m) {
           return m + 4;
           };
           void (^block1)(void)=^{
           NSLog(@"%zd", self.block(5));
           };
           dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), block1);
          }
          

          持有關(guān)系如下描述:

          `self(viewController)`持有了`block`,`dispatch_after`的一個全局管理器持有了`block1`,`block1`持有了`self`,沒有出現(xiàn)循環(huán)引用。

          輸出如下:

          9
          RXBlockBlockObject dellloc
          

          `_test_block_nested_retain_cycle` method

          - (void)_test_block_nested_retain_cycle
          {
           self.tmpValue=10;
           
           // A(self)強引用了B(block),B強引用了C(block1),C強引用了A(self)了,導(dǎo)致循環(huán)引用
           self.block=^(int m) {
           // inline block
           
           void (^block1)(void)=^{
           // 不會提示warning,但是實際上已經(jīng)出現(xiàn)了循環(huán)引用
           NSLog(@"%zd", self.tmpValue + m);
           // 會提示warning: Capturing 'self' strongly in this block is likely to load to a retain cycle
           // 但是感覺這是無限遞歸調(diào)用了~~~~
           // NSLog(@"%zd", self.block(10));
           };
           dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), block1);
           return m + 4;
           };
           
           void (^block2)(void)=^{
           NSLog(@"%zd", self.block(5));
           };
           dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), block2);
          }
          

          持有關(guān)系如注釋中的描述:

          A(`self`)強引用了B(`block`),B強引用了C(`block1`),C強引用了A(`self`)了,導(dǎo)致循環(huán)引用。

          輸出結(jié)果是:

          9
          15
          

          不會調(diào)用dealloc,出現(xiàn)循環(huán)引用,內(nèi)存泄漏了

          `_test_block_nested` method 上一個的進化版本,解決循環(huán)引用

          - (void)_test_block_nested
          {
           self.tmpValue=10;
           __weak __typeof(self) weakSelf=self;
           self.block=^(int m) {
           // inline block
           __strong __typeof(self) strongSelf=weakSelf;
           void (^block1)(void)=^{
           NSLog(@"%zd", strongSelf.tmpValue + m);
           };
           dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), block1);
           return m + 4;
           };
           dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
           NSLog(@"%zd", self.block(5));
           });
          }
          

          使用`__weak`和`__strong`解決,`block2`延長了`self`的生命周期

          輸出結(jié)果:

          近在看[Blocks Programming Topics](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html#//apple_ref/doc/uid/TP40007502-CH1-SW1)中[Creating a Block](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/Articles/bxDeclaringCreating.html#//apple_ref/doc/uid/TP40007502-CH4-SW4)有如下的這段話:

          If you don’t explicitly declare the return value of a block expression, it can be automatically inferred from the contents of the block. If the return type is inferred and the parameter list is `void`, then you can omit the (`void`) parameter list as well. If or when multiple return statements are present, they must exactly match (using casting if necessary).

          總共有三句話,分別字面上的翻譯如下

          1. 如果你沒有明確的聲明一個block表達(dá)式的返回值,他可能會根據(jù)block的內(nèi)容來推斷出返回值。

          2. 如果返回類型是被推斷出的并且參數(shù)列表是`void`,那么你同樣可以省略(`void`)參數(shù)列表。

          3. 如果或者當(dāng)多個返回表達(dá)式出現(xiàn)的時候,它們必須精確的匹配上(如果有必要的話使用強制轉(zhuǎn)換)。

          首先我是對block表達(dá)式的概念理解有誤,所以做了如下的測試:

          - (void)_incorrect_test
          {
           // 以下的例子都是錯誤的(編譯報錯),因為在定義變量的時候,必須要明確的指定返回類型
           // 是block表達(dá)式中省略了返回值類型
          // (^block1)(void)=^(void) {
          // return nil;
          // };
          //
          // (^block2)(void)=id ^(void) {
          // return nil;
          // };
          //
          // (^block3)(void)=(id)^(void) {
          // return nil;
          // };
          }
          

          首先對于如下的一個block聲明:

           void* (^block2)(void)=^(void) {
           return nil;
           };
          

          `=`左邊的是變量聲明,而`=`右邊的才是`block expression`。

          正確的理解了`block expression`,做了如下的測試代碼:

          - (void)_correct_test
          {
           // 這個例子是錯誤的,因為nil在這里被理解為了 void*
          // id (^block1)(void)=^(void) {
          // return nil;
          // };
           // 這樣就可以了,定義變量的時候,讓其返回void*
           void* (^block2)(void)=^(void) {
           return nil;
           };
           // 在block表達(dá)式中,省略了返回值類型
           id (^block3)(void)=^(void) {
           return [NSObject new];
           };
           // 在block表達(dá)式中,明確的指出了返回值類型
           // 不是明確的指出了,是強制轉(zhuǎn)為id類型
           id (^block4)(void)=(id)^(void) {
           return [NSObject new];
           };
           // 當(dāng)參數(shù)列表是void的時候,在block表達(dá)式中可以省略
           // 返回值類型是推斷的 為void
           void (^block5)(void)=^{
           NSLog(@"1");
           };
           // 當(dāng)參數(shù)列表是void的時候,在block表達(dá)式中可以省略
           // 返回值類型是推斷的 為id
           id (^block6)(void)=^{
           return [NSObject new];
           };
           // 當(dāng)參數(shù)列表是void的時候,在block表達(dá)式中可以省略
           // 返回值類型是指明的 為id
           id (^block7)(void)=(id)^{
           return [NSObject new];
           };
           id block8=^(int m) {
           switch (m) {
           case 1:
           {
           // 這里推斷出block應(yīng)該返回int
           return 1;
           }
           break;
          // case 2:
          // {
          // // Error: Return type 'NSObject *' must match previous return type 'int' when block literal has unspecified explicit return type
          // return [NSObject new];
          // }
          // break;
           case 3:
           {
           // 強行把double轉(zhuǎn)換為整形,也許通常我們的做法是把case 1: 中返回int轉(zhuǎn)換為返回double
           return (int)(m + 4.0);
           }
           break;
          // case 4:
          // {
          // // Error: Return type 'void *' must match previous return type 'int' when block literal has unspecified explicit return type
          // return nil;
          // }
          // break;
           default:
           {
           return 0;
           }
           break;
           }
           };
           // block是一個對象,所以可以直接定義id block9
           id block9=^(int m) {
           return 5 + m;
           };
           // 正確的方法:顯示的指明返回值類型
           void (^block10)(void)=^void(void) {
           NSLog(@"11");
           };
           int (^block11)(int m)=^int(int m) {
           return m + 4;
           };
           int (^block12)(void)=^int {
           return 4;
           };
           block2();
           block3();
           block4();
           block5();
           block6();
           block7();
           int (^block_transf8)(int)=block8;
           block_transf8(1);
           int (^block_transf9)(int)=block9;
           block_transf9(1);
           block10();
           block11(2);
           block12();
           id block20=^void (void) { printf("hello world\n"); };
           id block21=^(void) { printf("hello world\n"); };
           id block22=^{ printf("hello world\n"); };
           id block23=^void { printf("hello world\n"); };
           
           NSLog(@"%@, %@, %@, %@", block20, block21, block22, block23);
          }
          

          可以得知如下幾個結(jié)論:

          1. `block1`和`block2`可以得知,`block`會把`nil`推斷為`void *`而不是`id`

          2. `block3`和`block4`可以得知,第一時間推斷出可以在`block expression`中指明返回類型,實際這個說法是錯誤的,前面的`(id)`是把`^{}`強制轉(zhuǎn)換為`id`的意思,而不是顯式的說這個block(`^{}`)的返回類型是`id`。

          3. `block5`,`block6`,`block7`,`block12`可以得知,只要是參數(shù)列表為`void`的時候,都可以省略,而不是文檔中描述的,還需要是推斷的返回類型(也許跟文檔一直沒有更新有關(guān))。`block20`,`block21`,`block22`,`block23`。

          4. `block8`是驗證第三句話的。

          5. `block9`是驗證 `block`是一個對象這個結(jié)論的。

          6. `block10`和`block11`是顯式的指明一個block的返回值類型

          所以原文應(yīng)該需要做如下的修改:

          第一句話,應(yīng)該是把返回值(`return value`)改成返回類型(`return type`)

          第二句話,可以把`the return type is inferred and`刪除

          有一個這樣的網(wǎng)站:http://goshdarnblocksyntax.com/ 介紹了大部分的寫法

          現(xiàn)在列出如下:

          As a local variable:(Demo1)

          returnType (^blockName)(parameterTypes)=^returnType(parameters) {...};

          As a property:(Demo2)

          @property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes);

          As a method parameter:(Demo3)

          - (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;

          As an argument to a method call:(Demo4)

          [someObject someMethodThatTakesABlock:^returnType (parameters) {...}];

          As a typedef:(Demo5)

          typedef returnType (^TypeName)(parameterTypes);

          TypeName blockName=^returnType(parameters) {...};

          首先我感覺還是缺少了一個例子:

          As a Type:(Demo6)

          returnType(^blockName)(parameterTypes)=(returnType(^)(parameterTypes))variable;

          即對于上面的例子例子`block8`來說就是:

          int (^block_transf8)(int)=(int(^)(int))block8;
          

          總結(jié)

          可以根據(jù)如下兩點:

          block的各種寫法樣例

          把block想象成一個函數(shù):`returnType functionName(paramTypes) {...}`

          可以上述所有的例子歸納成如下兩種:

          在仔細(xì)看著兩個`format`就會發(fā)現(xiàn),`block type`的格式跟`returnType functionName(paramTypes)`類似啊,只不過給`^`加了`()`,而`block expression` 是把`returnType`和`^`掉了個位置,去掉了`^`的括號。

          掌握了這些規(guī)矩,我覺得以后再寫有關(guān)block的時候就不需要再去查相關(guān)網(wǎng)站了吧。

          Blockquote 對象

          Blockquote 對象代表著一個 HTML 引用(blockquote)塊元素。

          <blockquote> 標(biāo)簽定義摘自另一個源的塊引用。

          一個 <blockquote> 元素的內(nèi)容通常展現(xiàn)為一個左、右兩邊進行縮進的段落。

          在 HTML 文檔中的每一個 <blockquote> 標(biāo)簽創(chuàng)建時,都會創(chuàng)建一個 Blockquote 對象。

          Blockquote 對象屬

          屬性描述
          cite設(shè)置或返回一個引用的cite屬性值

          標(biāo)準(zhǔn)屬性和事件

          Blockquote 對象支持標(biāo)準(zhǔn) 屬性 和事件。

          如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!


          主站蜘蛛池模板: 国产精品视频一区二区噜噜| 一区二区三区AV高清免费波多| 无码人妻AV免费一区二区三区| 黑人大战亚洲人精品一区| 无码乱人伦一区二区亚洲一| 日韩精品一区二区午夜成人版 | 好湿好大硬得深一点动态图91精品福利一区二区 | 亚洲一区二区三区在线视频| 竹菊影视欧美日韩一区二区三区四区五区| 国产一区二区三区在线| 亚洲综合在线一区二区三区| 3d动漫精品啪啪一区二区中 | 中文字幕精品一区二区| 久久4k岛国高清一区二区| 亚洲国产综合无码一区二区二三区| 国产综合视频在线观看一区| 无码一区18禁3D| 亚洲一区二区久久| 国产91久久精品一区二区| 91一区二区视频| 亚洲区精品久久一区二区三区| 色久综合网精品一区二区| 精品久久一区二区| 久久亚洲一区二区| 97久久精品一区二区三区| 日本免费一区二区三区 | 精品国产一区二区麻豆| 亚洲码欧美码一区二区三区| 亚洲熟女综合色一区二区三区| 国产日本一区二区三区| 乱码人妻一区二区三区| 波多野结衣AV无码久久一区| 伊人色综合一区二区三区| 无码人妻一区二区三区免费看| 人妻体体内射精一区二区| 伊人久久大香线蕉AV一区二区| 成人区人妻精品一区二区三区 | 无码人妻精品一区二区三区夜夜嗨 | 日本在线不卡一区| 中文字幕日本一区| 久久亚洲国产精品一区二区|