本文解决问题:(如无特殊说明,均指的ARC下编译)
- Block相关修饰符 __block __weak __strong __unsafe_unretained 作用
- Block的循环引用
在iOS 底层解析-----Block (上)中讲解到变量捕获,那么对于OC对象类型的操作如何
typedef void (^MJBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
person.age = 10;
MJBlock block = ^{
NSLog(@"---------%d", person.age);
};
}
return 0;
}
------------编译后的__main_block_impl_0----------
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
MJPerson *person;//捕获到内部的是person对象
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MJPerson *__strong _person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
延伸 如果给person 对象增加__weak 或者 __strong 修饰符则捕获后变化为
MJPerson * __weak person;
MJPerson * __strong person;
--------编译后的__main_block_desc_0--------
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
//以下两个方法为对象内存管理的方法
//ARC下当Block从栈中拷贝到堆中的时候会自动调用该copy方法,如果是__strong 形成强引用 __weak __unsafe_unretained形成弱引用
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
//当Block从堆中移除时,dispose函数内部会自动释放引用的变量(release)
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};
总结:
我们都知道用__block 可以修改基本变量类型内部的值
typedef void (^MJBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
MJBlock block = ^{
age = 20;
NSLog(@"---------%d", age);
};
}
return 0;
}
------------编译后的__main_block_impl_0----------
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//编译器将__block 修饰的变量包装成一个对象
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//__block 修饰的变量包装成一个对象 该对象拷贝到堆上的时候会对改对象形成强引用(retain)
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
被__block 修饰的对象类型
typedef void (^MJBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [MJPerson alloc] init];
__block __weak MJPerson *weakPerson = person;
MJBlock block = ^{
age = 20;
NSLog(@"%p", weakPerson);
};
}
return 0;
}
------------编译后的__main_block_impl_0----------
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//编译器将__block 修饰的变量包装成一个对象
__Block_byref_weakPerson_0 *weakPerson; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//__block 修饰的变量包装成一个对象 该对象拷贝到堆上的时候会对改对象形成强引用(retain)
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
void(*__Block_byref_id_object_copy)(void*, void*);
void(*__Block_byref_id_object_dispose)(void*);
//此处的 修饰符为weak 所以为弱引用 如下图
MJPerson *__weak weakPerson;
};
总结:
__weak __unsafe_unretained 都为弱引用,__weak在指向的对象不存在的时候,会设置为nil, 而__unsafe_unretained指向的对象不存在的时候,还是会指向一块已经回收的内存地址即俗称的野指针,是不安全的,所以一般使用__weak
再来看循环引用代码
typedef void (^MJBlock) (void);
@interface MJPerson : NSObject
@property (copy, nonatomic) MJBlock block;
@property (assign, nonatomic) int age;
@end
#import "MJPerson.h"
@implementation MJPerson
@end
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [MJPerson alloc] init];
person.age = 10;
//循环引用 如同
person.block = ^{
NSLog(@"age is %d", person.age);
}
}
return 0;
}
编译后源码不再展示 但是可以知道 block 中捕获的对象变量默认为强引用,
peson 实例对象中它的成员变量(@property (copy, nonatomic) MJBlock block)
如果使用__weak修饰 则可将结构体需要捕获的对象的修饰符改为弱引用
当实例对象的类对象中的成员变量不再使用block 会自动释放block对象,而block结构体中捕获的实例对象指针为弱引用,也不再继续持有实例对象,可解决循环引用