近在看[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 會被釋放的 }
- (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
- (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)存泄漏了
- (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;
可以根據(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,我們一起飛!
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。