块中引用的实例变量和临时变量有什么区别?
What's the difference between instance variable and temporary variable referenced in block?
如截图所示。
//
// ViewController.m
// Test
//
// Created by jam on 15/11/4.
// Copyright © 2015年 Baidu Inc. All rights reserved.
//
#import "ViewController.h"
#import "CJTestObject.h"
@interface ViewController ()
@property (strong, nonatomic) CJTestObject *testObject1;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_testObject1 = [[CJTestObject alloc] init];
CJTestObject *testObject2 = [[CJTestObject alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"_testObject1: %@", _testObject1);
NSLog(@"testObject2: %@", testObject2);
});
_testObject1 = nil;
testObject2 = nil;
}
@end
控制台输出
2015-11-20 18:07:28.855 Test[33585:417965] _testObject1: (null)
2015-11-20 18:07:28.857 Test[33585:417965] testObject2: <CJTestObject: 0x79f564f0>
我的问题是块中引用的这两种变量有什么不同。
在你的例子中,你有这 2 个对象类型的变量,所以你的块应该在你的例子中保留它们。
但这里的主要区别在于 testObject2
(在方法范围内声明的变量)将被保留,但是当您在块中捕获 _testObject1
时,它会保留 self
而不是一个 _testObject1
(在你的例子中)。
在块中捕获实例变量会导致保留 self
,因此当此块存在时,块将保留对 self
的强引用。
复制块时,它们引用的任何局部对象变量都会自动保留。当块被销毁时,它们会自动释放。这有助于确保引用保持有效。任何对 self
的引用都是对局部对象变量的引用,导致 self
被保留。对实例变量的任何引用都是对 self
的隐式引用并导致相同的事情。
您可以在这篇精彩的博客中找到更多详细信息
https://mikeash.com/pyblog/friday-qa-2009-08-14-practical-blocks.html
更新:
至于你的控制台输出,当块被执行时发生了什么:
NSLog(@"testObject1: %@", _testObject1);
这里它复制了对 self
的引用,并在执行时取消引用它的 _testObject1 实例变量 "pointer"。在 C 中它看起来像 self->_testObject1
。
您将对 _testObject1 的引用设置为 nil,2 秒后您的块将查看此 'updated' 引用。
NSLog(@"testObject2: %@", testObject2);
此处复制了您的块并保留了对 testObject2 的引用。当您设置 testObject2 = nil 时,您将变量的引用设置为 nil。但是块仍然有 'copied' 并保留变量的地址,因此它存储并打印该值。
看起来有点复杂,但我希望能理解我的回答:)
如截图所示。
//
// ViewController.m
// Test
//
// Created by jam on 15/11/4.
// Copyright © 2015年 Baidu Inc. All rights reserved.
//
#import "ViewController.h"
#import "CJTestObject.h"
@interface ViewController ()
@property (strong, nonatomic) CJTestObject *testObject1;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_testObject1 = [[CJTestObject alloc] init];
CJTestObject *testObject2 = [[CJTestObject alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"_testObject1: %@", _testObject1);
NSLog(@"testObject2: %@", testObject2);
});
_testObject1 = nil;
testObject2 = nil;
}
@end
控制台输出
2015-11-20 18:07:28.855 Test[33585:417965] _testObject1: (null)
2015-11-20 18:07:28.857 Test[33585:417965] testObject2: <CJTestObject: 0x79f564f0>
我的问题是块中引用的这两种变量有什么不同。
在你的例子中,你有这 2 个对象类型的变量,所以你的块应该在你的例子中保留它们。
但这里的主要区别在于 testObject2
(在方法范围内声明的变量)将被保留,但是当您在块中捕获 _testObject1
时,它会保留 self
而不是一个 _testObject1
(在你的例子中)。
在块中捕获实例变量会导致保留 self
,因此当此块存在时,块将保留对 self
的强引用。
复制块时,它们引用的任何局部对象变量都会自动保留。当块被销毁时,它们会自动释放。这有助于确保引用保持有效。任何对 self
的引用都是对局部对象变量的引用,导致 self
被保留。对实例变量的任何引用都是对 self
的隐式引用并导致相同的事情。
您可以在这篇精彩的博客中找到更多详细信息
https://mikeash.com/pyblog/friday-qa-2009-08-14-practical-blocks.html
更新: 至于你的控制台输出,当块被执行时发生了什么:
NSLog(@"testObject1: %@", _testObject1);
这里它复制了对 self
的引用,并在执行时取消引用它的 _testObject1 实例变量 "pointer"。在 C 中它看起来像 self->_testObject1
。
您将对 _testObject1 的引用设置为 nil,2 秒后您的块将查看此 'updated' 引用。
NSLog(@"testObject2: %@", testObject2);
此处复制了您的块并保留了对 testObject2 的引用。当您设置 testObject2 = nil 时,您将变量的引用设置为 nil。但是块仍然有 'copied' 并保留变量的地址,因此它存储并打印该值。
看起来有点复杂,但我希望能理解我的回答:)