iOS Block与循环引用

循环引用的产生:
下面以ARC 下为例
a).忧郁Block对象内部引用了外部的变量,而外部的变量又引用了当前的这个block,形成了彼此持有对方。如果外界不主动打断这种链接,那么就会一直持有,导致当前变量对象无法释放。

b).如果block内部没有引用外部变量或者外部变量没有引用block,则不会构成循环引用。
例如Masonry布局代码,其中self并没有对masonry进行引用,所以也就不存在循环引用问题。

[self.view mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.equalTo(self.otherView.mas_centerY);
}];

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

又如下面,当前外部容器也没有对block进行循环引用

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
	void(^tBlock)(void) = ^(void) {
        self.aaaa = 2;
    };
    tBlock();
}

数组的遍历不会造成循环引用,因为外部变量也没有引用当前的block

	self.mutArr = [NSMutableArray arrayWithCapacity:0];
    [self.mutArr addObject:@"000"];
    [self.mutArr addObject:@"001"];
    [self.mutArr addObject:@"003"];
    [self.mutArr addObject:@"004"];

    [self mas_makeConstraints:^(NSString *str) {
        self.aaaa = YES;
        NSLog(@"------");
    }];

解决循环引用
1.使用__weak修饰

- (void)test {
    __weak Person *weakPerson = self;
    self.pblock = ^{
        NSLog(@"------%d", weakPerson.age);
    };
}

上述代码因为采用了__weak修饰,导致了Block对象内部以weak形式持有Person示例,用clang编译成cpp文件,查看如下

//xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-9.0.0 Person.m
struct __Person__test_block_impl_0 {
  struct __block_impl impl;
  struct __Person__test_block_desc_0* Desc;
  Person *__weak weakPerson; //weak指针
  __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

2.____unsafe_unretained修饰变量
也可以实现接触循环引用的问题,但是不常用,主要原因是因为____unsafe_unretained指向的对象销毁时,不会自动让这个指针指向的地址值不变,这样导致不安全,__weak指向的对象不存在,则会自动置为nil

- (void)test {
    __unsafe_unretained Person *weakPerson = self;
    self.pblock = ^{
        NSLog(@"------%d", weakPerson.age);
    };
}

3.__block解决循环引用问题
在block内部对当前 对象进行置nil,同时必要调用一次block()。
因为block内部使用__block修饰的当前对象,此时,block引用__block修饰的block对象成员,而此__block对象内部引用当前对象,此时三者形成互相持有关系。
当调用block()后,__block对象内部持有的指向当前对象的指针置nill,此时也就解除了批次的循环指针指向.
iOS Block与循环引用_第1张图片
ARC下最好的实现,就是用__weak,当然如果在MRC下,也可以使用__block解决,因为MRC下__block对象不会对当前的变量进行retain操作

你可能感兴趣的:(iOS,block)