從上些章節(jié)block-變量的捕獲(caputer)中,詳細(xì)說了基本類型的auto變量的捕獲,現(xiàn)在來了解下,對象類型的auto變量是怎樣捕獲和底層結(jié)構(gòu)是如何的。
block自動(dòng)copy的情況
在ARC環(huán)境下,編譯器會(huì)根據(jù)情況自動(dòng)將棧上的block復(fù)制到堆上,比如以下情況
-
block
作為函數(shù)返回值時(shí) -
block
賦值給__strong
指針時(shí) -
block
作為Cocoa API
中方法名含有usingBlock
的方法參數(shù)時(shí) -
block
作為GCD API
的方法參數(shù)時(shí)
#import <Foundation/Foundation.h>
#import "RMPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
RMPerson *person = [[RMPerson alloc] init];
person.age = 20;
void (^block)(void) = ^ {
NSLog(@"age is %d",person.age);
};
[person release];
NSLog(@"-----------");
}
return 0;
}
----------------- RMPerson.h -----------------
#import <Foundation/Foundation.h>
@interface RMPerson : NSObject
@property (nonatomic, assign) int age;
@end
----------------- RMPerson.m -----------------
#import "RMPerson.h"
@implementation RMPerson
- (void)dealloc {
[super delloc];
NSLog(@"RMPerson-delloc");
}
@end
// MRC 環(huán)境 控制臺(tái)輸出
2018-07-03 10:34:39.523223+0800 __block的本質(zhì)[20912:1898201] RMPerson-delloc
2018-07-03 10:36:17.021712+0800 __block的本質(zhì)[20912:1898201] -----------
Program ended with exit code: 0
留意上面代碼,是在MRC的環(huán)境下的代碼,當(dāng)NSLog(@"-----------");
打印前了,RMPerson就釋放了,什么原因呢?雖然block訪問的是對象類型的auto變量
,但還是訪問了auto變量,所以block是屬于NSStackBlock,是存在棧空間的,block運(yùn)行完就會(huì)釋放,它自己都不知道自己能存活多久,所以是不會(huì)作強(qiáng)持有RMPerson的操作。(結(jié)論一:如果block是在棧上,將不會(huì)對auto變量產(chǎn)生強(qiáng)引用)
#import <Foundation/Foundation.h>
#import "RMPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
RMPerson *person = [[RMPerson alloc] init];
person.age = 20;
void (^block)(void) = [^ {
NSLog(@"age is %d",person.age);
} copy]; // copy操作,從棧中復(fù)制到堆中
[person release];
NSLog(@"-----------");
}
return 0;
}
----------------- RMPerson.h -----------------
#import <Foundation/Foundation.h>
@interface RMPerson : NSObject
@property (nonatomic, assign) int age;
@end
----------------- RMPerson.m -----------------
#import "RMPerson.h"
@implementation RMPerson
- (void)dealloc {
[super delloc];
NSLog(@"RMPerson-delloc");
}
@end
// MRC 環(huán)境 控制臺(tái)輸出
2018-07-03 10:49:35.425698+0800 __block的本質(zhì)[21020:1916302] -----------
Program ended with exit code: 0
從上面的控制臺(tái)輸出可以看出,即使程序結(jié)束了,RMPerson都沒有釋放,這是為什么呢? 因?yàn)閎lock做了copy操作,從棧中拷貝到了堆中,此時(shí)block強(qiáng)引用了RMPerson,所以保住了RMPerson。
下面我們來看一下,底層c++代碼是如何堆中的block是如何保住RMPerson的。
對象類型的auto變量
從上面源碼分析,
如果block被拷貝到堆上時(shí)
- 1.會(huì)調(diào)用block內(nèi)部的copy函數(shù)
- 2.copy函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
- 3._Block_object_assign函數(shù)會(huì)根據(jù)auto變量的修飾符(__strong、__weak、__unsafe_unretained)做出相應(yīng)的操作,形成強(qiáng)引用(retain)或者弱引用。
如果block從堆上移除
- 1.會(huì)調(diào)用block內(nèi)部的dispose函數(shù)
- 2.dispose函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
- 3._Block_object_dispose函數(shù)會(huì)自動(dòng)釋放引用的auto變量(release)
block copy
總結(jié):
當(dāng)block內(nèi)部訪問了對象類型的auto變量時(shí)
1.如果block是在棧上,將不會(huì)對auto變量產(chǎn)生強(qiáng)引用
2.如果block被拷貝到堆上
- 會(huì)調(diào)用block內(nèi)部的copy函數(shù)
- copy函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
- _Block_object_assign函數(shù)會(huì)根據(jù)auto變量的修飾符(__strong、__weak、* __unsafe_unretained)做出相應(yīng)的操作,形成強(qiáng)引用(retain)或者弱引用
3.如果block從堆上移除
- 會(huì)調(diào)用block內(nèi)部的dispose函數(shù)
- dispose函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
- _Block_object_dispose函數(shù)會(huì)自動(dòng)釋放引用的auto變量(release)