天天看点

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底层原理课程 所写笔记