最近在想,block 被 copy 之后內(nèi)存會有什么變化,block 里面使用的外部變量為什么不會被銷毀的問題,目前總結(jié)如下凝颇。
太長不看版
1.block 被 copy 的時(shí)候,block 會被從棧(stack)中移到堆(heap)中疹鳄,內(nèi)存地址 MRC 下變化
2.block 里面使用的外部變量(OC 對象)會被 retain 一次拧略,所有 block 執(zhí)行結(jié)束之前,外部變量不會被銷毀
3.block 里面使用的外部變量都是“只讀”的瘪弓,想要修改垫蛆,需要修改變量類型為“__block”
- (instancetype)init {
if (self = [super init]) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeContactAdd];
button.frame = CGRectMake(100, 100, 100, 100);
NSLog(@"打印的是 block 定義時(shí)候 block 外面 button 的地址 ***** %p", button);
NSLog(@"button retainCount %zd", button.retainCount); // retainCount = 1
void(^tempBlock)() = ^(void) {
//相當(dāng)于 button 在這里 retain
NSLog(@"這是定義 block 的地方 打印的是 block 里面 button 的地址 ***** %s -- %p", __func__, button);
NSLog(@"button retainCount %zd", button.retainCount); // retainCount = 1
NSLog(@"back");
};
NSLog(@"%@", tempBlock);
self.viewControllerTestBlock = tempBlock;
NSLog(@"button retainCount %zd", button.retainCount); // retainCount = 2
} // button retainCount = 1
return self;
}
block 被 copy 的時(shí)候,block 會被從棧(stack)中移到堆(heap)中腺怯,關(guān)于 block 從 stack 到 heap 地址變化如下(MRC)(但是 ARC 下是沒有變化的)
2016-03-24 02:03:05.680 BlockCircle_01[3722:428943] <NSStackBlock: 0x7fff52e65ac8>
2016-03-24 02:03:05.681 BlockCircle_01[3722:428943] <NSMallocBlock: 0x7f9110c4dd30>
block 中使用的 block 外部的局部變量袱饭,如果是 OC 對象,這個(gè) OC 對象是會 retain 一次的呛占,MRC 下可以打印出來這個(gè) OC 對象的 retainCount虑乖,關(guān)于 button 引用計(jì)數(shù)如下(MRC)
2016-03-24 01:37:52.567 BlockCircle_01[3337:406242] 定義 button 時(shí)候 retainCount 1
2016-03-24 01:37:52.568 BlockCircle_01[3337:406242] 定義 button 所在方法將要結(jié)束時(shí)候 retainCount 2
2016-03-24 01:37:55.635 BlockCircle_01[3337:406242] block 使用 button retainCount 1
但是,這些局部變量在 block 中只是 read 的晾虑,不可以修改這些局部變量的值疹味,如果想要修改,需要在外面把這些變量改成 __block 類型的帜篇。
文檔里面一些資料
Block objects are a C-level syntactic and runtime feature. They are similar to standard C functions, but in addition to executable code they may also contain variable bindings to automatic (stack) or managed (heap) memory. A block can therefore maintain a set of state (data) that it can use to impact behavior when executed.
You can use blocks to compose function expressions that can be passed to API, optionally stored, and used by multiple threads. Blocks are particularly useful as a callback because the block carries both the code to be executed on callback and the data needed during that execution.
Blocks are particularly useful as a callback because the block carries both the code to be executed on callback and the data needed during that execution.
Blocks 作為回調(diào)函數(shù)使用的時(shí)候會更有效糙捺,因?yàn)?block 不但存儲了函數(shù)部分,同時(shí)執(zhí)行時(shí)候需要的數(shù)據(jù)也被存儲了下來笙隙。
Can continue to share and modify state defined within the lexical scope (the stack frame) after the lexical scope (the stack frame) has been destroyed
詞法范圍內(nèi)定義的變量洪灯,如果在 block 中使用,當(dāng) block 被 copy 的時(shí)候竟痰,會被放到堆(heap)中签钩,在棧中局部變量銷毀的時(shí)候,還是可以使用這些變量
Typically, you shouldn't need to copy (or retain) a block. You only need to make a copy when you expect the block to be used after destruction of the scope within which it was declared. Copying moves a block to the heap.
一般情況下凯亮,block 是不需要被 copy 的边臼,除非你希望在這個(gè) block 聲明的詞法范圍被銷毀之后哄尔,仍舊可以調(diào)用這個(gè) block假消,copy 會把 block 移到堆(heap)中。
還有一個(gè)問題岭接,文檔在說 OC 對象和 block 關(guān)系的時(shí)候富拗,說是“通過引用”和“通過值”臼予,是不一樣的,如果“通過引用”使用 OC 對象啃沪,就會對 self 有一個(gè)強(qiáng)引用粘拾,這樣是不是就有可能造成循環(huán)引用,但是创千,程序運(yùn)行是沒有問題的缰雇,代碼放在最下面了。
Objective-C Objects
When a block is copied, it creates strong references to object variables used within the block. If you use a block within the implementation of a method:
If you access an instance variable by reference, a strong reference is made to self;
If you access an instance variable by value, a strong reference is made to the variable.
The following examples illustrate the two different situations:
dispatch_async(queue, ^{
// instanceVariable is used by reference, a strong reference is made to self
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
/*
localVariable is used by value, a strong reference is made to localVariable
(and not to self).
*/
doSomethingWithObject(localVariable);
});
To override this behavior for a particular object variable, you can mark it with the __block storage type modifier.
- (IBAction)btnDidClick {
__block SXView *smallView = [[SXView alloc] initWithFrame:CGRectMake(([UIScreen mainScreen].bounds.size.width - 200) * 0.5, ([UIScreen mainScreen].bounds.size.height - 200) * 0.5, 200, 200)];
smallView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:smallView];
NSLog(@"smallView ----- %p", smallView);
__weak typeof(self) weakSelf = self;
self.sxViewControllerTestBlock = ^(void) {
weakSelf.view.backgroundColor = [UIColor brownColor];
smallView.backgroundColor = [UIColor purpleColor];
NSLog(@"smallView inside ----- %p", smallView);
};
ViewController *nextViewcontroller = [[ViewController alloc] init];
nextViewcontroller.viewControllerTestBlock = self.sxViewControllerTestBlock;
// presentViewController works like modal(from bottom)
// [self presentViewController:nextViewcontroller animated:YES completion:nil];
[self.navigationController pushViewController:nextViewcontroller animated:YES];
}
- (void)dealloc {
NSLog(@"***** %@ dealloc *****", [self class]);
}
對 smallView 引用追驴,但是械哟,控制器 pop 之后,還是正常銷毀了殿雪,沒有發(fā)生內(nèi)存泄露問題暇咆。