V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
vincentxue
V2EX  ›  iDev

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

  •  
  •   vincentxue ·
    vincentsit · 2015-12-16 17:16:35 +08:00 · 2766 次点击
    这是一个创建于 3268 天前的主题,其中的信息可能已经有所发展或是发生改变。

    帮朋友理解 Block 和 循环引用,写了几个测试,欢迎大家也测一下。

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

    @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 的全局变量。

    @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 的局部变量。

    @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 。

    @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 的全局变量。

    @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 的局部变量。

    @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 。

    @interface ViewController ()
    @property (nonatomic, copy) void(^block)();
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.block = ^ {
            NSLog(@"%@", self);
        };
    }
    
    @end
    

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

    @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 的局部变量。

    @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 。

    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        void(^block)() = ^ {
            NSLog(@"%@", self);
        };
    }
    
    @end
    

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

    @interface ViewController ()
    @property (nonatomic, strong) NSString *string;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        void(^block)() = ^ {
            NSLog(@"%@", _string);
        };
    }
    
    @end
    

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

    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    - (void)foo:(void(^)())param {}
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self foo:^{
            NSLog(@"%@", self);
        }];
    }
    
    @end
    

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

    @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 的局部变量。

    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    - (void)foo:(void(^)())param {}
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        NSString *string = @"";
        [self foo:^{
            NSLog(@"%@", string);
        }];
    }
    
    @end
    

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

    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    + (void)foo:(void(^)())param {}
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [[self class] foo:^{
            NSLog(@"%@", self);
        }];
    }
    
    @end
    

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

    @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 的局部变量。

    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    + (void)foo:(void(^)())param {}
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        NSString *string = @"";
        [[self class] foo:^{
            NSLog(@"%@", string);
        }];
    }
    
    @end
    

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

    @interface Object1 : NSObject
    + (void)foo:(void(^)())param;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [Object1 foo:^{
            NSLog(@"%@", self);
        }];
    }
    
    @end
    

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

    @interface Object1 : NSObject
    - (void)foo:(void(^)())param;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [[Object1 new] foo:^{
            NSLog(@"%@", self);
        }];
    }
    
    @end
    
    15 条回复    2016-02-25 18:39:27 +08:00
    bytelee
        1
    bytelee  
       2015-12-17 00:55:26 +08:00
    还要分情况讨论 ARC MRC 这里边的表现也不同.
    PS: 如果能贴上正确结论 就更好了
    vincentxue
        2
    vincentxue  
    OP
       2015-12-17 08:55:33 +08:00 via iPhone
    竟然没有愿意尝试的,出乎预料。
    ianisme
        3
    ianisme  
       2015-12-17 10:24:16 +08:00
    一般情况下系统会出警告 is likely to lead to retain cycle
    所以为了避免可能的循环引用以及系统出现的所谓 likely 警告,所有的都加上 weak block 一劳永逸
    cheng4741
        4
    cheng4741  
       2015-12-17 11:00:31 +08:00   ❤️ 1
    ###先说结论(欢迎指正):
    * 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
        5
    cheng4741  
       2015-12-17 11:01:00 +08:00
    额。我以为支持 markdown 呢
    xi_lin
        6
    xi_lin  
       2015-12-17 12:46:19 +08:00
    @cheng4741 我的答案和你的一样哈
    shanksxiao
        7
    shanksxiao  
       2015-12-17 13:47:59 +08:00   ❤️ 1
    造成循环引用的是: 1 、 2 、 7 、 8
    不会循环引用的为: 3 、 6 、 9 、 10 、 11 、 12 、 13 、 14 、 15 、 16 、 17
    具体看方法的实现中有无属性强引用 block 参数而定: 4 、 5 、 18 、 19
    vincentxue
        8
    vincentxue  
    OP
       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
        9
    shanksxiao  
       2015-12-24 09:38:40 +08:00
    @vincentxue 12 、 13 、 15 、 16 、 17 你实际上已经实现了。。。
    例如:+ (void)foo:(void(^)())param { } 的实现就是什么都不做,呵呵
    vincentxue
        10
    vincentxue  
    OP
       2015-12-24 09:42:35 +08:00
    @shanksxiao 是的
    shanksxiao
        11
    shanksxiao  
       2015-12-25 00:40:37 +08:00
    @vincentxue 换句话说 12 、 13 、 15 、 16 是不会 retain cycle 的,而非视具体情况而定。
    vincentxue
        12
    vincentxue  
    OP
       2015-12-25 09:10:28 +08:00
    @shanksxiao 你试试用静态变量持有 block 会不会。
    vincentxue
        13
    vincentxue  
    OP
       2015-12-25 09:12:18 +08:00
    @shanksxiao 实例方法用一个全局变量持有就好。
    vincentxue
        14
    vincentxue  
    OP
       2015-12-25 09:20:13 +08:00
    @shanksxiao 如果你是说以目前题目的实现当然是没有循环引用的。所以我并没有说你的答案错了啊,还给你点了赞。
    c447279704
        15
    c447279704  
       2016-02-25 18:39:27 +08:00
    6 也是有可能啊,谁知道里面写的啥。。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3555 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 00:45 · PVG 08:45 · LAX 16:45 · JFK 19:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.