天天看點

Block之__block

__block 可以用來解決block内部無法修改auto變量值的問題

__blcok不能修飾全局變量、靜态變量(static)

編譯器會将__block變量包裝成一個對象

代碼示例

============================== 原始代碼 ==============================

@interface TBPerson : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation TBPerson

@end

@interface ViewController ()

@end

@implementation ViewController

typedef void (^TBBlock) (void);

- (void)viewDidLoad {
    [super viewDidLoad];
    //  __block修飾的基礎資料類型
    __block int age = 10;
    //  __block修飾的對象類型
    __block TBPerson *person = [[TBPerson alloc] init];
    person.name = @"111";
    //  對象類型的auto變量
    TBPerson *person1 = [[TBPerson alloc] init];
    person1.name = @"000";
    
    TBBlock block = ^{
        age = 20;
        person.name = @"222";
        NSLog(@"age is %d; person -- name is %@; person1 -- name is %@",age,person.name,person1.name);
    };
    block();
}

============================== 使用clang轉換為C++猜測底層實作 ==============================

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
    //  __block int age = 10;
    __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
    //  __block TBPerson *person = [[TBPerson alloc] init];
    __attribute__((__blocks__(byref))) __Block_byref_person_1 person = {(void*)0,(__Block_byref_person_1 *)&person, 33554432, sizeof(__Block_byref_person_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((TBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((TBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TBPerson"), sel_registerName("alloc")), sel_registerName("init"))};
    //  person.name = @"111";
    ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)(person.__forwarding->person), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_lx_q2n5w01149j15zhtyt93nzpr0000gn_T_ViewController_a6f2e3_mi_0);
    //  TBPerson *person1 = [[TBPerson alloc] init];
    TBPerson *person1 = ((TBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((TBPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TBPerson"), sel_registerName("alloc")), sel_registerName("init"));
    //  person1.name = @"000";
    ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)person1, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_lx_q2n5w01149j15zhtyt93nzpr0000gn_T_ViewController_a6f2e3_mi_1);
    //  block
    TBBlock block = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, person1, (__Block_byref_age_0 *)&age, (__Block_byref_person_1 *)&person, 570425344));
    //  調用block方法
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

//	block内部要執行的函數
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
    //  __block變量包裝成一個對象__Block_byref_age_0
    __Block_byref_age_0 *age = __cself->age; // bound by ref
    __Block_byref_person_1 *person = __cself->person; // bound by ref
    TBPerson *person1 = __cself->person1; // bound by copy
    /*
        為什麼要使用age->__forwarding->age、person->__forwarding->person方式來通路
            當變量從棧上拷貝到堆上的時候,可以保證不管是從棧上進行的通路,還是從堆上進行的通路,最後都一定是通路的堆上的變量
     */
    (age->__forwarding->age) = 20;
    ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)(person->__forwarding->person), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_lx_q2n5w01149j15zhtyt93nzpr0000gn_T_ViewController_a6f2e3_mi_2);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_lx_q2n5w01149j15zhtyt93nzpr0000gn_T_ViewController_a6f2e3_mi_3,(age->__forwarding->age),((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)(person->__forwarding->person), sel_registerName("name")),((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)person1, sel_registerName("name")));
}

struct __ViewController__viewDidLoad_block_impl_0 {
    struct __block_impl impl;
    struct __ViewController__viewDidLoad_block_desc_0* Desc;
    TBPerson *person1;              //  block捕獲的外部變量
    __Block_byref_age_0 *age;       //  指向結構體對象 struct __Block_byref_age_0      (就是個強引用,不存在弱引用)
    __Block_byref_person_1 *person; //  指向結構體對象 struct __Block_byref_person_1   (就是個強引用,不存在弱引用)
    __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, TBPerson *_person1, __Block_byref_age_0 *_age, __Block_byref_person_1 *_person, int flags=0) : person1(_person1), age(_age->__forwarding), person(_person->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

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

static struct __ViewController__viewDidLoad_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
    void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
} __ViewController__viewDidLoad_block_desc_0_DATA = {
    0,
    sizeof(struct __ViewController__viewDidLoad_block_impl_0),
    __ViewController__viewDidLoad_block_copy_0,
    __ViewController__viewDidLoad_block_dispose_0
};

/*
    當block在棧上的時候,并不會對__block變量産生強引用
    當block被copy到堆上時
        會調用block内部的copy函數
        copy函數内部會調用_Block_object_assign函數
        _Block_object_assign函數會對 __block變量形成強引用(retain)
 */
static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) {
    _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_assign((void*)&dst->person1, (void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

/*
    當block從堆上移除時
        會調用block内部的dispose函數
        dispose函數内部會調用_Block_object_dispose函數
        _Block_object_dispose函數會自動釋放引用的 __block變量(release)
 */
static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {
    _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_dispose((void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

//  基本資料類型轉C++(__block int age 包裝後的對象)
struct __Block_byref_age_0 {
    void *__isa;                            //  指向結構體的類型
    __Block_byref_age_0 *__forwarding;      //  指向自己
    int __flags;                            //  暫時未用到
    int __size;                             //  結構體的大小
    int age;                                //  變量的值
};

//  對象類型轉C++(__block TBPerson *person 包裝後的對象)
struct __Block_byref_person_1 {
    void *__isa;                            //  指向結構體的類型
    __Block_byref_person_1 *__forwarding;   //  指向自己
    int __flags;                            //  暫時未用到
    int __size;                             //  結構體的大小
    void (*__Block_byref_id_object_copy)(void*, void*);     //  對象執行個體的記憶體管理:copy函數的位址
    void (*__Block_byref_id_object_dispose)(void*);         //  對象執行個體的記憶體管理:dispose函數的位址
    /*
        在ARC下:
            可以是強引用(__block TBPerson *person),也可以是弱引用(__block __weak TBPerson *person),主要看外部修飾詞
        在MRC下:
            不會對TBPerson *person進行retain(也就是說一直是弱引用)
     */
    TBPerson *person;                       //  指向對象執行個體
};

/*
    當__block被copy到堆上時
        會調用__block内部的copy函數
        copy函數内部會調用_Block_object_assign函數
        _Block_object_assign函數會根據指向對象的修飾符(__strong、__weak、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用
        (注意:這裡僅限于ARC時才會retain,在MRC環境時不會retain)
 */
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
    //  (char*)dst + 40 就是TBPerson *person(8+8+4+4+8+8)
    _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}

/*
    當__block從堆上移除時
        會調用__block内部的dispose函數
        dispose函數内部會調用_Block_object_dispose函數
        _Block_object_dispose函數會自動釋放指向的對象(release)
 */
static void __Block_byref_id_object_dispose_131(void *src) {
    _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}


============================== 總結 ==============================

一、對象類型的auto變量、__block變量 的記憶體管理
	當block在棧上時,對它們都不會産生強引用
	當block拷貝到推上時,都會通過copy函數來處理它們
		__block變量:		_Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
		對象類型的auto變量:	_Block_object_assign((void*)&dst->person1, (void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);
	當block從堆上移除時,都會通過dispose函數來釋放它們
		__block變量:		_Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
		對象類型的auto變量:	_Block_object_dispose((void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);

注意:
	__block變量		:	BLOCK_FIELD_IS_BYREF
	對象類型auto變量	:	BLOCK_FIELD_IS_OBJECT

	
二、針對的是__block包裝的結構體的記憶體管理(如:__Block_byref_age_0 *age;		__Block_byref_person_1 *person;)
	當block在棧上的時候,并不會對__block變量産生強引用
	當block被copy到堆上時
		會調用block内部的copy函數
		copy函數内部會調用_Block_object_assign函數
		_Block_object_assign函數會對 __block變量形成強引用(retain)
	當block從堆上移除時
		會調用block内部的dispose函數
		dispose函數内部會調用_Block_object_dispose函數
		_Block_object_dispose函數會自動釋放引用的 __block變量(release


三、針對的是__block包裝的結構體内部指向的對象(__block修飾的對象類型)的記憶體管理
	當__block變量在棧上的時候,并不會對指向的對象産生強引用
	當__block被copy到堆上時
		會調用__block内部的copy函數
		copy函數内部會調用_Block_object_assign函數
		_Block_object_assign函數會根據指向對象的修飾符(__strong、__weak、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用
		(注意:這裡僅限于ARC時才會retain,在MRC環境時不會retain)
	當__block從堆上移除時
		會調用__block内部的dispose函數
		dispose函數内部會調用_Block_object_dispose函數
		_Block_object_dispose函數會自動釋放指向的對象(release)

    
注意:
	1、block内部需要修改auto變量的值時才需要加__block
	2、如果是使用 則不用加__block 如數組的添加、删除元素等操作
	
    3、如果block外面通路__block修飾的變量時,通路的是 包裝後的對象裡面的變量 如:
    	struct __Block_byref_age_0      --> int age             (通路的是age)
    	struct __Block_byref_person_1   --> TBPerson *person    (通路的是person)

    
           

__block變量的copy示意圖(截取自課程内容)

Block之__block

__block變量的dispose示意圖(截取自課程内容)

Block之__block

__block變量的__forwarding指針示意圖(截取自課程内容)

Block之__block
PS 此文為學習 李明傑 老師的 iOS底層原理課程 所寫筆記