Object-C語言Block的實現方式

開場白
Block基本概念
中間態轉換方法
Block編譯後結果分析
Block運行時狀態與編譯狀態對比
 
開場白
 
Object-C語言是對C語言的擴展,因此將OC源碼進行編譯的時候,會將OC源碼會被轉換成C\C++,因此想了解OC源碼的實現細節,仍是須要手動編譯成中間狀態進行觀察。
命令1:
clang -rewrite-objc main.m

若是Xcode版本較高,可能會出現報錯:html

./block_VC.h:9:9: fatal error: 'UIKit/UIKit.h' file not found

此時可嘗試另外一個命令:objective-c

clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m
命令好長,每次輸入這麼長的指令很麻煩,此時能夠考慮使用命令別名:alias

 

Block的基本概念express

_NSConcreteGlobalBlock 全局的靜態 block,不會訪問任何外部變量。
_NSConcreteStackBlock 保存在棧中的 block,當函數返回時會被銷燬。
_NSConcreteMallocBlock 保存在堆中的 block,當引用計數爲 0 時會被銷燬。

 

堆中的 block什麼時候轉換:
1.調用copy方法時
2.從一個函數返回時
3.將block傳入dispatch等系統IPA參數block或者傳遞給帶有usingBlock的Cocoa框架函數時。
4.將block賦給帶有__strong修飾符的id類型或者Block類型時。

 

Block類型變換方式

1.沒有捕獲任何局部變量的block爲_NSConcreteGlobalBlock,它以static函數的形式存儲在代碼區app

2.捕獲了局部變量的block爲_NSConcreteStackBlock框架

3.當_NSConcreteStackBlock出現上面四種狀況時,會變成_NSConcreteMallocBlock。(注意此時是調用了Block_Copy函數後)函數

 

Block編譯後結果分析佈局

基本類型變量spa

OC源碼:3d

NSInteger age = 10;
void(^completeBlock)(NSString *) = ^(NSString *name) {
    NSString *info = [NSString stringWithFormat:@"name:%@ - age:%d",name,age];
    NSLog(@"%@",info);
};

completeBlock(@"jack");

編譯後的C\C++源碼:指針

// __block_impl爲block的基礎struct
//1.*isa:當前block對象所屬的類型(注意:在OC語言中帶有isa指針的都是對象)
//2.Flags:標示位,當類型爲NSConcreteMallocBlock時,表示須要ARC內存管理,其餘默認不須要內存管理
//3.Reserved:預留標示位,暫時無論它
//4.block的實際static內部函數地址指針
struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

// __main_block_impl_0爲block的最終struct
//1.impl:爲上面的__block_impl結構體實例
//2.Desc:爲block的描述結構體__main_block_desc_0,裏面包含了block結構體的信息描述,在下面講解
//3.age:從外面補貨的變量,block採用的方式是直接在block實例中定義一個變量,將捕獲的值賦給它
//4.__main_block_impl_0:爲block結構體的構造函數,參數中包含從外面捕獲的變量值
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    NSInteger age;
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSInteger _age, int flags=0) : age(_age) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

//__main_block_func_0爲block具體實現的函數載體,具體的block的任務實如今此函數內
//參數:1是block的實例指針,2是block的外部傳參name
static void __main_block_func_0(struct __main_block_impl_0 *__cself, NSString *name) {
    //從block實例中獲取age變量的值
    NSInteger age = __cself->age; // bound by copy

    //生成字符串NSString *info = [NSString stringWithFormat:@"name:%@ - age:%d",name,age];
    NSString *info = ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_83d55e_mi_0, (NSString *)name, (NSInteger)age);
    
    //打印NSLog(@"%@",info);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_83d55e_mi_1,info);
}

//reserved:保留字0
//Block_size:block實例在內存中佔據內存大小
//__main_block_desc_0_DATA:默認desc結構體實例
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)};

//main函數
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

        NSInteger age = 10;
        void(*completeBlock)(NSString *) = ((void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));

        ((void (*)(__block_impl *, NSString *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock, (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_83d55e_mi_2);

        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

block簡單結構佈局

struct __block_impl
struct __main_block_impl_0
static void __main_block_func_0
static struct __main_block_desc_0
詳細解釋見上面的中間編譯狀態。
block實例中定義了一個age變量,並將外部的age傳了過去
 
__block基本類型變量
OC源碼:
__block NSInteger age = 10;
void(^completeBlock)(NSString *) = ^(NSString *name) {
    NSString *info = [NSString stringWithFormat:@"name:%@ - age:%d",name,age];
    NSLog(@"%@",info);
};

completeBlock(@"jack");
編譯後的C\C++源碼:
struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

//age的block變量實例__Block_byref_age
//__isa:age的block變量實例所屬的類型
//__forwarding:age的block變量實例的指針
//__flags:變量是否被內存管理標示
//__size:所佔內存大小
//age:age變量值
struct __Block_byref_age_0 {
    void *__isa;
    __Block_byref_age_0 *__forwarding;
    int __flags;
    int __size;
    NSInteger age;
};

//原來的age變量值,變成了__Block_byref_age_0變量指針
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_age_0 *age; // by ref
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, NSString *name) {
    //經過block實例獲取變量的指針
    __Block_byref_age_0 *age = __cself->age; // bound by ref

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_7acc63_mi_0,name,(age->__forwarding->age));
}

//當block從棧複製到堆上時,會對__Block_byref_age_0變量進行拷貝,也會從棧拷貝到堆上
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

//__Block_byref_age_0變量的析構函數,當block實例的引用計數爲0時,是否__Block_byref_age_0變量的內存空間
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

//desc結構體中新增了copy函數和dispose函數指針
static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        //block變量實例
        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
        //block實例
        void(*completeBlock)(NSString *) = ((void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));

        ((void (*)(__block_impl *, NSString *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock, (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_7acc63_mi_1);

        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

簡單結構佈局

struct __block_impl
struct __main_block_impl_0
static void __main_block_func_0
static struct __main_block_desc_0
//新增
struct __Block_byref_age_0
static void __main_block_copy_0(
static void __main_block_dispose_0(

__block NSInteger age = 10;後,在簡單佈局結構中新增了3項

struct __Block_byref_age_0
static void __main_block_copy_0(
static void __main_block_dispose_0(
__Block_byref_age_0將基本變量轉換成__block變量結構體實例指針
__main_block_copy_0和__main_block_dispose_0是對__block變量結構體實例的內存管理方法。
__block變量結構體實例從棧拷貝到堆時,調用方法:__main_block_copy_0
__block變量結構體實例引用計數爲0時,調用方法:__main_block_dispose_0
 
指針類型變量

OC源碼:

NSMutableArray *friends = [NSMutableArray array];;
void(^completeBlock)(NSString *) = ^(NSString *name) {
    NSLog(@"%@--%@",name,friends);
};

completeBlock(@"jack");

編譯後的C\C++源碼:

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    NSMutableArray *friends;
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableArray *_friends, int flags=0) : friends(_friends) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, NSString *name) {
    NSMutableArray *friends = __cself->friends; // bound by copy

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_1b4a2f_mi_0,name,friends);
}
//block實例從棧複製到堆上時,將指針變量引用計數加一
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->friends, (void*)src->friends, 3/*BLOCK_FIELD_IS_OBJECT*/);}

//block實例引用計數爲0時,析構指針變量對象
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->friends, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

        NSMutableArray *friends = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));;
        void(*completeBlock)(NSString *) = ((void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, friends, 570425344));

        ((void (*)(__block_impl *, NSString *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock, (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_1b4a2f_mi_1);

        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

簡單結構佈局

struct __block_impl
struct __main_block_impl_0
static void __main_block_func_0
static struct __main_block_desc_0
static void __main_block_copy_0(
static void __main_block_dispose_0(
block實例中定義了一個NSMutableArray *friends;指針變量,並經過構造函數將指針傳遞給block實例
__main_block_copy_0:block實例從棧複製到堆上時,將指針變量引用計數加一
__main_block_dispose_0:block實例引用計數爲0時,析構指針變量對象

 

__block指針類型變量

OC源碼:

__block NSMutableArray *friends = [NSMutableArray array];;
void(^completeBlock)(NSString *) = ^(NSString *name) {
    NSLog(@"%@--%@",name,friends);
};

completeBlock(@"jack");

編譯後的C\C++源碼:

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

//__Block_byref_friends_0結構體實例中包含了friends指針
//而且多了__Block_byref_id_object_copy函數:對friends進行copy
//多了__Block_byref_id_object_dispose函數:對friends進行析構
struct __Block_byref_friends_0 {
    void *__isa;
    __Block_byref_friends_0 *__forwarding;
    int __flags;
    int __size;
    void (*__Block_byref_id_object_copy)(void*, void*);
    void (*__Block_byref_id_object_dispose)(void*);
    NSMutableArray *friends;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_friends_0 *friends; // by ref
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_friends_0 *_friends, int flags=0) : friends(_friends->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, NSString *name) {
    __Block_byref_friends_0 *friends = __cself->friends; // bound by ref

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_252d0c_mi_0,name,(friends->__forwarding->friends));
}

//block實例從棧複製到堆上時,__block變量結構體實例將指針變量引用計數加一,同時__block變量結構體實例內的NSMutableArray *friends變量也遞歸加一
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->friends, (void*)src->friends, 8/*BLOCK_FIELD_IS_BYREF*/);}

//block實例引用計數爲0時,__block變量結構體實例被析構,同時__block變量結構體實例內的NSMutableArray *friends變量也遞歸析構
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->friends, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

        __attribute__((__blocks__(byref))) __Block_byref_friends_0 friends = {(void*)0,(__Block_byref_friends_0 *)&friends, 33554432, sizeof(__Block_byref_friends_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"))};;
        void(*completeBlock)(NSString *) = ((void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_friends_0 *)&friends, 570425344));

        ((void (*)(__block_impl *, NSString *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock, (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_252d0c_mi_1);

        return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
}

簡單結構變量

struct __block_impl
struct __main_block_impl_0
static void __main_block_func_0
static struct __main_block_desc_0
static void __main_block_copy_0(
static void __main_block_dispose_0(
//新增
struct __Block_byref_age_0
和__block普通變量同樣,出現了__Block_byref_age_0結構體
__Block_byref_friends_0結構體實例中包含了friends指針
而且多了__Block_byref_id_object_copy函數:對friends進行copy
多了__Block_byref_id_object_dispose函數:對friends進行析構
同時在函數__main_block_copy_0和__main_block_dispose_0中,出現的也是對__Block_byref_age_0結構體的調用。它是在將__Block_byref_age_0結構體析構以前,先析構friends對象。
 
Block編譯中間態與運行時狀態對比
利用lldb打印出來的結果
局部自由變量
//局部變量
__block NSMutableArray *friends = [NSMutableArray array];;
void(^completeBlock)(NSString *) = ^(NSString *name) {
    NSLog(@"%@--%@",name,friends);
};


(lldb) expression -P 5 -- completeBlock
(void (^)(NSString *)) $2 = 0x000000010a061130 {
  __isa = __NSMallocBlock__
  __flags = -1023410170
  __reserved = 0
  __FuncPtr = 0x000000010a061130 (iOS_KnowledgeStructure`__main_block_invoke at main.m:19) {}
}

利用clang轉換後的C\C++源碼

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __Block_byref_friends_0 *friends; // by ref
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_friends_0 *_friends, int flags=0) : friends(_friends->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
證實clang編譯器和llvm編譯器編譯後的結果有出入。發現isa指針不一致。
編譯後的爲:impl.isa = &_NSConcreteStackBlock;
運行時的爲:__isa = __NSMallocBlock__
緣由是block類型轉換狀況4:「將block賦給帶有__strong修飾符的id類型或者Block類型時。」
 
注意:
_NSConcreteMallocBlock通常不會在源碼中出現,它一般在block copy到堆時出現,變化的源碼:
static void *_Block_copy_internal(const void *arg, const int flags) {
struct Block_layout *aBlock;
const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
// 1
if (!arg) return NULL;
// 2
aBlock = (struct Block_layout *)arg;
// 3
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
// 4
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
// 5
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return (void *)0;
// 6
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// 7
result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 1;
// 8
result->isa = _NSConcreteMallocBlock;
// 9
if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
return result;
}
相關文章
相關標籤/搜索