關于block捕獲外部的變量
(1)局部變量
(2)靜態(tài)變量
(3)全局變量
(4)全局靜態(tài)變量
(6)block捕獲外邊的變量在里面可以更改變量的值嗎?可更改是為什么,不可更改是為什么.
(5)block捕獲基本類型變量和捕獲對象類型變量的區(qū)別
關于block的類型
(1)堆block
(2)棧block
(3)全局block
(4)block在什么情況下分別是什么類型
關于block在MRC和在ARC注意事項
(1)block之后copy 分別在MRC, ARC會發(fā)生什么
(2)block會不會持有捕獲的對象變量
關于循環(huán)引用__weak
(1)__weak對象或者__strong對象,block是怎么對捕獲的對象進行內存管理的
關于__block
(1)__block修飾后的變量本質是什么
(2)在block內部為什么不能修改外邊的局部變量
我們先看看下面代碼根據(jù)xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m命令轉成的c++代碼
int globle_a = 1;
static int globlea_static_a = 2;
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autob = 1;
auto int autoa = 1;
static int statica = 1;
void(^lc_block)(int , NSObject *) = ^(int a, NSObject *obj){
//參數(shù)
NSLog(@"%d",a);
NSLog(@"%@",obj);
//捕獲的外部變量
NSLog(@"%d",autoa);
NSLog(@"%d",statica);
NSLog(@"%d",globle_a);
NSLog(@"%d",globlea_static_a);
};
lc_block(10,[NSObject new]);
}
return 0;
}
下面是main.cpp中關于block 的關鍵代碼
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
int globle_a = 1;
static int globlea_static_a = 2;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int autoa;
int *statica;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _autoa, int *_statica, int flags=0) : autoa(_autoa), statica(_statica) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, NSObject *obj) {
int autoa = __cself->autoa; // bound by copy
int *statica = __cself->statica; // bound by cop
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_0,a);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_1,obj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_2,autoa);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_3,(*statica));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_4,globle_a);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_5,globlea_static_a);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
auto int autob = 1;
auto int autoa = 1;
static int statica = 1;
void(*lc_block)(int , NSObject *) = ((void (*)(int, NSObject *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica));
((void (*)(__block_impl *, int, NSObject *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block, 10, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")));
}
return 0;
}
void(*lc_block)(int , NSObject *) = ((void (*)(int, NSObject *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica));
這行代碼
可以簡化成這樣 去掉一些強制烈性裝換
lc_block = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica)
__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica)是__main_block_impl_0C++結構體的構造函數(shù),可以理解成OC的初始化方法
根據(jù)上面的解釋我們可以看出lc_block其實就是指向__main_block_impl_0結構體的指針到這里我們其實已經可以看到lc_block是怎么捕獲外邊的變量了__main_block_impl_0構造函數(shù)的 第一個參數(shù)(void)__main_block_func_0 就是block的實現(xiàn)函數(shù)指針, 第二個參數(shù)&__main_block_desc_0_DATA暫時先不做解釋,第三,第四個參數(shù)autoa, &statica就是我們捕獲外邊的變量 ,說道這里我們至少明白了一個問題
(1)block是怎么捕獲外邊的變量的.
下面我們回答一下這兩個問題 block是在__main_block_impl_0初始化的時候把需要的捕獲的變量令作為參數(shù)傳進__main_block_impl_0結構體的
下面我們再來分析一下block的調用
((void (*)(__block_impl *, int, NSObject *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block, 10, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")))
去掉一些類型強制轉換其實可以變成下面這樣
lc_block->FuncPtr(lc_block, 10,[NSObject new]);
impl.FuncPtr = fp; FuncPtr就是我們在初始化結構體穿進去的第一個參數(shù)就是__main_block_func_0函數(shù)的指針,這里大家可能有個疑問按照正常的寫法不應該是lc_block->impl.FuncPtr(lc_block, 10,[NSObject new]);這樣才對嘛. 是的這樣是沒錯,但是直接lc_block->FuncPtr加上類型強制轉換也是沒有問題的原因大概解釋一下因為impl是結構體中第一個成員變量所以lc_block指針指向的其實是impl的地址.
lc_block->FuncPtr(lc_block, 10,[NSObject new]);就是調用了__main_block_func_0這個函數(shù)(再次強調一下這個函數(shù)是block的實現(xiàn))這個函數(shù)的第一個參數(shù)就是block的指針.第二個第三個參數(shù)是我們自己傳進去的.
看到這里我們可以基本可以回答兩個問題.
(1)block捕獲會捕獲外邊的什么變量
下面我們回答一下這兩個問題block里面用到的變量block才會捕獲但是block不會捕獲 全局變量, static靜態(tài)變量(可以??一下為啥block不會捕獲全局變量,全局靜態(tài)變量) 細心的朋友可能還發(fā)現(xiàn)了block捕獲局部變量autoa是值傳遞,捕獲局部靜態(tài)變量statica是指針傳遞.這也就說明為啥block中可以修改靜態(tài)變量不可以修改局部變量了(可以??一下為啥block不會捕獲局部變量為啥是值傳遞,static變量為啥是指針傳遞)
下面我們分析block的類型 我們先分析在MAC情況下
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
void(^lcc_block)(void) = ^(void){
//捕獲的外部變量
};
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:29:07.556789+0800 block_1[64578:2231805] __NSGlobalBlock__
int globle_a = 1;
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
void(^lcc_block)(void) = ^(void){
//捕獲的外部變量
NSLog(@"%d",globle_a);
};
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:29:07.556789+0800 block_1[64578:2231805] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = ^(void){
//捕獲的外部變量
NSLog(@"%d",statica);
};
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:32:02.108197+0800 block_1[64603:2235019] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = ^(void){
//捕獲的外部變量
NSLog(@"%d",autoa);
};
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:33:35.248612+0800 block_1[64630:2237066] __NSStackBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = [^(void){
//捕獲的外部變量
NSLog(@"%d",autoa);
} copy];
NSLog(@"%@",[lcc_block class]);
[lcc_block release];
}
return 0;
}
2020-01-04 21:34:23.187194+0800 block_1[64644:2238310] __NSMallocBlock__
我們總結一下結論
block類型 | 環(huán)境 |
---|---|
NSGlobalBlock | 沒有訪問auto變量 |
NSStackBlock | 訪問了auto變量 |
NSMallocBlock | NSStackBlock調用了copy |
我們再分析ARC的情況
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
void(^lcc_block)(void) = ^(void){
//捕獲的外部變量
};
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:42:00.940409+0800 block_1[64708:2245501] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
void(^lcc_block)(void) = ^(void){
//捕獲的外部變量
NSLog(@"%d",globle_a);
};
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:42:56.328330+0800 block_1[64721:2246791] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = ^(void){
//捕獲的外部變量
NSLog(@"%d",statica);
};
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:44:54.807266+0800 block_1[64745:2248664] __NSGlobalBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = ^(void){
//捕獲的外部變量
NSLog(@"%d",autoa);
};
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:45:31.803956+0800 block_1[64756:2249690] __NSMallocBlock__
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
static int statica = 1;
void(^lcc_block)(void) = [^(void){
//捕獲的外部變量
NSLog(@"%d",autoa);
} copy];
NSLog(@"%@",[lcc_block class]);
}
return 0;
}
2020-01-04 21:46:55.321199+0800 block_1[64767:2251090] __NSMallocBlock__
我們可以看出在MRC和在ARC的情況下不同的是block訪問auto局部變量是NSMallocBlock 難道是我們以上總結的結論是錯的嗎? 答案是不是.
看下面代碼
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int autoa = 1;
NSLog(@"%@",[^(void){NSLog(@"%d",autoa);} class]);
}
return 0;
}
2020-01-04 21:58:45.531138+0800 block_1[64814:2259295] __NSStackBlock__
在ARC的情況下這樣的block還是NSStackBlock和我們上表格總結的訪問了auto變量 是NSStackBlock并不矛盾
在ARC的情況下編譯器在什么情況下會自動 將NSStackBlock調用了copy轉成NSMallocBlock呢?
block作為函數(shù)返回值時
將block賦值給__strong指針時
block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時
block作為GCD API的方法參數(shù)時
不知道大家有沒有思考過這個問題編譯器為什么要把棧block copy一下變成堆block, 這樣的好處是啥?
帶著疑問我們看下面的代碼
//在MRC的情況下
void(^lc_blcok)(void);
void lc_test(){
auto int autoa = 1;
lc_blcok = ^(void){
NSLog(@"autoa = %d",autoa);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
lc_test();
lc_blcok();
}
return 0;
}
2020-01-04 22:15:13.271055+0800 block_1[64958:2273028] __NSStackBlock__
2020-01-04 22:15:13.271751+0800 block_1[64958:2273028] autoa = -27
我們發(fā)現(xiàn)打印的autoa這個變量是個奇怪的值原因是lc_blcok是NSStackBlock雖然我們用一個全局指針lc_blcok指向這個block(也可以說是結構體 因為我們通過前面的代碼分析出來block其實就是一個結構體的指針)但是這個結構體是在棧 上呢,在棧上的數(shù)據(jù)有個特點就是他的內存管理是系統(tǒng)控制的, lc_test函數(shù)以調用完畢內存就可呢釋放了 所以block捕獲進去的autoa也是垃圾內存了. 所以編譯器會根據(jù)情況自動將棧上的block復制到堆上.
說道這里還能引申一個問題就是在block作為一個類的屬性時到底是用strong 修飾還是用copy修飾呢
結論是
ARC的情況下 用strong和copy 都可以
MRC情況下建議用copy
原因相信聰明的你已經知道了
block捕獲對象類型是否持有auto對象
下面我們根據(jù) clang命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m再看看轉成c++的代碼 (我們考慮到有__weak的情況現(xiàn)在只討論arc)
int main(int argc, const char * argv[]) {
@autoreleasepool {
LCTest1 *testObj = [[LCTest1 alloc]init];
testObj.age = 10;
LCTest1 *testObj2 = [[LCTest1 alloc]init];
testObj2.age = 10;
__weak LCTest1 *weakTestObj = testObj2;
void(^lc_block)(void) = ^(void){
NSLog(@"%@",testObj);
NSLog(@"%@",weakTestObj);
};
lc_block();
}
return 0;
}
下面是轉成c++的關鍵代碼
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
LCTest1 *__strong testObj;
LCTest1 *__weak weakTestObj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj, LCTest1 *__weak _weakTestObj, int flags=0) : testObj(_testObj), weakTestObj(_weakTestObj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
LCTest1 *__strong testObj = __cself->testObj; // bound by copy
LCTest1 *__weak weakTestObj = __cself->weakTestObj; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_ba54bf_mi_0,testObj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_ba54bf_mi_1,weakTestObj);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj, (void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->weakTestObj, (void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
LCTest1 *testObj = ((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)testObj, sel_registerName("setAge:"), 10);
LCTest1 *testObj2 = ((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)testObj2, sel_registerName("setAge:"), 10);
__attribute__((objc_ownership(weak))) LCTest1 *weakTestObj = testObj2;
void(*lc_block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, testObj, weakTestObj, 570425344));
((void (*)(__block_impl *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block);
}
return 0;
}
我們可以看出block在捕獲外面auto變量和捕獲基本類型的變量是有所不同的.
我們看__main_block_desc_0這個結構體里面多了兩個函數(shù)指針變量
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
下面就是函數(shù)指針變量的實現(xiàn) __main_block_copy_0函數(shù)會在block從棧被拷貝到堆上自動調用,
__main_block_dispose_0函數(shù)會在堆上的block被銷毀的時候自動調用
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj, (void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->weakTestObj, (void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
我們看到 __main_block_impl_0結構體中捕獲的 testObj的變量和weakTestObj變量 分別用__strong, __weak修飾
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
LCTest1 *__strong testObj;
LCTest1 *__weak weakTestObj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj, LCTest1 *__weak _weakTestObj, int flags=0) : testObj(_testObj), weakTestObj(_weakTestObj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
看到這里我們可能已經能夠感覺到了block捕獲外面的auto對象變量會通過__main_block_copy_0里面的_Block_object_assign函數(shù)和__main_block_dispose_0里面的_Block_object_dispose函數(shù)對auto對象變量做內存方面的管理 那我們是不是人為 只要是__strong修飾的對象block就會持有(是對象的引用計數(shù)+1)這個對象__weak修飾的對象就不會持有這個對象呢? 這個結論不太準確 我們舉個例子
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
LCTest1 *testObj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *_testObj, int flags=0) : testObj(_testObj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
可以看出 __main_block_impl_0中的捕獲了testObj但是在61行testObj就被釋放了所以block中并內有持有testObj
我們再看一個arc下的例子
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^lc_block)(void) = nil;
testObj1 = nil;
autoa++;
LCTest1 *testObj1 = [[LCTest1 alloc]init];
testObj1.age = 10;
^(void){
NSLog(@"%@",testObj1);
};
printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(testObj1)));
}
return 0;
}
testObj retain count = 1可以看出block也沒有引用testObj
我們看下轉換過之后的 c++關鍵代碼 我們發(fā)現(xiàn)
雖然LCTest1 *__strong testObj1 是__strong但是testObj并沒有被block強持有
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
LCTest1 *__strong testObj1;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj1, int flags=0) : testObj1(_testObj1) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
經過上面的分析我們 我們看到__strong修飾的變量,block并不一定會強引用這個__strong變量..
下面給出總結
如果block是在棧上,將不會對auto變量產生強引用
如果block被拷貝到堆上會調用block內部的copy函數(shù)
copy函數(shù)內部會調用_Block_object_assign函數(shù)
_Block_object_assign函數(shù)會根據(jù)auto變量的修飾符(__strong、__weak砚殿、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用
下面我們再分析一下__block
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block LCTest1 *testObj1 = [[LCTest1 alloc]init];
__block int autoa = 1;
void(^lc_block)(void) = ^(void){
NSLog(@"%@",testObj1);
NSLog(@"%d",autoa);
};
lc_block();
printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(testObj1)));
}
return 0;
}
下面是轉換成c++的代碼
struct __Block_byref_testObj1_0 {
void *__isa;
__Block_byref_testObj1_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
LCTest1 *__strong testObj1;
};
struct __Block_byref_autoa_1 {
void *__isa;
__Block_byref_autoa_1 *__forwarding;
int __flags;
int __size;
int autoa;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_testObj1_0 *testObj1; // by ref
__Block_byref_autoa_1 *autoa; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_testObj1_0 *_testObj1, __Block_byref_autoa_1 *_autoa, int flags=0) : testObj1(_testObj1->__forwarding), autoa(_autoa->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_testObj1_0 *testObj1 = __cself->testObj1; // bound by ref
__Block_byref_autoa_1 *autoa = __cself->autoa; // bound by ref
(testObj1->__forwarding->testObj1) = __null;
(autoa->__forwarding->autoa)++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_d1c897_mi_0,(testObj1->__forwarding->testObj1));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_d1c897_mi_1,(autoa->__forwarding->autoa));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj1, (void*)src->testObj1, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->autoa, (void*)src->autoa, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj1, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->autoa, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_testObj1_0 testObj1 = {
(void*)0,
(__Block_byref_testObj1_0 *)&testObj1,
33554432,
sizeof(__Block_byref_testObj1_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"))
};
__attribute__((__blocks__(byref))) __Block_byref_autoa_1 autoa = {
(void*)0,
(__Block_byref_autoa_1 *)&autoa,
0,
sizeof(__Block_byref_autoa_1),
1
};
void(*lc_block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_testObj1_0 *)&testObj1, (__Block_byref_autoa_1 *)&autoa, 570425344));
((void (*)(__block_impl *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block);
printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)((testObj1.__forwarding->testObj1))));
}
return 0;
}
可以看出__block修飾的變量在最終會被包裝成一個結構體如果是__block修飾的是對象變量這個結構體中會有兩個函數(shù)指針__Block_byref_id_object_copy,和__Block_byref_id_object_dispose分別對對象變量做內存方面的管理,結構體里面有個重要的指針__forwarding是指想自己的指針.
我們從block的實現(xiàn)中__main_block_func_0看到__Block_byref_testObj1_0 *testObj1 = __cself->testObj1; // bound by ref
__Block_byref_autoa_1 *autoa = __cself->autoa; // bound by ref
testObj1和autoa分別是包裝成block的結構體的指針修改__block對象其實是通過
包裝成block的指針 拿到 __forwarding(包裝成block的指針)再拿到真正的變量然后修改
現(xiàn)在大家可能有一個疑問為啥不通過包裝成block的指針直接拿到真正的變量
(testObj1->__forwarding->testObj1) = __null;
(autoa->__forwarding->autoa)++;
我們根據(jù)上面的圖可以看出當block在棧上包裝__block修飾的變量的結構體中__forwarding是指向自己,當block通過copy拷貝到堆上包裝__block修飾的變量的結構體中__forwarding指向的就是堆上的, 猜測難道是我們再__main_block_func_0這個函數(shù)中使用的__Block_byref_testObj1_0 *testObj1和__Block_byref_autoa_1 *autoa 是棧上的? 如果是testObj1和autoa 是棧上的那么__forwarding指針就是上圖說的意義了. 如果是堆上的__forwarding指針貌似沒有存在的意義,因為完全可以通過下面這樣修改堆上的結果,沒必要通過__forwarding再轉一下
(堆上的)
testObj1-> testObj1
(堆上的)
autoa-> autoa
我們可以用代碼驗證一下
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __Block_byref_obj2_0 {
void *__isa;
struct __Block_byref_obj2_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj2;
};
struct __Block_byref_autoa_1 {
void *__isa;
struct __Block_byref_autoa_1 *__forwarding;
int __flags;
int __size;
int autoa;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
struct __Block_byref_obj2_0 *obj2; // by ref
struct __Block_byref_autoa_1 *autoa; // by ref
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 1;
NSObject *obj1 = [[NSObject alloc]init];
__block NSObject *obj2 = [[NSObject alloc]init];
__block int autoa = 10;
void(^lc_block)(void) = [^(void){
obj2 = nil;
autoa++;
} copy];
struct __main_block_impl_0 * Static_block = (__bridge struct __main_block_impl_0 *)((id)lc_block);
NSLog(@"lc_block class = %@",[lc_block class]);
lc_block();
}
return 0;
}
我們在lc_block();這一行代碼打上斷點
我們通過 po查看
2020-01-07 11:30:43.578077+0800 block_1[98305:3389230] lc_block class = __NSMallocBlock__
(lldb) po &a
0x00007ffeefbff54c
(lldb) po obj1
<NSObject: 0x1006758f0>
(lldb) po obj2
<NSObject: 0x100675930>
(lldb) po Static_block
<__NSMallocBlock__: 0x1006785b0>
signature: "v8@?0"
invoke : 0x100000da0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__main_block_invoke)
copy : 0x100000de0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__copy_helper_block_e8_32r40r)
dispose : 0x100000e40 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__destroy_helper_block_e8_32r40r)
(lldb) po Static_block->obj2
0x00000001006785e0
(lldb) po Static_block->obj2->__forwarding
0x00000001006785e0
(lldb) po Static_block->obj2->__forwarding->obj2
<NSObject: 0x100675930>
(lldb) po &autoa
0x0000000100678628
(lldb) po Static_block->autoa
0x0000000100678610
(lldb) po Static_block->autoa->__forwarding
0x0000000100678610
(lldb) po &(Static_block->autoa->__forwarding->autoa)
0x0000000100678628
(lldb)
我們分析一下
&a 0x00007ffeefbff54c 這個肯定是棧地址
po obj1 <NSObject: 0x1006758f0> 這個肯定是堆地址
po Static_block 0x1006785b0 這個地址和obj1接近也可以看到到block的類型 卻是是在堆
Static_block->obj2 0x00000001006785e0 這個地址也是堆
po &autoa 0x0000000100678628 我們看這個地址也是堆
可以看出 基本類型本來應該在棧上 經過__block修飾可以看出被分到了堆上
po Static_block->autoa 0x0000000100678610這個地址也是在堆上,可以看出我們直接用的就是堆上并不是棧上的 那么有必要 autoa-> __forwarding-> autoa 我想不通__forwarding存在的意義
==================================================
我們在分析一下棧上的block
#import <Foundation/Foundation.h>
#import "LCTest1.h"
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __Block_byref_obj2_0 {
void *__isa;
struct __Block_byref_obj2_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj2;
};
struct __Block_byref_autoa_1 {
void *__isa;
struct __Block_byref_autoa_1 *__forwarding;
int __flags;
int __size;
int autoa;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
struct __Block_byref_obj2_0 *obj2; // by ref
struct __Block_byref_autoa_1 *autoa; // by ref
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 1;
NSObject *obj1 = [[NSObject alloc]init];
__block NSObject *obj2 = [[NSObject alloc]init];
__block int autoa = 10;
void(^lc_block)(void) = ^(void){
obj2 = nil;
autoa++;
};
struct __main_block_impl_0 * Static_block = (__bridge struct __main_block_impl_0 *)((id)lc_block);
NSLog(@"lc_block class = %@",[lc_block class]);
lc_block();
}
return 0;
}
2020-01-07 11:05:15.894243+0800 block_1[98239:3381416] lc_block class = __NSStackBlock__
(lldb) po &a
0x00007ffeefbff54c
(lldb) po obj1
<NSObject: 0x1006355b0>
(lldb) po Static_block
<__NSStackBlock__: 0x7ffeefbff4b8>
signature: "v8@?0"
invoke : 0x100000da0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__main_block_invoke)
copy : 0x100000de0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__copy_helper_block_e8_32r40r)
dispose : 0x100000e40 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__destroy_helper_block_e8_32r40r)
(lldb) po Static_block->obj2
0x00007ffeefbff510
(lldb) po Static_block->obj2->__forwarding
0x00007ffeefbff510
(lldb) po Static_block->obj2->__forwarding->obj2
<NSObject: 0x100635920>
(lldb) po obj2
<NSObject: 0x100635920>
(lldb) po &autoa
0x00007ffeefbff508
(lldb) po Static_block->autoa
0x00007ffeefbff4f0
(lldb) po Static_block->autoa->__forwarding
0x00007ffeefbff4f0
(lldb) po &(Static_block->autoa->__forwarding->autoa)
0x00007ffeefbff508
po Static_block->obj2 0x00007ffeefbff510 是在棧上
po Static_block->obj2->__forwarding 0x00007ffeefbff510還是在棧上
po Static_block->autoa 0x00007ffeefbff4f0 是在棧上
po Static_block->autoa->__forwarding 0x00007ffeefbff4f0 還是在棧上
__forwarding也是沒有存在的意義啊
經過我的分析我還是沒有弄明白__forwarding存在的意義,哪位大佬了解拜托指教一二