Block內部定義
我們想要知道Block的實現過程,首先我們深入Block,下圖是對Block的結構是在棧中的結構,圖片來自互聯網。
下面代碼爲對應結構體定義:
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
Block_layout就是對block結構體的定義:
isa指針:指向表明該block類型的類。
flags:按bit位表示一些block的附加信息,比如判斷block類型、判斷block引用計數、判斷block是否需要執行輔助函數等。
reserved:保留變量,我的理解是表示block內部的變量數。
invoke:函數指針,指向具體的block實現的函數調用地址。
descriptor:block的附加描述信息,比如保留變量數、block的大小、進行copy或dispose的輔助函數指針。
variables:因爲block有閉包性,所以可以訪問block外部的局部變量。這些variables就是複製到結構體中的外部局部變量或變量的地址。
Block類型
從上面Block的定義Block_layout結構體中isa指針類型分爲三種:
- _NSConcreteGlobalBlock:全局的靜態block,不訪問任何外部變量,不會涉及到任何拷貝,比如一個空的block。例如:
^{print("hello world");}
- _NSConcreteStackBlock:保存在棧中的block,引用周圍變量,當函數返回時被銷燬。
char a = 'A';
^{ print("%c\n",a); }
- _NSConcreteMallocBlock:保存在堆中的block。該類型是由棧中Block ( Block_copy / Copy ) 複製到堆中形成的。
//只需要加上copy就可以
char a = 'A';
[^{ print("%c\n",a); } copy];
需要注意的是我們一般創建的Block默認都是在棧中的,而系統的基礎數據類型也是在棧中,那麼又能使用同一個棧,倒是元素丟失,出現野指針,我們在開發中使用安全可以在定義Block時使用copy屬性遷移到棧中。
Block使用
- 使用typedef來定義Block提高程序可讀性。
//適應typedef提高可讀性
typedef void (^ReturnTextBlock)(NSString *showText);
@interface nickNameViewController : UIViewController
@property (nonatomic, copy) ReturnTextBlock returnTextBlock;
- (void)returnText:(ReturnTextBlock)block;
@end
- 使用安全我們定義Block使用copy屬性
@property (nonatomic, copy) ReturnTextBlock returnTextBlock;
- 外部變量使用__block關鍵字,可在Block中修改,默認不能修改
__block int age = 0;
void (^addAge) = ^{
age += 1;
}
NSLog(%d,age )
- 避免循環引用
__block int age = 0;
__weak id weakSelf = self;
void (^push) = ^{
[weakSelf push];
}
PS
在實際開發Block的使用非常多,作爲方法參數,比如網絡請求的失敗和成功回調,SDWebImage也同樣使用多種閉包來完成對應的功能,我記得曾經用的一個插件把Selecte做成Block也是很方便,但是在使用的時候要注意。今天在博客中介紹堆和棧並非傳統意義的堆棧,是程序在運行時在內存的分佈情況,之後我們將詳細介紹。