[toc]
參考
http://www.reibang.com/p/a11337dbc8fd // 存儲(chǔ)域
存儲(chǔ)域 - 3種類型
(類型和存儲(chǔ)域是一一對(duì)應(yīng)的)
block.class |
__block_impl.isa |
特征 | 存儲(chǔ)域 | 生命周期 | copy效果 | 持有對(duì)象 |
---|---|---|---|---|---|---|
__NSGlobalBlock__ |
_NSConcreteGlobalBlock |
沒(méi)有訪問(wèn)auto變量(局部變量) | 全局 .data | 從創(chuàng)建到應(yīng)用程序結(jié)束 | 什么也不做 | 否 |
__NSStackBlock__ |
_NSConcreteStackBlock |
訪問(wèn)了auto變量 | 棧 | 出棧時(shí)(出函數(shù)作用域)會(huì)被銷毀 | 從棧復(fù)制到堆 | 否 |
__NSMallocBlock__ |
_NSConcreteMallocBlock |
__NSStackBlock__ 調(diào)用了copy |
堆 | 當(dāng)引用計(jì)數(shù)為0時(shí)會(huì)被銷毀 | 引用計(jì)數(shù)增加 | 是 |
可以通過(guò)打印出 block 對(duì)象來(lái)確定它存儲(chǔ)的位置, 這3種類型的 block 對(duì)象打印出來(lái)的類分別是:
__NSGlobalBlock__
、__NSStackBlock__
雏吭、__NSMallocBlock__
通過(guò)調(diào)用 class 和 superclass 方法, 可查看 block 具體類型:
// __NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject
void (^block)(void) = ^{
NSLog(@"Hello");
};
NSLog(@"%@", [block class]); // __NSGlobalBlock__
NSLog(@"%@", [block class].superclass); // __NSGlobalBlock
NSLog(@"%@", [block class].superclass.superclass); // NSBlock
NSLog(@"%@", [block class].superclass.superclass.superclass); // NSObject
可見(jiàn): block 最終都是繼承自 NSBlock 類型, 而 NSBlock 繼承于 NSObjcet锁施。
那么 block 其中的 isa 指針其實(shí)是來(lái)自NSObject中的, 這也更加印證了block 的本質(zhì)就是OC對(duì)象。
__NSGlobalBlock__
未訪問(wèn)任何變量的 全局block
// 定義在全局的block
void (^globalBlk)(void) = ^{
printf("全局的block");
};
// 在viewDidLoad 中打印
NSLog(@"全局block變量-本身 %@", globalBlk);
NSLog(@"全局block變量-拷貝 %@", [globalBlk copy]);
// ARC/MRC下驗(yàn)證上述代碼, 均輸出如下:
全局block變量-本身 <__NSGlobalBlock__: 0x100018090>
全局block變量-拷貝 <__NSGlobalBlock__: 0x100018090>
結(jié)論:
普通全局block, 不論是ARC/MRC, 不論是否拷貝, block對(duì)象都是
__NSGlobalBlock__
類, 存儲(chǔ)區(qū)域都是.data區(qū)
當(dāng)我們把Block作為全局變量使用時(shí), 對(duì)應(yīng)生成的Block將被設(shè)為 _NSConcreteGlobalBlock
, 如:
void (^block)(void) = ^{ NSLog(@"This is a Global Block"); };
int main(int argc, const char * argv[]) {
@autoreleasepool {
block();
}
return 0;
}
該代碼轉(zhuǎn)換后的代碼中, Block結(jié)構(gòu)體的成員變量isa的初始化為: impl.isa = &_NSConcreteGlobalBlock
;
訪問(wèn)全局變量的 全局block
(全局block只能訪問(wèn)全局變量)
// 全局變量
int globalNum = 1;
// 定義在全局的block
void (^globalBlk)(void) = ^{
globalNum = 2;
};
// 在viewDidLoad 中打印
NSLog(@"全局block變量-本身 %@", globalBlk);
NSLog(@"全局block變量-拷貝 %@", [globalBlk copy]); // 未拷貝出新對(duì)象
// ARC/MRC下驗(yàn)證上述代碼, 均輸出如下:
全局block變量-本身 <__NSGlobalBlock__: 0x100098088>
全局block變量-拷貝 <__NSGlobalBlock__: 0x100098088>
結(jié)論:
訪問(wèn)了全局變量的全局 block, 和普通全局 block 性質(zhì)完全一樣
全局 block, 不論是ARC/MRC, 不論是否拷貝, 不論是否訪問(wèn)全局變量, block 對(duì)象都是
__NSGlobalBlock__
類, 存儲(chǔ)區(qū)域都是.data區(qū)
未訪問(wèn)任何變量的 局部block
- (void)viewDidLoad {
[super viewDidLoad];
void (^localBlk)(void) = ^{
printf("局部的");
};
NSLog(@"局部block變量-本身 %@", localBlk);
NSLog(@"局部block變量-拷貝 %@", [localBlk copy]); // 未拷貝出新對(duì)象
NSLog(@"局部block對(duì)象-本身 %@", ^{
printf("局部的");
});
NSLog(@"局部block對(duì)象-拷貝 %@", [^{
printf("局部的");
} copy]); // 拷貝出了新對(duì)象
}
// ARC/MRC下驗(yàn)證上述代碼, 均輸出如下:
局部block變量-本身 <__NSGlobalBlock__: 0x1000a4098>
局部block變量-拷貝 <__NSGlobalBlock__: 0x1000a4098>
局部block對(duì)象-本身 <__NSGlobalBlock__: 0x1000a40d8>
局部block對(duì)象-本身 <__NSGlobalBlock__: 0x1000a4118>
結(jié)論:
未訪問(wèn)變量的局部block, 不論是ARC/MRC, 不論是否拷貝, block對(duì)象都是
__NSGlobalBlock__
類, 存儲(chǔ)區(qū)域都是.data區(qū)
訪問(wèn)(靜態(tài))全局變量的 局部block
// 全局變量 (考慮加static的情況)
int globalNum = 1;
- (void)viewDidLoad {
[super viewDidLoad];
void (^localBlk)(void) = ^{
globalNum = 2;
};
NSLog(@"局部block變量-本身 %@", localBlk);
NSLog(@"局部block變量-拷貝 %@", [localBlk copy]);
NSLog(@"局部block對(duì)象-本身 %@", ^{
globalNum = 3;
});
NSLog(@"局部block對(duì)象-拷貝 %@", [^{
globalNum = 4;
} copy]); // 拷貝出了新對(duì)象
}
// ARC/MRC 普通/靜態(tài)全局變量, 驗(yàn)證上述代碼, 均輸出如下:
局部block變量-本身 <__NSGlobalBlock__: 0x100034090>
局部block變量-拷貝 <__NSGlobalBlock__: 0x100034090>
局部block對(duì)象-本身 <__NSGlobalBlock__: 0x1000340d0>
局部block對(duì)象-拷貝 <__NSGlobalBlock__: 0x100034110>
結(jié)論:
局部 block 訪問(wèn)全局變量, 不論是 ARC/MRC , 不論是否拷貝 , block 對(duì)象都是
__NSGlobalBlock__
類, 存儲(chǔ)區(qū)域都是.data區(qū)
捕獲靜態(tài)局部變量的 局部block
- (void)viewDidLoad {
[super viewDidLoad];
static int localNum = 1;
void (^localBlk)(void) = ^{
localNum = 2;
};
NSLog(@"局部block變量-本身 %@", localBlk);
NSLog(@"局部block變量-拷貝 %@", [localBlk copy]);
NSLog(@"局部block對(duì)象-本身 %@", ^{
localNum = 3;
});
NSLog(@"局部block對(duì)象-拷貝 %@", [^{
localNum = 4;
} copy]); // 拷貝出了新對(duì)象
}
// ARC/MRC下 驗(yàn)證上述代碼, 均輸出如下:
局部block變量-本身 <__NSGlobalBlock__: 0x1000d4070>
局部block變量-拷貝 <__NSGlobalBlock__: 0x1000d4070>
局部block對(duì)象-本身 <__NSGlobalBlock__: 0x1000d40b0>
局部block對(duì)象-拷貝 <__NSGlobalBlock__: 0x1000d40f0>
結(jié)論:
局部block訪問(wèn)靜態(tài)局部變量, 不論是ARC/MRC, 不論是否拷貝, block對(duì)象都是
__NSGlobalBlock__
類, 存儲(chǔ)區(qū)域都是.data區(qū)
__NSStackBlock__
/ __NSMallocBlock__
捕獲普通局部變量的 局部block (F) ★★
- (void)viewDidLoad {
[super viewDidLoad];
int localNum = 1;
void (^localBlk)(void) = ^{
printf("%d", localNum);
};
NSLog(@"局部block變量-本身 %@", localBlk); // ARC在堆上, MRC在棧上
NSLog(@"局部block變量-拷貝 %@", [localBlk copy]); // ARC/MRC 經(jīng)過(guò)copy, 都在堆上
NSLog(@"局部block對(duì)象-本身 %@", ^{
printf("%d", localNum);
}); // ARC/MRC都在棧上 (該block未被強(qiáng)指針引用)
NSLog(@"局部block對(duì)象-拷貝 %@", [^{
printf("%d", localNum);
} copy]); // ARC/MRC都在堆上
}
// ARC下驗(yàn)證上述代碼, 輸出如下:
局部block變量-本身 <__NSMallocBlock__: 0x174240ae0>
局部block變量-拷貝 <__NSMallocBlock__: 0x174240ae0>
局部block對(duì)象-本身 <__NSStackBlock__: 0x16fd69e90>
局部block對(duì)象-拷貝 <__NSMallocBlock__: 0x174240990>
// MRC下驗(yàn)證上述代碼, 輸出如下:
局部block變量-本身 <__NSStackBlock__: 0x16fd05eb8>
局部block變量-拷貝 <__NSMallocBlock__: 0x170245520>
局部block對(duì)象-本身 <__NSStackBlock__: 0x16fd05e90>
局部block對(duì)象-拷貝 <__NSMallocBlock__: 0x170245580>
結(jié)論:
F0) 捕獲局部變量的 Block, 不論是ARC/MRC, 不論是否被強(qiáng)指針引用, 只要主動(dòng)調(diào)用copy方法, 都會(huì)被復(fù)制到堆上;
F1) MRC下:
截獲局部變量的Block對(duì)象本身是存儲(chǔ)在棧上的, 即使通過(guò)持有這個(gè)Block對(duì)象的變量來(lái)訪問(wèn)它, 仍然能順利訪問(wèn)到存儲(chǔ)在棧上的Block對(duì)象思恐。捕獲局部變量的 Block 不管是否被強(qiáng)指針引用, 都存儲(chǔ)在棧上;
捕獲局部變量的 Block 只有主動(dòng)調(diào)用copy方法, 才能將Block對(duì)象從棧上復(fù)制到堆上;
F2) ARC下:
截獲局部變量的Block對(duì)象本身也是存儲(chǔ)在棧上的, 只是當(dāng)使用持有這個(gè)Block對(duì)象的變量來(lái)訪問(wèn)它時(shí), 這個(gè)Block對(duì)象就會(huì)被從棧上復(fù)制到堆上, 這樣變量訪問(wèn)到的就是存儲(chǔ)在堆上的Block對(duì)象了沾谜。捕獲局部變量的 Block 未被強(qiáng)指針引用, 存儲(chǔ)在棧上;
捕獲局部變量的 Block 被強(qiáng)指針引用 (Block被賦值給__strong指針或者id類型), 會(huì)被從棧上復(fù)制到堆上;
F3) ARC下, 只有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 類型的block。比較ARC/MRC下的區(qū)別, 可以發(fā)現(xiàn): 使用變量訪問(wèn)Block對(duì)象時(shí), 在不同編譯模式下Block對(duì)象的存儲(chǔ)區(qū)域不同, MRC下能訪問(wèn)到棧上的對(duì)象, ARC下訪問(wèn)到的是被復(fù)制到堆上的對(duì)象, 所以才會(huì)有 "<u>ARC下沒(méi)有 _NSConcreteStackBlock</u>" 這種說(shuō)法胀莹。
捕獲__block局部變量的 局部block (G)
- (void)viewDidLoad {
[super viewDidLoad];
__block int localNum = 1;
void (^localBlk)(void) = ^{
localNum = 2;
};
NSLog(@"局部block變量-本身 %@", localBlk); // ARC在堆上, MRC在棧上
NSLog(@"局部block變量-拷貝 %@", [localBlk copy]); // ARC/MRC 經(jīng)過(guò)copy, 都在堆上
NSLog(@"局部block對(duì)象-本身 %@", ^{
localNum = 3;
}); // ARC/MRC都在棧上
NSLog(@"局部block對(duì)象-拷貝 %@", [^{
localNum = 4;
} copy]); // ARC/MRC都在堆上
}
// ARC下驗(yàn)證上述代碼, 輸出如下:
局部block變量-本身 <__NSMallocBlock__: 0x17405dfa0>
局部block變量-拷貝 <__NSMallocBlock__: 0x17405dfa0>
局部block對(duì)象-本身 <__NSStackBlock__: 0x16fdf5e58>
局部block對(duì)象-拷貝 <__NSMallocBlock__: 0x170240540>
// MRC下驗(yàn)證上述代碼, 輸出如下:
局部block變量-本身 <__NSStackBlock__: 0x16fd91e90>
局部block變量-拷貝 <__NSMallocBlock__: 0x17004b370>
局部block對(duì)象-本身 <__NSStackBlock__: 0x16fd91e58>
局部block對(duì)象-拷貝 <__NSMallocBlock__: 0x17004b220>
結(jié)論:
對(duì)比F和G, 可以發(fā)現(xiàn)
__block
不會(huì)影響block的存儲(chǔ)域基跑。
Block聲明在全局, 定義在局部, 訪問(wèn)局部變量
// 聲明在全局
void (^block)(void);
- (void)viewDidLoad {
[super viewDidLoad];
int age = 10;
// 定義在局部
block = ^{
NSLog(@"age is %d", age);
};
NSLog(@"block: %@", block);
block();
}
// MRC 輸出:
block: <__NSStackBlock__: 0x7ffeefbff3e8>
// ARC 輸出:
block: <__NSMallocBlock__: 0x103a04ee0>
結(jié)論:
Block 定義在局部, 行為與局部block一致。
存儲(chǔ)域 - 類型結(jié)論 ★★
① 沒(méi)有訪問(wèn)局部變量的 block 就是 __NSGlobalBlock__
未訪問(wèn)任何外部變量描焰、或僅訪問(wèn)了 (靜態(tài))全局變量
/ 靜態(tài)局部變量
的block, 不論該 block 定義在 全局 / 局部
, 不論是 MRC / ARC
, 不論是否被拷貝
, 該block對(duì)象都是 __NSGlobalBlock__
類, <u>存儲(chǔ)在 .data區(qū)
, 生命周期從創(chuàng)建到應(yīng)用程序結(jié)束</u>媳否。
② 也就是說(shuō), 只有訪問(wèn)了普通局部變量的block , 存儲(chǔ)域才會(huì)改為 堆/棧
; 該block必然是局部block。
③ 被強(qiáng)指針引用的 block 訪問(wèn)了局部變量:
- 如果未手動(dòng)copy, ARC 在堆上(自動(dòng)copy了), MRC在棧上;
- 經(jīng)過(guò)copy,
ARC/MRC
都在堆上荆秦。
④ 未被強(qiáng)指針引用的 block 訪問(wèn)了局部變量:
- 如果未手動(dòng)copy,
ARC/MRC
都在棧上; - 經(jīng)過(guò)copy, ARC/MRC 都在堆上篱竭。(案例F)
⑤ __block
不會(huì)影響 block 的存儲(chǔ)域。
存儲(chǔ)域 - 對(duì)持有對(duì)象的影響
只有堆上的block會(huì)持有對(duì)象 (產(chǎn)生強(qiáng)引用, 使retainCount +1)
__NSGlobalBlock__
不持有對(duì)象
// 在MRC下執(zhí)行
static NSObject *obj;
- (void)viewDidLoad {
[super viewDidLoad];
obj = [[NSObject alloc] init];
NSLog(@"block定義前: obj引用 = %lu", (unsigned long)obj.retainCount);
void (^localBlk)(void) = ^{
NSLog(@"block內(nèi)部一: obj引用 = %lu", (unsigned long)obj.retainCount);
};
NSLog(@"block定義后: obj引用 = %lu, 局部block變量-本身 %@", (unsigned long)obj.retainCount, localBlk);
localBlk();
NSLog(@"block執(zhí)行后: obj引用 = %lu, 局部block變量-本身 %@", (unsigned long)obj.retainCount, localBlk);
}
輸出:
block定義前: obj引用 = 1
block定義后: obj引用 = 1, 局部block變量-本身 <__NSGlobalBlock__: 0x10005c070>
block內(nèi)部一: obj引用 = 1
block執(zhí)行后: obj引用 = 1, 局部block變量-本身 <__NSGlobalBlock__: 0x10005c070>
__NSStackBlock__
不持有對(duì)象
// 在MRC下執(zhí)行 (ARC下被強(qiáng)指針引用的block會(huì)直接copy到堆)
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *obj = [[NSObject alloc] init];
NSLog(@"block定義前: obj引用 = %lu", (unsigned long)obj.retainCount);
void (^localBlk)(void) = ^{
NSLog(@"block內(nèi)部一: obj引用 = %lu", (unsigned long)obj.retainCount);
};
NSLog(@"block定義后: obj引用 = %lu, 局部block變量-本身 %@", (unsigned long)obj.retainCount, localBlk);
localBlk();
NSLog(@"block執(zhí)行后: obj引用 = %lu, 局部block變量-本身 %@", (unsigned long)obj.retainCount, localBlk);
// [localBlk release];
}
// 輸出:
block定義前: obj引用 = 1
block定義后: obj引用 = 1, 局部block變量-本身 <__NSStackBlock__: 0x16fdc1eb8>
block內(nèi)部一: obj引用 = 1
block執(zhí)行后: obj引用 = 1, 局部block變量-本身 <__NSStackBlock__: 0x16fdc1eb8>
__NSMallocBlock__
持有對(duì)象
// 在MRC下執(zhí)行
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *obj = [[NSObject alloc] init];
NSLog(@"block定義前: obj引用 = %lu", (unsigned long)obj.retainCount);
void (^localBlk)(void) = [^{
NSLog(@"block內(nèi)部一: obj引用 = %lu", (unsigned long)obj.retainCount); // 此時(shí)obj指針copy到堆, 但和包外的obj共同指向堆中的同一塊內(nèi)存地址, 導(dǎo)致其引用計(jì)數(shù)+1 <參考2.3C>
} copy];
NSLog(@"block定義后: obj引用 = %lu, 局部block變量-本身 %@", (unsigned long)obj.retainCount, localBlk);
localBlk();
NSLog(@"block執(zhí)行后: obj引用 = %lu, 局部block變量-本身 %@", (unsigned long)obj.retainCount, localBlk);
[localBlk release]; // 如果不銷毀, obj.retainCount 還是 2
NSLog(@"block銷毀后: obj引用 = %lu", (unsigned long)obj.retainCount);
}
block定義前: obj引用 = 1
block定義后: obj引用 = 2, 局部block變量-本身 <__NSMallocBlock__: 0x17405a970>
block內(nèi)部一: obj引用 = 2
block執(zhí)行后: obj引用 = 2, 局部block變量-本身 <__NSMallocBlock__: 0x17405a970>
block銷毀后: obj引用 = 1
自動(dòng)copy到堆 ★
ARC下, 編譯器會(huì)根據(jù)以下情況自動(dòng)將棧上的block 復(fù)制到堆上:
Block 作為函數(shù)的返回值;
由于_NSConcreteStackBlock
所屬的變量域一旦結(jié)束, 該Block就會(huì)被銷毀步绸。所以編譯器會(huì)自動(dòng)將返回的block進(jìn)行copy操作; 這樣即使 Block 的變量作用域結(jié)束, 堆上的 Block 還可以繼續(xù)存在掺逼。Block 被強(qiáng)引用, Block被賦值給
__strong
指針或者id類型 (有強(qiáng)指針引用block) ;調(diào)用 Cocoa API 入?yún)⒅泻?usingBlock 的方法;
block 作為 GCD API 的入?yún)?