block

block是C語言的擴充功能薛训。帶有自動變量的匿名函數(shù)

截獲自動變量值
保存該自動變量的瞬間值

int main() {
    int dmy = 256;
    int val = 10;
    const char *fmt = "f";
    
    void (*blk)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, fmt, val);
    return 0;
}

block語法表達式中使用的自動變量被作為成員變量追加到了__main_block_impl_0結(jié)構(gòu)體中

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
    const char *fmt;
    int val;
}

使用執(zhí)行block語法時的自動變量fmt和val來初始化__main_block_impl_0結(jié)構(gòu)體實例。

impl.isa = &_NSConcreteStackBlock;
fmt = "f";
val = 10;

通過block使用的匿名函數(shù),實際上被作為簡單的C語音函數(shù)來處理典阵。即自動變量瞬間值被截獲

// id 為objc_object結(jié)構(gòu)體的指針類型
typedef struct objc_object {
    Class isa;
} *id;

// Class 為objc_class結(jié)構(gòu)體的指針類型
typedef struct objc_class *Class;
struct objc_class {
    Class isa;
};

總的來說,所謂‘截獲自動變量的值’意味著在執(zhí)行block語法時,block語法表達式所使用的自動變量值被保存到block的結(jié)構(gòu)體實例中

// OC 類聲明
@interface MyObject : NSObject {
    int val0;
    int val1;
}
@end

// 該類的對象的結(jié)構(gòu)體
struct MyObject {
    Class isa;
    int val0;
    int val1;
};

MyObject類的實例變量val0和val1被直接聲明為對象的結(jié)構(gòu)體成員。
各類的結(jié)構(gòu)體就是基于objc_class結(jié)構(gòu)體的class_t結(jié)構(gòu)體

struct class_t {
    struct class_t *isa;
    struct class_t *superclass;
    Cache cache;
    IMP *vtable;
    uintptr_t data_NEVER_USE;
}

在OC中惧财,比如NSObject的class_t結(jié)構(gòu)體實例,以及NSMutableArray的class_t結(jié)構(gòu)體實例等扭仁,均生成并保持各個類的class_t結(jié)構(gòu)體實例垮衷。該實例持有聲明的成員變量、方法的名稱乖坠、方法的實現(xiàn)(即函數(shù)指針)搀突、屬性以及父類的指針。

// block結(jié)構(gòu)體
struct __main_block_impl_0 {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
    struct __main_block_desc_0 *Desc;
}

此__main_block_impl_0結(jié)構(gòu)體相當于基于objc_objct結(jié)構(gòu)體的OC類對象的結(jié)構(gòu)體熊泵。
對其中的成員變量 isa 進行初始化
isa = &_NSConcreteStackBlock;
即_NSConcreteStackBlock相當于class_t結(jié)構(gòu)體實例仰迁,在將block作為OC的對象處理時,關(guān)于該類的信息放置于_NSConcreteStackBlock中顽分。所以block的實質(zhì)就是OC的對象

__block

__block同block一樣變成__Block_byref_val_o結(jié)構(gòu)體類型的自動變量轩勘,即棧上生成的__Block_byref_val_0結(jié)構(gòu)體實例。該變量初始化為10怯邪,且這個值也出現(xiàn)在結(jié)構(gòu)體的初始化中,這意味著該結(jié)構(gòu)體持有相當于原自動變量的成員變量花墩。

__block int val = 10;
void (^blk)(void) = ^{val = 1;};

// 編譯器代碼
struct __Block_byref_val_0 {
    void *__isa;
    __Block_byref_val_0 * __forwarding;
    int val;
}

struct __main_block_impl_0 {
    __Block_byref_val_0 *val;
    ...
}

block的__main_block_impl_0結(jié)構(gòu)體實例持有指向__block變量的__Block_byref_val_0結(jié)構(gòu)體實例的指針悬秉。
__Block_byref_val_0結(jié)構(gòu)體實例的成員變量__forwarding持有指向該實例自身的指針
另外澄步,__block變量的__Block_byref_val_0結(jié)構(gòu)體并不在Block的__main_block_impl_0結(jié)構(gòu)體中。這樣做是為了在多個Block中使用同一__block變量和泌。

Block存儲

名稱 實質(zhì)
Block 棧上Block的結(jié)構(gòu)體實例
__block變量 棧上__block變量的結(jié)構(gòu)體實例
存儲域
NSConcreteStackBlock
NSConcreteGlobalBlock 程序的數(shù)據(jù)區(qū)
NSConcreteMallocBlock

typedef int (^blk_t)(int);
blk_t blk = ^(int count){return count};
不截獲自動變量的值村缸,放置在程序的數(shù)據(jù)區(qū),其他的block放置在棧上

block超出變量作用域可存在的原因 武氓?
__block變量用結(jié)構(gòu)體成員變量__forwarding存在的原因 梯皿?
配置在全局變量上的block,從變量作用域外也可以通過指針安全的使用县恕,但設(shè)置在棧上的block东羹,__block變量,如果其所屬的變量作用域結(jié)束忠烛,該block属提,__block變量就被廢棄。
將配置在棧上的block復(fù)制到堆上美尸,這樣即使block變量的作用域結(jié)束冤议,堆上的block還可以繼續(xù)存在。
復(fù)制到堆上的block將_NSConcreteMallocBlock類對象寫入block的結(jié)構(gòu)體實例的成員變量isa
impl.isa = &_NSConcreteMallocBlock;
__block變量用結(jié)構(gòu)體成員變量__forwarding可以實現(xiàn)無論__block變量配置在棧上還是堆上师坎,都能正確的訪問__block變量恕酸。在__block變量配置在堆上時,只要棧上的結(jié)構(gòu)體實例成員變量__forwarding指向堆上的結(jié)構(gòu)體實例胯陋。

一般編譯器自行判斷是否將block從棧上copy到堆上
編譯器不能進行判斷的情況:向方法或函數(shù)的參數(shù)中傳遞block時
下面的方法或函數(shù)不用手動復(fù)制
1.Cocoa框架的方法且方法名中含有usingBlock
NSArray enumerateObjectsUsingBlock
2.CGD 的API
dispatch_async函數(shù)

- (id)getBlockArray{
    int val = 10;
    return [[NSArray alloc] initWithObjects: ^{NSLog(@"blk0:%d",val);},
                                                       ^{NSLog(@"blk1:%d",val);}, nil];
}

- (void)testBlock{
    id obj = getBlockArray();
    typedef void (^blk_t) (void);
    blk_t blk = (blk_t)[obj objectAtIndex:0];
    blk(); // 異常
}
// getBlockArray 函數(shù)執(zhí)行結(jié)束時蕊温,棧上的Block已被廢棄

// 修正 如果block不截取自動變量則可正確運行
- (id)getBlockArray{
    int val = 10;
    return [[NSArray alloc] initWithObjects: [^{NSLog(@"blk0:%d",val);} copy],
                                                       [^{NSLog(@"blk1:%d",val);} copy], nil];
}

block類 | 副本源的配置存儲域 | 復(fù)制效果
------- | -------
_NSConcreteStackBlock | 棧 | 從棧復(fù)制到堆
_NSConcreteGlobalBlock | 程序的數(shù)據(jù)區(qū) | 什么也不做
_NSConcreteMallocBlock | 堆 | 引用計數(shù)增加

不管block配置在何處,用copy方法復(fù)制都不會引起任何問題惶岭,在不確定時調(diào)用copy即可

__block變量存儲域

__block變量的配置存儲 block從棧上復(fù)制到堆時的影響
從棧復(fù)制到堆寿弱,并被block持有
被block持有

將棧上的__block變量結(jié)構(gòu)體實例復(fù)制到堆上時,會將成員變量_forwarding的值替換為復(fù)制目標堆上的__block變量結(jié)構(gòu)體實例的地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末按灶,一起剝皮案震驚了整個濱河市症革,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸯旁,老刑警劉巖噪矛,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異铺罢,居然都是意外死亡艇挨,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門韭赘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缩滨,“玉大人,你說我怎么就攤上這事÷雎” “怎么了苞冯?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長侧巨。 經(jīng)常有香客問我舅锄,道長,這世上最難降的妖魔是什么司忱? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任皇忿,我火速辦了婚禮,結(jié)果婚禮上坦仍,老公的妹妹穿的比我還像新娘鳍烁。我一直安慰自己,他們只是感情好桨踪,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布老翘。 她就那樣靜靜地躺著,像睡著了一般锻离。 火紅的嫁衣襯著肌膚如雪铺峭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天汽纠,我揣著相機與錄音卫键,去河邊找鬼。 笑死虱朵,一個胖子當著我的面吹牛莉炉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碴犬,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼絮宁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了服协?” 一聲冷哼從身側(cè)響起绍昂,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎偿荷,沒想到半個月后窘游,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡跳纳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年忍饰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寺庄。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡艾蓝,死狀恐怖力崇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饶深,我是刑警寧澤餐曹,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站敌厘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏朽合。R本人自食惡果不足惜俱两,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望曹步。 院中可真熱鬧宪彩,春花似錦、人聲如沸讲婚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筹麸。三九已至活合,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間物赶,已是汗流浹背白指。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留酵紫,地道東北人告嘲。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像奖地,于是被迫代替她去往敵國和親橄唬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容