工具命令轉(zhuǎn)化C++
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
一. iOS代碼塊Block
1.1 概述
代碼塊Block是蘋果在iOS4開始引入的對C語言的擴(kuò)展,用來實現(xiàn)匿名函數(shù)的特性,Block是一種特殊的數(shù)據(jù)類型,其可以正常定義變量、作為參數(shù)、作為返回值,特殊地,Block還可以保存一段代碼,在需要的時候調(diào)用,目前Block已經(jīng)廣泛應(yīng)用于iOS開發(fā)中,常用于GCD、動畫、排序及各類回調(diào)
注: Block的聲明與賦值只是保存了一段代碼段,必須調(diào)用才能執(zhí)行內(nèi)部代碼
1.2 Block變量的聲明、賦值與調(diào)用
1.2.1 Block變量的聲明
Block變量的聲明格式為: 返回值類型(^Block名字)(參數(shù)列表);
// 聲明一個無返回值,參數(shù)為兩個字符串對象,叫做aBlock的Block
void(^aBlock)(NSString *x, NSString *y);
// 形參變量名稱可以省略,只留有變量類型即可
void(^aBlock)(NSString *, NSString *);
1.2.2 Block變量的賦值
Block變量的賦值格式為: Block變量 = ^(參數(shù)列表){函數(shù)體};
aBlock = ^(NSString *x, NSString *y){
NSLog(@"%@ love %@", x, y);
};
注: Block變量的賦值格式可以是: Block變量 = ^返回值類型(參數(shù)列表){函數(shù)體};,不過通常情況下都將返回值類型省略,
因為編譯器可以從存儲代碼塊的變量中確定返回值的類型
1.2.3 聲明Block變量的同時進(jìn)行賦值
int(^myBlock)(int) = ^(int num){
return num * 7;
};
// 如果沒有參數(shù)列表,在賦值時參數(shù)列表可以省略
void(^aVoidBlock)() = ^{
NSLog(@"I am a aVoidBlock");
};
1.2.4 Block變量的調(diào)用
// 調(diào)用后控制臺輸出"Li Lei love Han Meimei"
aBlock(@"Li Lei",@"Han Meimei");
// 調(diào)用后控制臺輸出"result = 63"
NSLog(@"result = %d", myBlock(9));
// 調(diào)用后控制臺輸出"I am a aVoidBlock"
aVoidBlock();
二. 使用typedef定義Block類型
在實際使用Block的過程中,我們可能需要重復(fù)地聲明多個相同返回值相同參數(shù)列表的Block變量,如果總是重復(fù)地編寫一長串代碼來聲明變量會非常繁瑣,所以我們可以使用typedef來定義Block類型
// 定義一種無返回值無參數(shù)列表的Block類型
typedef void(^SayHello)();
// 我們可以像OC中聲明變量一樣使用Block類型SayHello來聲明變量
SayHello hello = ^(){
NSLog(@"hello");
};
// 調(diào)用后控制臺輸出"hello"
hello();
三. Block作為函數(shù)參數(shù)
3.1 Block作為C函數(shù)參數(shù)
// 1.定義一個形參為Block的C函數(shù)
void useBlockForC(int(^aBlock)(int, int))
{
NSLog(@"result = %d", aBlock(300,200));
}
// 2.聲明并賦值定義一個Block變量
int(^addBlock)(int, int) = ^(int x, int y){
return x+y;
};
// 3.以Block作為函數(shù)參數(shù),把Block像對象一樣傳遞
useBlockForC(addBlock);
// 將第2點和第3點合并一起,以內(nèi)聯(lián)定義的Block作為函數(shù)參數(shù)
useBlockForC(^(int x, int y) {
return x+y;
});
3.2 Block作為OC函數(shù)參數(shù)
// 1.定義一個形參為Block的OC函數(shù)
- (void)useBlockForOC:(int(^)(int, int))aBlock
{
NSLog(@"result = %d", aBlock(300,200));
}
// 2.聲明并賦值定義一個Block變量
int(^addBlock)(int, int) = ^(int x, int y){
return x+y;
};
// 3.以Block作為函數(shù)參數(shù),把Block像對象一樣傳遞
[self useBlockForOC:addBlock];
// 將第2點和第3點合并一起,以內(nèi)聯(lián)定義的Block作為函數(shù)參數(shù)
[self useBlockForOC:^(int x, int y){
return x+y;
}];
3.3 使用typedef簡化Block
// 1.使用typedef定義Block類型
typedef int(^MyBlock)(int, int);
// 2.定義一個形參為Block的OC函數(shù)
- (void)useBlockForOC:(MyBlock)aBlock
{
NSLog(@"result = %d", aBlock(300,200));
}
// 3.聲明并賦值定義一個Block變量
MyBlock addBlock = ^(int x, int y){
return x+y;
};
// 4.以Block作為函數(shù)參數(shù),把Block像對象一樣傳遞
[self useBlockForOC:addBlock];
// 將第3點和第4點合并一起,以內(nèi)聯(lián)定義的Block作為函數(shù)參數(shù)
[self useBlockForOC:^(int x, int y){
return x+y;
}];
四 Block內(nèi)訪問局部變量
4.1 在Block中可以訪問局部變量
// 聲明局部變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺輸出"global = 100"
myBlock();
4.2 在聲明Block之后、調(diào)用Block之前對局部變量進(jìn)行修改,在調(diào)用Block時局部變量值是修改之前的舊值
// 聲明局部變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
global = 101;
// 調(diào)用后控制臺輸出"global = 100"
myBlock();
4.3 在Block中不可以直接修改局部變量
// 聲明局部變量global
int global = 100;
void(^myBlock)() = ^{
global ++; // 這句報錯
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺輸出"global = 100"
myBlock();
五. Block內(nèi)訪問__block修飾的局部變量
5.1 在局部變量前使用下劃線下劃線block修飾,在聲明Block之后、調(diào)用Block之前對局部變量進(jìn)行修改,在調(diào)用Block時局部變量值是修改之后的新值
// 聲明局部變量global
__block int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
global = 101;
// 調(diào)用后控制臺輸出"global = 101"
myBlock();
5.2 在局部變量前使用下劃線下劃線block修飾,在Block中可以直接修改局部變量
// 聲明局部變量global
__block int global = 100;
void(^myBlock)() = ^{
global ++; // 這句正確
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺輸出"global = 101"
myBlock();
六. Block內(nèi)訪問全局變量
6.1 在Block中可以訪問全局變量
// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺輸出"global = 100"
myBlock();
6.2 在聲明Block之后、調(diào)用Block之前對全局變量進(jìn)行修改,在調(diào)用Block時全局變量值是修改之后的新值
// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
global = 101;
// 調(diào)用后控制臺輸出"global = 101"
myBlock();
6.3 在Block中可以直接修改全局變量
// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
global ++;
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺輸出"global = 101"
myBlock();
七. Block內(nèi)訪問靜態(tài)變量
7.1 在Block中可以訪問靜態(tài)變量
// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺輸出"global = 100"
myBlock();
7.2 在聲明Block之后、調(diào)用Block之前對靜態(tài)變量進(jìn)行修改,在調(diào)用Block時靜態(tài)變量值是修改之后的新值
// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
global = 101;
// 調(diào)用后控制臺輸出"global = 101"
myBlock();
7.3 在Block中可以直接修改靜態(tài)變量
// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
global ++;
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺輸出"global = 101"
myBlock();
8. Block在MRC及ARC下的內(nèi)存管理
8.1 Block在MRC下的內(nèi)存管理
默認(rèn)情況下,Block的內(nèi)存存儲在棧中,不需要開發(fā)人員對其進(jìn)行內(nèi)存管理
// 放Block變量出了作用域,Block的內(nèi)存會被自動釋放
void(^myBlock)() = ^{
NSLog(@"------");
};
myBlock();
在Block的內(nèi)存存儲在棧中時,如果在Block中引用了外面的對象,不會對所引用的對象進(jìn)行任何操作
Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
[p release]; // Person對象在這里可以正常被釋放
如果對Block進(jìn)行一次copy操作,那么Block的內(nèi)存會被移動到堆中,這時需要開發(fā)人員對其進(jìn)行release操作來管理內(nèi)存
void(^myBlock)() = ^{
NSLog(@"------");
};
myBlock();
Block_copy(myBlock);
// do something ...
Block_release(myBlock);
如果對Block進(jìn)行一次copy操作,那么Block的內(nèi)存會被移動到堆中,在Block的內(nèi)存存儲在堆中時,如果在Block中引用了外面的對象,會對所引用的對象進(jìn)行一次retain操作,即使在Block自身調(diào)用了release操作之后,Block也不會對所引用的對象進(jìn)行一次release操作,這時會造成內(nèi)存泄漏
Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
Block_copy(myBlock);
// do something ...
Block_release(myBlock);
[p release]; // Person對象在這里無法正常被釋放,因為其在Block中被進(jìn)行了一次retain操作
如果對Block進(jìn)行一次copy操作,那么Block的內(nèi)存會被移動到堆中,在Block的內(nèi)存存儲在堆中時,如果在Block中引用了外面的對象,會對所引用的對象進(jìn)行一次retain操作,為了不對所引用的對象進(jìn)行一次retain操作,可以在對象的前面使用下劃線下劃線block來修飾
__block Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
Block_copy(myBlock);
// do something ...
Block_release(myBlock);
[p release]; // Person對象在這里可以正常被釋放
如果對象內(nèi)部有一個Block屬性,而在Block內(nèi)部又訪問了該對象,那么會造成循環(huán)引用
- 情況一
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
Block_release(_myBlock);
[super dealloc];
}
@end
Person *p = [[Person alloc] init];
p.myBlock = ^{
NSLog(@"------%@", p);
};
p.myBlock();
[p release]; // 因為myBlock作為Person的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統(tǒng)釋放),所以Block會對Person對象進(jìn)行一次retain操作,導(dǎo)致循環(huán)引用無法釋放
- 情況二
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
- (void)resetBlock;
@end
@implementation Person
- (void)resetBlock
{
self.myBlock = ^{
NSLog(@"------%@", self);
};
}
- (void)dealloc
{
NSLog(@"Person dealloc");
Block_release(_myBlock);
[super dealloc];
}
@end
Person *p = [[Person alloc] init];
[p resetBlock];
[p release]; // Person對象在這里無法正常釋放,雖然表面看起來一個alloc對應(yīng)一個release符合內(nèi)存管理規(guī)則,但是實際在resetBlock方法實現(xiàn)中,Block內(nèi)部對self進(jìn)行了一次retain操作,導(dǎo)致循環(huán)引用無法釋放
如果對象內(nèi)部有一個Block屬性,而在Block內(nèi)部又訪問了該對象,那么會造成循環(huán)引用,解決循環(huán)引用的辦法是在對象的前面使用下劃線下劃線block來修飾,以避免Block對對象進(jìn)行retain操作
情況一
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
Block_release(_myBlock);
[super dealloc];
}
@end
__block Person *p = [[Person alloc] init];
p.myBlock = ^{
NSLog(@"------%@", p);
};
p.myBlock();
[p release]; // Person對象在這里可以正常被釋放
情況二
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
- (void)resetBlock;
@end
@implementation Person
- (void)resetBlock
{
// 這里為了通用一點,可以使用__block typeof(self) p = self;
__block Person *p = self;
self.myBlock = ^{
NSLog(@"------%@", p);
};
}
- (void)dealloc
{
NSLog(@"Person dealloc");
Block_release(_myBlock);
[super dealloc];
}
@end
Person *p = [[Person alloc] init];
[p resetBlock];
[p release]; // Person對象在這里可以正常被釋放
八: Block的實質(zhì)
- 1: block本質(zhì)上也是一個OC對象,它內(nèi)部也有個isa指針
- 2: block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象
- 3: block是封裝函數(shù)及其上下文的OC對象

8.1 Block最簡單情形的分析
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^mainBlock)(void) = ^{
NSLog(@"mainBlock");
};
mainBlock();
}
return 0;
}
將這塊代碼轉(zhuǎn)為C++代碼 使用一下命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
我們可以找到
1: mainBlock將會轉(zhuǎn)化為如下結(jié)構(gòu)體
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
**2. mainBlock包含兩個結(jié)構(gòu)體元素__block_impl和__main_block_desc_0
// 結(jié)構(gòu)體 __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 結(jié)構(gòu)體 __main_block_desc_0
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)};
3. mainBlock結(jié)構(gòu)體的構(gòu)造方法會對兩個子結(jié)構(gòu)體進(jìn)行賦值
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
4. 在main.m的執(zhí)行過程中是
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void(*mainBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)mainBlock)->FuncPtr)((__block_impl *)mainBlock);
}
return 0;
}
轉(zhuǎn)化關(guān)系比較復(fù)雜 我們進(jìn)行簡化一下
void(*mainBlock)(void) = (&__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
(mainBlock->FuncPtr)(mainBlock);
8.2 帶參數(shù)Block的分析
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^mainBlock)(int, int) = ^(int a, int b){
NSLog(@"main--Block---%d--%d", a, b);
};
mainBlock(10, 20);
}
return 0;
}
將這塊代碼轉(zhuǎn)為C++代碼
1: 查看當(dāng)前的block結(jié)構(gòu)體結(jié)構(gòu)和不帶參數(shù)的一樣
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
**2. __main_block_impl_0包含兩個結(jié)構(gòu)體元素__block_impl和__main_block_desc_0
// 結(jié)構(gòu)體 __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 結(jié)構(gòu)體 __main_block_desc_0
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)};
3: block函數(shù)發(fā)生了變化 增加了兩個參數(shù)
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_wn_z9c5vw_x6h16tszlbnmscg_80000gn_T_main_6550f6_mi_0, a, b);
}
4: main方法的具體體現(xiàn)
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*mainBlock)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *, int, int))((__block_impl *)mainBlock)->FuncPtr)((__block_impl *)mainBlock, 10, 20);
}
return 0;
}
簡化后
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*mainBlock)(int, int) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
(mainBlock->FuncPtr)(mainBlock, 10, 20);
}
return 0;
}
8.3 引用Block外部的局部變量的分析
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
void (^mainBlock)(void) = ^{
NSLog(@"main--Block---%d", age);
};
mainBlock();
}
return 0;
}
將這塊代碼轉(zhuǎn)為C++代碼
1: 查看當(dāng)前的block結(jié)構(gòu)體結(jié)構(gòu)和以上兩種情況都不一樣
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
: age(_age) C++語法在構(gòu)造函數(shù)中會講_age賦值給結(jié)構(gòu)體元素中的age
**2. __main_block_impl_0包含三個結(jié)構(gòu)體元素__block_impl和__main_block_desc_0和age
// 結(jié)構(gòu)體 __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 結(jié)構(gòu)體 __main_block_desc_0
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)};
**3: block函數(shù)發(fā)生了變化 在函數(shù)中取出結(jié)構(gòu)體的數(shù)據(jù) 進(jìn)行初始化一個變量 **
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_wn_z9c5vw_x6h16tszlbnmscg_80000gn_T_main_09a856_mi_0, age);
}
4: main方法的具體體現(xiàn)
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*mainBlock)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *, int, int))((__block_impl *)mainBlock)->FuncPtr)((__block_impl *)mainBlock, 10, 20);
}
return 0;
}
簡化后
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*mainBlock)(int, int) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
(mainBlock->FuncPtr)(mainBlock, 10, 20);
}
return 0;
}
結(jié)論: 我們在初始化結(jié)構(gòu)體的時候 會講外部的age傳入結(jié)構(gòu)體的構(gòu)造函數(shù),結(jié)構(gòu)體有對應(yīng)的age作為對應(yīng), 會講外界的10賦值給結(jié)構(gòu)體的age, 當(dāng)我們執(zhí)行block的時候 取出結(jié)構(gòu)體的age數(shù)據(jù)10進(jìn)行操作
8.4 引用Block外部的static局部變量的分析
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
static int num = 20;
void (^mainBlock)(void) = ^{
NSLog(@"main--Block---%d--%d", age, num);
};
mainBlock();
}
return 0;
}
將這塊代碼轉(zhuǎn)為C++代碼
1: 查看當(dāng)前的block結(jié)構(gòu)體結(jié)構(gòu)和以上兩種情況都不一樣
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
int *num;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_num, int flags=0) : age(_age), num(_num) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
: age(_age) C++語法在構(gòu)造函數(shù)中會講_age賦值給結(jié)構(gòu)體元素中的age, 此時會講外部的num的地址賦值給結(jié)構(gòu)體元素
int *num。
**2. __main_block_impl_0包含三個結(jié)構(gòu)體元素__block_impl和__main_block_desc_0、age和num
// 結(jié)構(gòu)體 __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 結(jié)構(gòu)體 __main_block_desc_0
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)};
**3: block函數(shù)發(fā)生了變化 在函數(shù)中取出結(jié)構(gòu)體的數(shù)據(jù) 進(jìn)行初始化兩個變量 **
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
int *num = __cself->num; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_wn_z9c5vw_x6h16tszlbnmscg_80000gn_T_main_e699a8_mi_0, age, (*num));
}
4: main方法的具體體現(xiàn)
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10;
static int num = 20;
void (*mainBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &num));
((void (*)(__block_impl *))((__block_impl *)mainBlock)->FuncPtr)((__block_impl *)mainBlock);
}
return 0;
}
簡化后
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10;
static int num = 20;
void (*mainBlock)(void) =&__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, age, &num));
(mainBlock->FuncPtr)(mainBlock);
}
return 0;
}
結(jié)論: 我們在初始化結(jié)構(gòu)體的時候 會講外部num地址傳入結(jié)構(gòu)體的構(gòu)造函數(shù),結(jié)構(gòu)體有對應(yīng)的指針 int *num作為對應(yīng), 會講外界的數(shù)據(jù)賦值給結(jié)構(gòu)體的數(shù)據(jù), 當(dāng)我們執(zhí)行block的時候 取出結(jié)構(gòu)體的int * num通過尋址處理數(shù)據(jù)
8.5 引用Block外部的全局變量的分析
int age = 10;
static int num = 20;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^mainBlock)(void) = ^{
NSLog(@"main--Block---%d--%d", age, num);
};
age = 20;
num = 40;
mainBlock();
}
return 0;
}
將這塊代碼轉(zhuǎn)為C++代碼
1: 查看當(dāng)前的block結(jié)構(gòu)體結(jié)構(gòu)和以上兩種情況都不一樣
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
**2. __main_block_impl_0包含三個結(jié)構(gòu)體元素__block_impl和__main_block_desc_0
// 結(jié)構(gòu)體 __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 結(jié)構(gòu)體 __main_block_desc_0
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)};
**3: block函數(shù)發(fā)生了變化 在函數(shù)中取出結(jié)構(gòu)體的數(shù)據(jù) 進(jìn)行初始化兩個變量 **
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_wn_z9c5vw_x6h16tszlbnmscg_80000gn_T_main_bceaeb_mi_0, age, num);
}
4: main方法的具體體現(xiàn)
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*mainBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
age = 20;
num = 40;
((void (*)(__block_impl *))((__block_impl *)mainBlock)->FuncPtr)((__block_impl *)mainBlock);
}
return 0;
}
簡化后
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*mainBlock)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
age = 20;
num = 40;
(mainBlock->FuncPtr)(mainBlock);
}
return 0;
}
結(jié)論: 因為全局變量一直存在 不會捕獲到block中 直接使用調(diào)用就可以了
全局變量不捕獲到block, 局部變量捕獲到block
作用域的問題
九. Block在ARC下的內(nèi)存管理
9.1 在ARC默認(rèn)情況下,Block的內(nèi)存存儲在堆中,ARC會自動進(jìn)行內(nèi)存管理,程序員只需要避免循環(huán)引用即可
// 放Block變量出了作用域,Block的內(nèi)存會被自動釋放
void(^myBlock)() = ^{
NSLog(@"------");
};
myBlock();
在Block的內(nèi)存存儲在堆中時,如果在Block中引用了外面的對象,會對所引用的對象進(jìn)行強(qiáng)引用,但是在Block被釋放時會自動去掉對該對象的強(qiáng)引用,所以不會造成內(nèi)存泄漏
Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
// Person對象在這里可以正常被釋放
9.2
如果對象內(nèi)部有一個Block屬性,而在Block內(nèi)部又訪問了該對象,那么會造成循環(huán)引用
情況一
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
p.myBlock = ^{
NSLog(@"------%@", p);
};
p.myBlock();
// 因為myBlock作為Person的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統(tǒng)釋放),所以Block會對Person對象進(jìn)行一次強(qiáng)引用,導(dǎo)致循環(huán)引用無法釋放
情況二
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
- (void)resetBlock;
@end
@implementation Person
- (void)resetBlock
{
self.myBlock = ^{
NSLog(@"------%@", self);
};
}
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
[p resetBlock];
// Person對象在這里無法正常釋放,在resetBlock方法實現(xiàn)中,Block內(nèi)部對self進(jìn)行了一次強(qiáng)引用,導(dǎo)致循環(huán)引用
如果對象內(nèi)部有一個Block屬性,而在Block內(nèi)部又訪問了該對象,那么會造成循環(huán)引用,解決循環(huán)引用的辦法是使用一個弱引用的指針指向該對象,然后在Block內(nèi)部使用該弱引用指針來進(jìn)行操作,這樣避免了Block對對象進(jìn)行強(qiáng)引用
情況一
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
__weak typeof(p) weakP = p;
p.myBlock = ^{
NSLog(@"------%@", weakP);
};
p.myBlock();
// Person對象在這里可以正常被釋放
情況二
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
- (void)resetBlock;
@end
@implementation Person
- (void)resetBlock
{
// 這里為了通用一點,可以使用__weak typeof(self) weakP = self;
__weak Person *weakP = self;
self.myBlock = ^{
NSLog(@"------%@", weakP);
};
}
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
[p resetBlock];
// Person對象在這里可以正常被釋放
十. Block在ARC下的內(nèi)存管理的官方案例
在MRC中,我們從當(dāng)前控制器采用模態(tài)視圖方式present進(jìn)入MyViewController控制器,在Block中會對myViewController進(jìn)行一次retain操作,造成循環(huán)引用
MyViewController *myController = [[MyViewController alloc] init];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
[myController release];
}];
在MRC中解決循環(huán)引用的辦法即在變量前使用下劃線下劃線block修飾,禁止Block對所引用的對象進(jìn)行retain操作
__block MyViewController *myController = [[MyViewController alloc] init];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
[myController release];
}];
但是上述方法在ARC下行不通,因為下劃線下劃線block在ARC中并不能禁止Block對所引用的對象進(jìn)行強(qiáng)引用,解決辦法可以是在Block中將myController置空(為了可以修改myController,還是需要使用下劃線下劃線block對變量進(jìn)行修飾)
__block MyViewController *myController = [[MyViewController alloc] init];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil;
};
[self presentViewController:myController animated:YES completion:^{}];
上述方法確實可以解決循環(huán)引用,但是在ARC中還有更優(yōu)雅的解決辦法,新創(chuàng)建一個弱指針來指向該對象,并將該弱指針放在Block中使用,這樣Block便不會造成循環(huán)引用
MyViewController *myController = [[MyViewController alloc] init];
// ...
__weak MyViewController *weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
[weakMyController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{}];
雖然解決了循環(huán)引用,但是也容易涉及到另一個問題,因為Block是通過弱引用指向了myController對象,那么有可能在調(diào)用Block之前myController對象便已經(jīng)被釋放了,所以我們需要在Block內(nèi)部再定義一個強(qiáng)指針來指向myController對象
MyViewController *myController = [[MyViewController alloc] init];
// ...
__weak MyViewController *weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if (strongMyController)
{
[strongMyController dismissViewControllerAnimated:YES completion:nil];
}
else
{
// Probably nothing...
}
};
[self presentViewController:myController animated:YES completion:^{}];
這里需要補(bǔ)充一下,在Block內(nèi)部定義的變量,會在作用域結(jié)束時自動釋放,Block對其并沒有強(qiáng)引用關(guān)系,且在ARC中只需要避免循環(huán)引用即可,如果只是Block單方面地對外部變量進(jìn)行強(qiáng)引用,并不會造成內(nèi)存泄漏
** 注: 關(guān)于下劃線下劃線block關(guān)鍵字在MRC和ARC下的不同 **
__block在MRC下有兩個作用
1. 允許在Block中訪問和修改局部變量
2. 禁止Block對所引用的對象進(jìn)行隱式retain操作
__block在ARC下只有一個作用
1. 允許在Block中訪問和修改局部變量