近在看[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.
大致翻译如下:
当你复制一个block的时候,在这个block里的其他block任何引用也会被复制,如果有必要的话,一颗完整的树也许被复制(从顶部开始)。如果你有block变量并且你在这个block引用了另一个block,另一个block也会被复制。
测试对象:`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); }
持有关系如下描述:
`self(viewController)`持有了`block`,`dispatch_after`的一个全局管理器持有了`block1`,`block1`持有了`self`,没有出现循环引用。
输出如下:
9 RXBlockBlockObject dellloc
- (void)_test_block_nested_retain_cycle { self.tmpValue=10; // A(self)强引用了B(block),B强引用了C(block1),C强引用了A(self)了,导致循环引用 self.block=^(int m) { // inline block void (^block1)(void)=^{ // 不会提示warning,但是实际上已经出现了循环引用 NSLog(@"%zd", self.tmpValue + m); // 会提示warning: Capturing 'self' strongly in this block is likely to load to a retain cycle // 但是感觉这是无限递归调用了~~~~ // 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); }
持有关系如注释中的描述:
A(`self`)强引用了B(`block`),B强引用了C(`block1`),C强引用了A(`self`)了,导致循环引用。
输出结果是:
9 15
不会调用dealloc,出现循环引用,内存泄漏了
- (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`的生命周期
输出结果:
近在看[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表达式的返回值,他可能会根据block的内容来推断出返回值。
2. 如果返回类型是被推断出的并且参数列表是`void`,那么你同样可以省略(`void`)参数列表。
3. 如果或者当多个返回表达式出现的时候,它们必须精确的匹配上(如果有必要的话使用强制转换)。
首先我是对block表达式的概念理解有误,所以做了如下的测试:
- (void)_incorrect_test { // 以下的例子都是错误的(编译报错),因为在定义变量的时候,必须要明确的指定返回类型 // 是block表达式中省略了返回值类型 // (^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表达式中,省略了返回值类型 id (^block3)(void)=^(void) { return [NSObject new]; }; // 在block表达式中,明确的指出了返回值类型 // 不是明确的指出了,是强制转为id类型 id (^block4)(void)=(id)^(void) { return [NSObject new]; }; // 当参数列表是void的时候,在block表达式中可以省略 // 返回值类型是推断的 为void void (^block5)(void)=^{ NSLog(@"1"); }; // 当参数列表是void的时候,在block表达式中可以省略 // 返回值类型是推断的 为id id (^block6)(void)=^{ return [NSObject new]; }; // 当参数列表是void的时候,在block表达式中可以省略 // 返回值类型是指明的 为id id (^block7)(void)=(id)^{ return [NSObject new]; }; id block8=^(int m) { switch (m) { case 1: { // 这里推断出block应该返回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转换为整形,也许通常我们的做法是把case 1: 中返回int转换为返回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); }
可以得知如下几个结论:
1. `block1`和`block2`可以得知,`block`会把`nil`推断为`void *`而不是`id`
2. `block3`和`block4`可以得知,第一时间推断出可以在`block expression`中指明返回类型,实际这个说法是错误的,前面的`(id)`是把`^{}`强制转换为`id`的意思,而不是显式的说这个block(`^{}`)的返回类型是`id`。
3. `block5`,`block6`,`block7`,`block12`可以得知,只要是参数列表为`void`的时候,都可以省略,而不是文档中描述的,还需要是推断的返回类型(也许跟文档一直没有更新有关)。`block20`,`block21`,`block22`,`block23`。
4. `block8`是验证第三句话的。
5. `block9`是验证 `block`是一个对象这个结论的。
6. `block10`和`block11`是显式的指明一个block的返回值类型
所以原文应该需要做如下的修改:
第一句话,应该是把返回值(`return value`)改成返回类型(`return type`)
第二句话,可以把`the return type is inferred and`删除
有一个这样的网站:http://goshdarnblocksyntax.com/ 介绍了大部分的写法
现在列出如下:
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;
可以根据如下两点:
block的各种写法样例
把block想象成一个函数:`returnType functionName(paramTypes) {...}`
可以上述所有的例子归纳成如下两种:
在仔细看着两个`format`就会发现,`block type`的格式跟`returnType functionName(paramTypes)`类似啊,只不过给`^`加了`()`,而`block expression` 是把`returnType`和`^`掉了个位置,去掉了`^`的括号。
掌握了这些规矩,我觉得以后再写有关block的时候就不需要再去查相关网站了吧。
Blockquote 对象
Blockquote 对象代表着一个 HTML 引用(blockquote)块元素。
<blockquote> 标签定义摘自另一个源的块引用。
一个 <blockquote> 元素的内容通常展现为一个左、右两边进行缩进的段落。
在 HTML 文档中的每一个 <blockquote> 标签创建时,都会创建一个 Blockquote 对象。
Blockquote 对象属性
属性 | 描述 |
---|---|
cite | 设置或返回一个引用的cite属性值 |
标准属性和事件
Blockquote 对象支持标准 属性 和事件。
如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!
*请认真填写需求信息,我们会在24小时内与您取得联系。