Block

1、Block

struct Block_layout {
    void *isa;//指向所屬類(lèi)的指針,也就是block的類(lèi)型 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沒(méi)有調(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)用,用來(lái)釋放資源
};

2、Block語(yǔ)法

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

typedef void (^NormalBlock)(void);

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

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

3、Block有哪幾種類(lèi)型

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

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

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

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

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

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

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

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

原代碼

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

clang后:

struct __block_impl {  
void *isa;//指向所屬類(lèi)的指針,也就是block的類(lèi)型
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; //指向所屬類(lèi)的指針,被初始化為 (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;//原始類(lèi)型的變量
};
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ì)象,類(lèi)型為__Block_byref_a_0,然后把&a作為參數(shù)傳給了block。
其中,isa、__flags 和 __size 的含義和之前類(lèi)似,而 __forwarding 是用來(lái)指向?qū)ο笤诙阎械目截悾瑀untime.c 里有源碼說(shuō)明:

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的訪(fǎng)問(wèn),都是直接訪(fǎng)問(wèn)堆內(nèi)的對(duì)象,而不上棧上的變量。同時(shí),在 block 拷貝到堆內(nèi)時(shí),它所捕獲的由 __block 修飾的局部基本類(lèi)型也會(huì)被拷貝到堆內(nèi)(拷貝的是封裝后的對(duì)象),從而會(huì)有 copy 和 dispose處理函數(shù)。

5、 Block copy的過(guò)程

Block經(jīng)過(guò)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)部訪(fǎng)問(wèn)了帶有__block修飾符的對(duì)象類(lèi)型的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ì)retainMRC時(shí)不會(huì)retain

6、__block的作用

  • __block可以用于解決block內(nèi)部無(wú)法修改auto變量值的問(wèn)題
  • __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的組成部分, 且它的類(lèi)型是__Block_byref_abc_0 *。

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

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

9、Block 釋放的過(guò)程

當(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ì)通過(guò)dispose函數(shù)來(lái)釋放它們

10、copy_dispose

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

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

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

block內(nèi)部會(huì)專(zhuān)門(mén)新增一個(gè)成員來(lái)外面變量的值,這個(gè)操作稱(chēng)之為捕獲

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

Hook Block 交換block的實(shí)現(xiàn)
BlockHook,fishhookBlockHookDemo,YSBlockHook...

  • 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ù)及其類(lèi)型

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

17、Block 常見(jiàn)題

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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容