整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

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.

大致翻译如下:

当你复制一个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 会被释放的
}

`_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);
}

持有关系如下描述:

`self(viewController)`持有了`block`,`dispatch_after`的一个全局管理器持有了`block1`,`block1`持有了`self`,没有出现循环引用。

输出如下:

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)了,导致循环引用
 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,出现循环引用,内存泄漏了

`_test_block_nested` method 上一个的进化版本,解决循环引用

- (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,我们一起飞!