下面代码是否会循环引用

DXPerson *person = [[DXPerson alloc] init];
person.age = 10;
person.block = ^{
    NSLog(@"age = %d", person.age);
};

答:会,编译器已经有提示

如何修改,才不会循环引用?

1.使用__weak

DXPerson *person = [[DXPerson alloc] init];
person.age = 10;
__weak DXPerson *weakPerson = person;
person.block = ^{
    NSLog(@"age = %d", weakPerson.age);
};

或者使用typeof()

// typeof(person) == DXPerson *
// 好处就是不需要知道person到底是什么类型
__weak typeof(person) weakPerson = person;

经常可以看到weakSelf

__weak typeof(self) weakSelf = self;

2.使用__unsafe_unretained这个关键字解决循环引用

__unsafe_unretained DXPerson *weakPerson = person;
// 或
// __unsafe_unretained typeof(person) weakPerson = person;

__unsafe_unretained__weak的区别

__unsafe_unretainerengd是不安全的,不会产生强引用,因为指向的对象释放后,指针仍然指向原来的地址,如果又使用到了这个指针,就会出现野指针,因此ARC环境下,不常用,多用于MRC环境上.

__weak是安全的,不会产生强引用,对象释放后,指针自动置为nil(MRC环境上,是没有__weak弱引用)

3.使用__block这个关键字解决循环引用

__block DXPerson *person = [[DXPerson alloc] init];
person.age = 10;
person.block = ^{
    NSLog(@"age = %d", person.age);
    // 使用`__block`修饰对象,那么这个对象就可以修改了,否则下面编译器会报错
    person = nil;
};
person.block();

必须调用blockperson = nil,两者缺一不可.这种方案不推荐

查看C++中block代码,这个block中有person这个对象

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_person_0 *person; // by ref
  
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

查看person这个__Block_byref_person_0结构体

struct __Block_byref_person_0 {
  void *__isa;
  __Block_byref_person_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 DXPerson *__strong person;
};

可以看到这个结构体有__strong修饰的person对象

如图 demo demo

在MRC环境上使用__unsafe_unretained或者__block

__unsafe_unretained DXPerson *person = [[DXPerson alloc] init];
//__block DXPerson *person = [[DXPerson alloc] init];
person.age = 10;
person.block = ^{
    NSLog(@"age = %d", person.age);
};
person.block();
[person release];

在MRC环境上__unsafe_unretained或者__block都用来告诉编译器,不要对对象进行retain操作,因此相当于弱引用

面试题:

1.在block内部NSMutableArray添加或者删除元素,是否需要添加__block进行修饰?

DXPerson *person = [[DXPerson alloc] init];
NSMutableArray *array = [NSMutableArray array];
person.block = ^{
    [array addObject:@1];
};

答案:不需要,除非在block内部array = nil才需要

2.声明block时候,使用copy还是strong?

@property (nonatomic,strong) DXBlock block;
@property (nonatomic,copy) DXBlock block;

在ARC环境上答案是一样,因为在ARC环境上,strong或者copy都会对block进行copy操作,保证block拷贝到堆上.

在MRC环境上,strong不会对block进行copy操作,copy会对block进行copy操作,保证block拷贝到堆上.