- block的原理是怎样的?本质是什么?
Block其实就是一个OC对象,具体来说是封装了函数调用以及调用环境的OC对象。
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
struct __block_impl {
void *isa; //isa指针
int Flags;
int Reserved;
void *FuncPtr; //函数调用的地址
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
};
- block的变量捕获(capture)
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制,简单来说就是变量是否在block内部生成。
变量类型 | 捕获到block内部 | 访问方式 | |
---|---|---|---|
局部变量 |
auto | √ | 值传递 |
static | √ | 指针传递 | |
全局变量 | × | 直接访问 |
在block中self以及类似self.age属于局部变量,访问的方式是按值传递。
- block的类型
nblock有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
NSGlobalBlock( _NSConcreteGlobalBlock)
NSStackBlock( _NSConcreteStackBlock )
NSMallocBlock( _NSConcreteMallocBlock )
block类型 | 环境 |
---|---|
NSGlobalBlock | 没有访问auto变量 |
NSStackBlock | 访问了auto变量 |
NSMallocBlock | NSStackBlock调用了copy |
每一种类型的block调用copy后的结果如下所示
- block的copy
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
block作为函数返回值时
将block赋值给__strong指针时
block作为Cocoa API中方法名含有usingBlock的方法参数时
block作为GCD API的方法参数时
MRC下block属性的建议写法
@property(copy,nonatomic)void(^block)(void);
ARC下block属性的建议写法
@property(strong,nonatomic)void(^block)(void);
@property(copy,nonatomic)void(^block)(void);
- 对象类型的auto变量
- 当block内部访问了对象类型的auto变量时
如果block是在栈上,将不会对auto变量产生强引用
- 如果block被拷贝到堆上
会调用block内部的copy函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会根据auto变量的修饰符(strong、weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
- 如果block从堆上移除
会调用block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放引用的auto变量(release)
- __block的作用是什么?有什么使用注意点?
__block可以用于解决block内部无法修改auto变量值的问题
__block不能修饰全局变量、静态变量(static)
编译器会将__block变量包装成一个对象
block调用的代码。
int no = 20;
__block int age = 10;
NSObject *object = [[NSObject alloc] init];
__weak NSObject *weakObject = object;
CJBlock cjBlock = ^{
NSLog(@"%d", no);
NSLog(@"age = %d", age);
NSLog(@"%p", weakObject);
};
cjBlock();
block实现代码
typedef void (*CJBlock)(void);
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int no;
NSObject *__weak weakObject;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _no, NSObject *__weak _weakObject, __Block_byref_age_0 *_age, int flags=0) : no(_no), weakObject(_weakObject), 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) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
int no = __cself->no; // bound by copy
NSObject *__weak weakObject = __cself->weakObject; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_ls1g9rvn1693sq6qgpbwjgbr0000gn_T_main_c1bc15_mi_0, no);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_ls1g9rvn1693sq6qgpbwjgbr0000gn_T_main_c1bc15_mi_1, (age->__forwarding->age));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_ls1g9rvn1693sq6qgpbwjgbr0000gn_T_main_c1bc15_mi_2, weakObject);
}
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_object_assign((void*)&dst->weakObject, (void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->weakObject, 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, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int no = 20;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
NSObject *object = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
__attribute__((objc_ownership(weak))) NSObject *weakObject = object;
CJBlock cjBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, no, weakObject, (__Block_byref_age_0 *)&age, 570425344));
((void (*)(__block_impl *))((__block_impl *)cjBlock)->FuncPtr)((__block_impl *)cjBlock);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
- 对象类型的auto变量、__block变量
当block在栈上时,对它们都不会产生强引用
当block拷贝到堆上时,都会通过copy函数来处理它们
__block变量(假设变量名叫做a)
_Block_object_assign((void)&dst->a,(void)src->a,8/BLOCK_FIELD_IS_BYREF/);
对象类型的auto变量(假设变量名叫做p)
_Block_object_assign((void)&dst->p,(void)src->p,3/BLOCK_FIELD_IS_OBJECT/);
当block从堆上移除时,都会通过dispose函数来释放它们
__block变量(假设变量名叫做a)
_Block_object_dispose((void)src->a,8/BLOCK_FIELD_IS_BYREF*/);
对象类型的auto变量(假设变量名叫做p)
_Block_object_dispose((void)src->p,3/BLOCK_FIELD_IS_OBJECT*/);
- 被__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)
//Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)
__block __weak MJPerson *weakPerson = person;
CJBlock block;
{
MJPerson *person = [[MJPerson alloc] init];
__block __weak MJPerson *weakPerson = person;
block = ^{
NSLog(@"%p", weakPerson);
};
}
block();
//在MRC模式下,__block修饰的变量,比如person不管是strong还是weak类型,都不会形成强引用,都不会retain。
CJBlock block;
__block MJPerson *person = [[MJPerson alloc] init];
block = [^{
NSLog(@"%p", person);
} copy];
[person release];
block();
[block release];
- 循环引用问题
就是两个强指针互相引用,你引用我,我引用你,导致无法正常释放。
解决循环引用问题 - ARC
解决循环引用问题 - MRC
在MRC的情况下 block修饰的修饰的变量,在Block的imp里面,不会有retain的操作,所以不存在强引用,所以再MRC的情况下,用block修饰变量,也可以解决循环引用的问题。
- block的属性修饰词为什么是copy?使用block有哪些使用注意?
block一旦没有进行copy操作,就不会在堆上
使用注意:循环引用问题
- block在修改NSMutableArray,需不需要添加__block?
不需要,只是要使用array的值,比如[array addObject:@”chenjie”],而不是对array进行赋值操作。
- 为什么有的block要加__strong修饰。
__weak typeof(self) weakSelf = self;<br /> self.block = ^{<br /> __strong typeof(weakSelf) myself = weakSelf;<br /> NSLog(@"age = %lu", (unsigned long)myself->_age);<br /> };
1、首先加__strong能保证编译正确,能正确的访问成员变量。
2、保住self的命,在程序运行的周期内,不会被无故的销毁。