[小测试] 以下情景中有哪些会造成循环引用?

2015-12-16 17:16:35 +08:00
 vincentxue
帮朋友理解 Block 和 循环引用,写了几个测试,欢迎大家也测一下。

1 、全局变量中的 Block 属性包含了 self 。

```objc
@interface Object1 : NSObject
@property (nonatomic, copy) void(^block)();
@end

@interface ViewController ()
@property (nonatomic, strong) Object1 *obj;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.obj.block = ^ {
NSLog(@"%@", self);
};
}

@end
```

2 、全局变量中的 Block 属性包含了 self 的全局变量。

```objc
@interface Object1 : NSObject
@property (nonatomic, copy) void(^block)();
@end

@interface Object2 : NSObject
@end

@interface ViewController ()
@property (nonatomic, strong) Object1 *obj;
@property (nonatomic, strong) Object2 *obj2;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.obj.block = ^ {
NSLog(@"%@", _obj2);
};
}

@end
```

3 、全局变量中的 Block 属性包含了 self 的局部变量。

```objc
@interface Object1 : NSObject
@property (nonatomic, copy) void(^block)();
@end

@interface ViewController ()
@property (nonatomic, strong) Object1 *obj;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

NSString *string = @"";
self.obj.block = ^ {
NSLog(@"%@", string);
};
}

@end
```

4 、全局变量中的方法中的 Block 参数包含了 self 。

```objc
@interface Object1 : NSObject
- (void)foo:(void(^)())param;
@end

@interface ViewController ()
@property (nonatomic, strong) Object1 *obj;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[self.obj foo:^{
NSLog(@"%@", self);
}];
}

@end
```

5 、全局变量中的方法中的 Block 参数包含了 self 的全局变量。

```objc
@interface Object1 : NSObject
- (void)foo:(void(^)())param;
@end

@interface Object2 : NSObject
@end

@interface ViewController ()
@property (nonatomic, strong) Object1 *obj;
@property (nonatomic, strong) Object2 *obj2;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[self.obj foo:^{
NSLog(@"%@", _obj2);
}];
}

@end
```

6 、全局变量中的方法中的 Block 参数包含了 self 的局部变量。

```objc
@interface Object1 : NSObject
- (void)foo:(void(^)())param;
@end

@interface ViewController ()
@property (nonatomic, strong) Object1 *obj;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

NSString *string = @"";
[self.obj foo:^{
NSLog(@"%@", string);
}];
}

@end
```

7 、 self 的 Block 属性包含了 self 。

```objc
@interface ViewController ()
@property (nonatomic, copy) void(^block)();
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.block = ^ {
NSLog(@"%@", self);
};
}

@end
```

8 、 self 的 Block 属性包含了 self 的全局变量。

```objc
@interface ViewController ()
@property (nonatomic, copy) void(^block)();
@property (nonatomic, strong) NSString *string;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.block = ^ {
NSLog(@"%@", _string);
};
}

@end
```

9 、 self 的 Block 属性包含了 self 的局部变量。

```objc
@interface ViewController ()
@property (nonatomic, copy) void(^block)();
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

NSString *string = @"";
self.block = ^ {
NSLog(@"%@", string);
};
}

@end
```

10 、 self 的 Block 本地变量包含了 self 。

```objc
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

void(^block)() = ^ {
NSLog(@"%@", self);
};
}

@end
```

11 、 self 的 Block 本地变量包含了 self 的全局变量。

```objc
@interface ViewController ()
@property (nonatomic, strong) NSString *string;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

void(^block)() = ^ {
NSLog(@"%@", _string);
};
}

@end
```

12 、 self 的实例方法中的 Block 参数包含了 self 。

```objc
@interface ViewController ()
@end

@implementation ViewController

- (void)foo:(void(^)())param {}

- (void)viewDidLoad {
[super viewDidLoad];

[self foo:^{
NSLog(@"%@", self);
}];
}

@end
```

13 、 self 的实例方法中的 Block 参数包含了 self 的全局变量。

```objc
@interface ViewController ()
@property (nonatomic, strong) NSString *string;
@end

@implementation ViewController

- (void)foo:(void(^)())param {}

- (void)viewDidLoad {
[super viewDidLoad];

[self foo:^{
NSLog(@"%@", _string);
}];
}

@end
```

14 、 self 的实例方法中的 Block 参数包含了 self 的局部变量。

```objc
@interface ViewController ()
@end

@implementation ViewController

- (void)foo:(void(^)())param {}

- (void)viewDidLoad {
[super viewDidLoad];

NSString *string = @"";
[self foo:^{
NSLog(@"%@", string);
}];
}

@end
```

15 、 self 的类方法中的 Block 参数包含了 self 。

```objc
@interface ViewController ()
@end

@implementation ViewController

+ (void)foo:(void(^)())param {}

- (void)viewDidLoad {
[super viewDidLoad];

[[self class] foo:^{
NSLog(@"%@", self);
}];
}

@end
```

16 、 self 的类方法中的 Block 参数包含了 self 的全局变量。

```objc
@interface ViewController ()
@property (nonatomic, copy) NSString *string;
@end

@implementation ViewController

+ (void)foo:(void(^)())param {}

- (void)viewDidLoad {
[super viewDidLoad];

[[self class] foo:^{
NSLog(@"%@", _string);
}];
}

@end
```

17 、 self 的类方法中的 Block 参数包含了 self 的局部变量。

```objc
@interface ViewController ()
@end

@implementation ViewController

+ (void)foo:(void(^)())param {}

- (void)viewDidLoad {
[super viewDidLoad];

NSString *string = @"";
[[self class] foo:^{
NSLog(@"%@", string);
}];
}

@end
```

18 、其他类中的类方法中的 Block 参数包含了 self 。

```objc
@interface Object1 : NSObject
+ (void)foo:(void(^)())param;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[Object1 foo:^{
NSLog(@"%@", self);
}];
}

@end
```

19 、局部对象的方法中的 Block 参数包含了 self 。

```objc
@interface Object1 : NSObject
- (void)foo:(void(^)())param;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[[Object1 new] foo:^{
NSLog(@"%@", self);
}];
}

@end
```
2765 次点击
所在节点    iDev
15 条回复
bytelee
2015-12-17 00:55:26 +08:00
还要分情况讨论 ARC MRC 这里边的表现也不同.
PS: 如果能贴上正确结论 就更好了
vincentxue
2015-12-17 08:55:33 +08:00
竟然没有愿意尝试的,出乎预料。
ianisme
2015-12-17 10:24:16 +08:00
一般情况下系统会出警告 is likely to lead to retain cycle
所以为了避免可能的循环引用以及系统出现的所谓 likely 警告,所有的都加上 weak block 一劳永逸
cheng4741
2015-12-17 11:00:31 +08:00
###先说结论(欢迎指正):
* 1 2 5 7 8 会循环引用
* 3 6 9 10 11 12 13 14 不会循环引用
* 4 15 16 17 18 看方法的实现

>循环引用就是两个对象强引用。区分的时候注意下面几点就行了

1. `block`也是对象,类也是特殊的对象
2. 引用成员变量(不管是`self.string`还是`_string`)本质上和引用了`self`是一个效果。
3. 方法内局部变量跟`self`没有引用关系
4. `block`作为方法的参数时,得看这个方法的实现有没有对这个`block`强引用,如果只是调用这个`block`那就不会产生引用
cheng4741
2015-12-17 11:01:00 +08:00
额。我以为支持 markdown 呢
xi_lin
2015-12-17 12:46:19 +08:00
@cheng4741 我的答案和你的一样哈
shanksxiao
2015-12-17 13:47:59 +08:00
造成循环引用的是: 1 、 2 、 7 、 8
不会循环引用的为: 3 、 6 、 9 、 10 、 11 、 12 、 13 、 14 、 15 、 16 、 17
具体看方法的实现中有无属性强引用 block 参数而定: 4 、 5 、 18 、 19
vincentxue
2015-12-21 23:30:43 +08:00
我贴个参考答案供大家拍砖。

一定会造成循环引用: 1, 2, 7, 8
一定不会造成循环引用: 3, 6, 9, 10, 11, 14, 17
视情况而定可能会造成循环引用: 4, 5, 12, 13, 15 16, 18, 19

视情况而定可能会造成循环引用,如果方法实现中有全局变量或静态变量引用了该 Block ,那么会造成循环引用。

一定不会造成循环引用,在某些情况下有可能会造成循环引用,例如第 10 题, 如果作为参数传递给 Category 方法, Category 里将此 block 用关联属性的方式添加给实例属性,这样就循环引用了。这在使用第三方库 BlockKit 的时候经常会遇到,同学们要小心。


具体判断的方法可以参考 @cheng4741 的总结。
shanksxiao
2015-12-24 09:38:40 +08:00
@vincentxue 12 、 13 、 15 、 16 、 17 你实际上已经实现了。。。
例如:+ (void)foo:(void(^)())param { } 的实现就是什么都不做,呵呵
vincentxue
2015-12-24 09:42:35 +08:00
@shanksxiao 是的
shanksxiao
2015-12-25 00:40:37 +08:00
@vincentxue 换句话说 12 、 13 、 15 、 16 是不会 retain cycle 的,而非视具体情况而定。
vincentxue
2015-12-25 09:10:28 +08:00
@shanksxiao 你试试用静态变量持有 block 会不会。
vincentxue
2015-12-25 09:12:18 +08:00
@shanksxiao 实例方法用一个全局变量持有就好。
vincentxue
2015-12-25 09:20:13 +08:00
@shanksxiao 如果你是说以目前题目的实现当然是没有循环引用的。所以我并没有说你的答案错了啊,还给你点了赞。
c447279704
2016-02-25 18:39:27 +08:00
6 也是有可能啊,谁知道里面写的啥。。。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/243974

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX