为什么将弱引用传递给块会阻止保留对象?

时间:2015-01-08 19:31:02

标签: objective-c automatic-ref-counting objective-c-blocks reference-counting

我们都知道块会保留它们捕获的对象。我们也知道我们可以通过将对象的弱引用传递给块来避免这种情况。但为什么它这样工作?保留对象意味着将其保留计数增加1。为什么通过弱引用会有所不同?由于弱或强,它仍将指向同一个对象,并且该对象的保留计数将逐块增加。我对吗?那么,如果我们将弱引用传递给块内的对象,为什么对象的保留计数不会增加?它是如何在里面工作的?

3 个答案:

答案 0 :(得分:3)

弱引用不会增加保留计数,弱引用只是指向对象的指针,如果对象不再存在,则weak属性设置为nil,ARC处理此问题。我不相信对象保留计数会因块内的弱引用而增加。

  

因为弱引用不会对它引用的实例保持强大的保持,所以可以在弱引用仍然引用它的情况下释放该实例。因此,当引用它的实例被解除分配时,ARC会自动设置对nil的弱引用。

文档链接:https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

至于有关ARC如何使用块的具体信息,我从苹果公司找到了这个,这对你的问题没什么帮助: https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html

但是,本段可能有助于理解块如何保留其局部变量:

  

块在其他语言中称为闭包,例如Python,Ruby和Lisp,因为它们在声明时封装了状态。块创建在其范围内引用的任何局部变量的const副本。

来自:http://www.raywenderlich.com/9438/how-to-use-blocks-in-ios-5-tutorial-part-2

答案 1 :(得分:2)

您可以将块视为具有每个捕获变量的“实例变量”的对象,该变量在创建块时使用相应捕获变量的值进行初始化。

在ARC中,块的“实例变量”与相应的捕获变量具有相同的所有权说明符。因此,如果捕获的对象指针类型的变量是__strong(默认值),则块的“实例变量”也是__strong,因此它保留了指向块生命周期的对象。如果捕获的对象指针类型变量为__strong,则块的“实例变量”也是__weak,因此它是对指向的对象的归零弱引用。

答案 2 :(得分:1)

您可能想了解闭包的工作原理。

以下面的代码为例,

var name = "NSNoName"
NSLog("Original name: %@ <%p>", name, name)

let takeName: (String) -> Void -> () = {
    name in
    return {
        NSLog("Name inside block: %@ <%p>", name, name)
    }
}

let  returnName = takeName(name)
name = "NSNoFame"
returnName()

NSLog("Changed name: %@ <%p>", name, name)

最初, name 变量的值为“NSNoName”。当我打印此名称时,我得到了结果,

Original name: NSNoName <0x7f8fb86004a0>

我有一个简单的闭包,它将字符串作为参数。我用相同的名称对象调用闭包,结果块创建了自己的对象副本。然后,我继续更改名称对象,现在,如果我调用块,打印名称,该块具有相同的原始值,并传递给它。但是,对象是不同的,这意味着块创建了具有相同值的新对象。

Name inside block: NSNoName <0x7f8fb8602510>

最后一个NSLog打印一个不同的值,因为它已经被更改并具有一些不同的值,

Changed name: NSNoFame <0x7f8fb8603ae0>

这就是你想告诉一个块,创建一个对象的弱引用的原因,这意味着如果原始对象不再存在, nil 在块内创建的引用对象

虽然使用Objective C,但它似乎有点不同,

@interface TestViewController ()

@property (nonatomic, strong) NSString *name;

@end

@implementation TestViewController


- (void)viewDidLoad
{
    [super viewDidLoad];

    self.name = @"NSNoName";

    NSLog(@"Original name: %@ <%p>", self.name, self.name);

    typedef void(^ReturnNameBlock)();

    ReturnNameBlock (^takeName)(NSString*) = ^ReturnNameBlock(NSString *name) {
        return ^{
            NSLog(@"Name inside block: %@ <%p>", name, name);
        };
    };

    ReturnNameBlock returnName = takeName(self.name);

    self.name = @"NSNoFame";

    returnName();

    NSLog(@"Changed name: %@ <%p>", self.name, self.name);

}

@end

我的日志显示如下,

Original name: NSNoName <0x103ae34c0>
Name inside block: NSNoName <0x103ae34c0>
Changed name: NSNoFame <0x103ae3520>

如果查看日志,该块拥有原始的self.name对象,因为它们具有相同的内存地址。虽然viewController不再拥有它,但是当我们更改self.name =“NSNoFame”时,该块仍然保留对象的相同实例。

swift和objective的区别在于,Objective C块保留传递给它的对象的原始实例,而swift closure则创建原始实例变量的副本。