Block

1戈毒、Block

struct Block_layout {
    void *isa;//指向所屬類的指針,也就是block的類型 NSStackBlock NSGlobalBlock  NSMallocBlock   
    int flags;//標(biāo)志變量横堡,在實(shí)現(xiàn)block的內(nèi)部操作時(shí)會(huì)用到
    int reserved;//保留變量
    void (*invoke)(void *, ...);//執(zhí)行時(shí)調(diào)用的函數(shù)指針埋市,block內(nèi)部的執(zhí)行代碼都在這個(gè)函數(shù)中
    struct Block_descriptor *descriptor;//block的詳細(xì)描述,包含 copy/dispose 函數(shù)命贴,處理block引用外部變量時(shí)使用
    /* Imported variables. */
//variables: block范圍外的變量道宅,如果block沒有調(diào)用任何外部變量食听,該變量就不存在
};

struct Block_descriptor {
    unsigned long int reserved;//保留變量
    unsigned long int size;//block的內(nèi)存大小
    void (*copy)(void *dst, void *src);// 拷貝block中被 __block 修飾的外部變量
    void (*dispose)(void *);//和 copy 方法配置應(yīng)用,用來釋放資源
};

2污茵、Block語法

@property(nonatomic, copy)  void (^NormalBlock)(void);

typedef void (^NormalBlock)(void);

@property(nonatomic, copy)  NormalBlock block;
^【返回值類型】【參數(shù)列表】【表達(dá)式】
exp. ^int (int count) {return count + 1;}
注意:【返回值類型】和【參數(shù)列表】可省略

void (^blockName) (int parameter);
//void代表返回值類型樱报,后面一次是block名,參數(shù)

3泞当、Block有哪幾種類型

NSStackBlock存儲(chǔ)于棧區(qū)

block 內(nèi)部引用外部變量迹蛤,retain、release 操作無效襟士,存儲(chǔ)于棧區(qū)盗飒,變量作用域結(jié)束時(shí),其被系統(tǒng)自動(dòng)釋放銷毀陋桂。
MRC 環(huán)境下逆趣,[[mutableAarry addObject: blockA],(在arc中不用擔(dān)心此問題嗜历,因?yàn)閍rc中會(huì)默認(rèn)將實(shí)例化的block拷貝到堆上)在其所在作用域結(jié)束也就是函數(shù)出棧后汗贫,從mutableAarry中取到的blockA已經(jīng)被回收,變成了野指針秸脱。正確的做法是先將blockA copy到堆上落包,然后加入數(shù)組。支持copy摊唇,copy之后生成新的NSMallocBlock類型對(duì)象咐蝇。

NSGlobalBlock 存儲(chǔ)于程序數(shù)據(jù)區(qū)

block 內(nèi)部沒有引用外部變量的 Block 類型都是 NSGlobalBlock 類型,存儲(chǔ)于全局?jǐn)?shù)據(jù)區(qū)巷查,由系統(tǒng)管理其內(nèi)存有序,retain、copy岛请、release操作都無效旭寿。引用static也為globalBlock

NSMallocBlock 存儲(chǔ)于堆區(qū)

如上例中的_block[blockA copy]操作后變量類型變?yōu)?NSMallocBlock崇败,支持retain盅称、release,雖然 retainCount 始終是 1后室,但內(nèi)存管理器中仍然會(huì)增加缩膝、減少計(jì)數(shù),當(dāng)引用計(jì)數(shù)為零的時(shí)候釋放(可多次retain岸霹、release 操作驗(yàn)證)疾层。copy之后不會(huì)生成新的對(duì)象,只是增加了一次引用贡避,類似retain痛黎,盡量不要對(duì)Block使用retain操作予弧。

在ARC環(huán)境下,Block也是存在__NSStackBlock的時(shí)候的湖饱,平時(shí)見到最多的是_NSMallocBlock桌肴,是因?yàn)槲覀儠?huì)對(duì)Block有賦值操作,所以ARC下琉历,block 類型通過=進(jìn)行傳遞時(shí)坠七,會(huì)導(dǎo)致調(diào)用objc_retainBlock->_Block_copy->_Block_copy_internal方法鏈。并導(dǎo)致 NSStackBlock 類型的 block 轉(zhuǎn)換為 NSMallocBlock 類型

4旗笔、Block的結(jié)構(gòu)(Clang后的對(duì)應(yīng))

原代碼

-(void)blockDemo{
    void (^block)(void) = ^{   
};
}

clang后:

struct __block_impl {  
void *isa;//指向所屬類的指針彪置,也就是block的類型
int Flags;  //標(biāo)志變量,在實(shí)現(xiàn)block的內(nèi)部操作時(shí)會(huì)用到
int Reserved;  //保留變量
void *FuncPtr;//block執(zhí)行時(shí)調(diào)用的函數(shù)指針
};
//block內(nèi)部實(shí)現(xiàn) func0
static void __ViewController__blockDemo_block_func_0(struct __ViewController__blockDemo_block_impl_0 *__cself) {
 }

static struct __ViewController__blockDemo_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}
 __ViewController__blockDemo_block_desc_0_DATA = { 0, sizeof(struct __ViewController__blockDemo_block_impl_0)};


static void _I_ViewController_blockDemo(ViewController * self, SEL _cmd) {
    void (*block)(void) = ((void (*)())&__ViewController__blockDemo_block_impl_0((void *)__ViewController__blockDemo_block_func_0, &__ViewController__blockDemo_block_desc_0_DATA));
}

捕獲變量 __block 源代碼

-(void)blockDemo{
    
    __block int a = 100;
    void (^block)(void) = ^{
        a++;
    };
    
    block();
}

clang后

struct __Block_byref_a_0 {
  void *__isa; //指向所屬類的指針蝇恶,被初始化為 (void*)0
__Block_byref_a_0 *__forwarding;//指向?qū)ο笤诙阎械目截? int __flags;//標(biāo)志變量拳魁,在實(shí)現(xiàn)block的內(nèi)部操作時(shí)會(huì)用到
 int __size;//對(duì)象的內(nèi)存大小
 int a;//原始類型的變量
};
static void __ViewController__blockDemo_block_func_0(struct __ViewController__blockDemo_block_impl_0 *__cself) {
  __Block_byref_a_0 *a = __cself->a; // bound by ref


        (a->__forwarding->a)++;

    }

可以看到 多了一個(gè)結(jié)構(gòu)體 被__block修飾的變量被封裝成了一個(gè)對(duì)象,類型為__Block_byref_a_0撮弧,然后把&a作為參數(shù)傳給了block潘懊。
其中,isa贿衍、__flags 和 __size 的含義和之前類似授舟,而 __forwarding 是用來指向?qū)ο笤诙阎械目截悾瑀untime.c 里有源碼說明:

static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {
    ...
    struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);
    copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack
    // 堆中拷貝的forwarding指向它自己
    copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
    // 棧中的forwarding指向堆中的拷貝
    src->forwarding = copy;  // patch stack to point to heap copy
    copy->size = src->size;
    ...
}

這樣做是為了保證在 block內(nèi) 或 block 變量后面對(duì)變量a的訪問贸辈,都是直接訪問堆內(nèi)的對(duì)象释树,而不上棧上的變量。同時(shí)擎淤,在 block 拷貝到堆內(nèi)時(shí)奢啥,它所捕獲的由 __block 修飾的局部基本類型也會(huì)被拷貝到堆內(nèi)(拷貝的是封裝后的對(duì)象),從而會(huì)有 copy 和 dispose處理函數(shù)嘴拢。

5桩盲、 Block copy的過程

Block經(jīng)過copy之后會(huì)在desc里生成的2個(gè)函數(shù)

  • copy函數(shù)
    調(diào)用時(shí)機(jī) 棧上的Block復(fù)制到堆時(shí)
  • dispose函數(shù)
    調(diào)用時(shí)機(jī) 堆上的Block被廢棄時(shí)

當(dāng)Block內(nèi)部訪問了帶有__block修飾符的對(duì)象類型的auto變量時(shí)

  • 當(dāng)block在棧上時(shí),并不會(huì)對(duì)__block變量產(chǎn)生強(qiáng)引用
  • 當(dāng)blockcopy到堆時(shí)
    • 會(huì)調(diào)用block內(nèi)部的copy函數(shù)
    • copy函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
    • _Block_object_assign函數(shù)會(huì)根據(jù)所指向?qū)ο蟮男揎椃?code>__strong, __weak, __unsafe_unretained)做出相應(yīng)的操作席吴,形成強(qiáng)引用(retain)或者弱引用(注意:這里僅限于ARC時(shí)會(huì)retain赌结,MRC時(shí)不會(huì)retain

6、__block的作用

  • __block可以用于解決block內(nèi)部無法修改auto變量值的問題
  • __block不能修飾全局變量抢腐、靜態(tài)變量(static
    編譯器會(huì)將__block變量包裝成一個(gè)對(duì)象
  • __block修改變量:age->__forwarding->age
  • __Block_byref_age_0結(jié)構(gòu)體內(nèi)部地址和外部變量age是同一地址

7姑曙、__block的結(jié)構(gòu)(Clang后的對(duì)應(yīng))

編譯器會(huì)將 __block變量包裝成一個(gè)對(duì)象

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;//age的地址
 int __flags;
 int __size;
 int age;//age 的值
};

8襟交、__forwarding指針的作用

__forwarding, 它是結(jié)構(gòu)體__Block_byref_abc_0的組成部分, 且它的類型是__Block_byref_abc_0 *迈倍。

  • 棧上__block__forwarding指向本身
  • 棧上__block復(fù)制到堆上后,棧上block__forwarding指向堆上的block捣域,堆上block__forwarding指向本身

__forwarding指針存在的意義就是啼染,無論在任何內(nèi)存位置宴合,都可以順利地訪問同一個(gè)__block 變量。

9迹鹅、Block 釋放的過程

當(dāng)block從堆中移除時(shí)

1卦洽、會(huì)調(diào)用block內(nèi)部的dispose函數(shù)
2、 dispose函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
3斜棚、_Block_object_dispose函數(shù)會(huì)自動(dòng)釋放引用的__block變量(release)

當(dāng)block從堆上移除時(shí)阀蒂,都會(huì)通過dispose函數(shù)來釋放它們

10、copy_dispose

11弟蚀、Block循環(huán)引用

三種方式:__weak蚤霞、__unsafe_unretained、__block 解決循環(huán)引用
1义钉、__weak:不會(huì)產(chǎn)生強(qiáng)引用昧绣,指向的對(duì)象銷毀時(shí),會(huì)自動(dòng)讓指針置為nil
2捶闸、__unsafe_unretained:不會(huì)產(chǎn)生強(qiáng)引用夜畴,不安全,指向的對(duì)象銷毀時(shí)删壮,指針存儲(chǔ)的地址值不變
3贪绘、__block:必須把引用對(duì)象置位nil,并且要調(diào)用該block

12央碟、Block捕獲(多重嵌套情況)

block內(nèi)部會(huì)專門新增一個(gè)成員來外面變量的值兔簇,這個(gè)操作稱之為捕獲

13、Block hook的幾種實(shí)現(xiàn)

Hook Block 交換block的實(shí)現(xiàn)
BlockHook硬耍,fishhook垄琐,BlockHookDemoYSBlockHook...

  • 1经柴、交換block的實(shí)現(xiàn) aspect 原理就是在運(yùn)行期間動(dòng)態(tài)交換兩個(gè)方法所指向的IMP指針狸窘,那么換作Block也是一樣的道理。只要將invoke指針指向我們自定義的函數(shù)地址坯认,就可以交換block的實(shí)現(xiàn)
  • 2翻擒、
  • 3、

14牛哺、Block為何會(huì)有Private Data

15陋气、如果獲取Block參數(shù)的個(gè)數(shù)及其類型

16、關(guān)于BLOCK_HAS_EXTENDED_LAYOUT的一些內(nèi)容

17引润、Block 常見題

1巩趁、Block的原理是怎樣的?本質(zhì)是什么淳附?
2议慰、__block的作用是什么蠢古?有什么使用注意點(diǎn)?
3别凹、Block的屬性修飾詞為什么是copy草讶?使用Block有哪些使用注意?
4炉菲、Block在修改NSMutableArray堕战,需不需要添加__block?

注:

clang 用法:clang -fobjc-arc -framework Foundation HelloWord.m -o HelloWord

  • -fobjc-arc表示編譯器需要支持ARC特性
  • -framework Foundation表示引用Foundation框架
  • HelloWord.m為需要進(jìn)行編譯的源代碼文件
  • -o HelloWord表示輸出的可執(zhí)行文件的文件名
  • 1、clang 生成C++
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ViewController.m
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拍霜,一起剝皮案震驚了整個(gè)濱河市践啄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沉御,老刑警劉巖屿讽,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吠裆,居然都是意外死亡伐谈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門试疙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诵棵,“玉大人,你說我怎么就攤上這事祝旷÷陌模” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵怀跛,是天一觀的道長距贷。 經(jīng)常有香客問我,道長吻谋,這世上最難降的妖魔是什么忠蝗? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮漓拾,結(jié)果婚禮上阁最,老公的妹妹穿的比我還像新娘。我一直安慰自己骇两,他們只是感情好速种,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著低千,像睡著了一般配阵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天闸餐,我揣著相機(jī)與錄音饱亮,去河邊找鬼矾芙。 笑死舍沙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的剔宪。 我是一名探鬼主播拂铡,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼葱绒!你這毒婦竟也來了感帅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤地淀,失蹤者是張志新(化名)和其女友劉穎失球,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帮毁,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡实苞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烈疚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黔牵。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖爷肝,靈堂內(nèi)的尸體忽然破棺而出猾浦,到底是詐尸還是另有隱情,我是刑警寧澤灯抛,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布金赦,位于F島的核電站,受9級(jí)特大地震影響对嚼,放射性物質(zhì)發(fā)生泄漏素邪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一猪半、第九天 我趴在偏房一處隱蔽的房頂上張望兔朦。 院中可真熱鬧,春花似錦磨确、人聲如沸沽甥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摆舟。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恨诱,已是汗流浹背媳瞪。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留照宝,地道東北人蛇受。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像厕鹃,于是被迫代替她去往敵國和親兢仰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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