我们都知道块会保留它们捕获的对象。我们也知道我们可以通过将对象的弱引用传递给块来避免这种情况。但为什么它这样工作?保留对象意味着将其保留计数增加1。为什么通过弱引用会有所不同?由于弱或强,它仍将指向同一个对象,并且该对象的保留计数将逐块增加。我对吗?那么,如果我们将弱引用传递给块内的对象,为什么对象的保留计数不会增加?它是如何在里面工作的?
答案 0 :(得分:3)
弱引用不会增加保留计数,弱引用只是指向对象的指针,如果对象不再存在,则weak属性设置为nil,ARC处理此问题。我不相信对象保留计数会因块内的弱引用而增加。
因为弱引用不会对它引用的实例保持强大的保持,所以可以在弱引用仍然引用它的情况下释放该实例。因此,当引用它的实例被解除分配时,ARC会自动设置对nil的弱引用。
至于有关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则创建原始实例变量的副本。