iOS之__block、__weak、Block循环引用、__weak typeof(self) weakSelf = self;

在介绍block循环引用前我们先了解一下typeof。我们经常可以看到这样的代码:__weak typeof(self) weakSelf = self;、

block对于其变量都会形成强引用(retain),对于self也会形成强引用(retain) ,而如果self本身对block也是强引用的话,就会形成 强引用 循环,无法释放——造成内存泄露。,所以我们会使用

__block typeof(self) bself = self;          // 适用MRC模式,当修饰变量时,表示这个变量值能在block中被修改
__weaktypeof(self) weakself = self;     // 适用ARC模式



typeof是什么???

通俗的说就是:可以根据typeof()括号里面的变量,自动识别变量类型并返回该类型。
typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型。 它返回值是一个字符串,该字符串说明运算数的类型。
一、对于数字类型的操作数而言,typeof返回的值是number。比如说:typeof(1),返回的值就是number。 上面是举的常规数字,对于非常规的数字类型而言,其结果返回的也是number。比如typeof(NaN),NaN在 JavaScript中代表的是特殊非数字值,虽然它本身是一个数字类型。
二、对于字符串类型,typeof返回的值是string。比如typeof("123")返回的值是string。
三、对于布尔类型,typeof返回的值是boolean.比如typeof(true)返回的值是boolean。
四、对于对象、数组、null 返回的值是 object 。比如typeof(window),typeof(document),typeof(null)返回的值都是object。
五、对于函数类型,返回的值是 function。比如:typeof(eval),typeof(Date)返回的值都是function。
六、如果运算数是没有定义的(比如说不存在的变量、函数或者undefined),将返回undefined。比如:typeof(sss)、typeof(undefined)都返回undefined。

为什么要用弱引用???
block在ARC中使用strong、copy这两种的效果是一样的。既然用到strong,copy当然就要考虑到强引用问题:
self有一个属性Block,然而这个Block在主函数体又引用了self的其他成员变量,那么就会对这个变量本身产生强引用,那么变量本身和它自己的Block属性就形成了循环引用。因此我们需要对其进行处理进行弱引用。

怎么实现弱引用???
下面是简单的代码:

__weak typeof(self) weakSelf = self;

self.Block = ^ {
      if (weakSelf.people) {
          weakSelf.people.name = @"hello";
      }
}; 



什么是Block循环引用???

两个对象相互持有,这样就会造成循环引用,如下图所示



对象A持有对象B,对象B持有对象A,相互持有,最终导致两个对象都不能释放。


1、block在主函数体用到了self / self.变量 / [self 方法],意味着:block对self 进行持有操作,

2、self声明了属性变量block,block用copy来修饰,意味着:self对block进行持有操作,会造成循环引用。如下,在类.m中

typedef void(^block)();

@property (copy, nonatomic) block myBlock;  // 2
@property (copy, nonatomic) NSString *blockString;


- (void)testBlock {
    self.myBlock = ^() {
        //其实注释中的代码,同样会造成循环引用
        NSString *localString = self.blockString; // 1
          //NSString *localString = _blockString;
          //[self doSomething];
    };
}

注:以下调用注释掉的代码同样会造成循环引用,因为不管是通过self.blockString还是_blockString,或是函数调用[self doSomething],因为只要 block中用到了对象的属性或者函数,block就会持有该对象而不是该对象中的某个属性或者函数。


解决方法:

__weak typeof(self) weakSelf = self;
self.myBlock = ^() {
    NSString *localString = weakSelf.blockString;
};

使用__weak打破循环的方法只在ARC下才有效,在MRC下应该使用__block
或者,

在block执行完后,将block置nil,这样也可以打破循环引用
这样做的缺点是,block只会执行一次,因为block被置nil了,要再次使用的话,需要重新赋值。


关于——Block在MRC和ARC模式的区别

1)__block在MRC下有两个作用
     允许在Block中访问和修改局部变量 
     禁止Block对所引用的对象进行隐式retain操作

2)__block在ARC下只有一个作用
    允许在Block中访问和修改局部变量


在MRC中解决循环引用的办法即在变量前使用下划线下划线block修饰,禁止Block对所引用的对象进行retain操作

__block MyViewController *myController = [[MyViewController alloc] init];
// ...
myController.completionHandler =  ^(NSInteger result) {
    [myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
   [myController release];
}];


什么时候在 block 中不需要使用 weakSelf???
一些不会造成循环引用的block

在开发工程中,发现一些同学并没有完全理解循环引用,以为只要有block的地方就会要用__weak来修饰对象,这样完全没有必要,以下几种block是不会造成循环引用的。

1)大部分GCD方法

dispatch_async(dispatch_get_main_queue(), ^{
    [self doSomething];
});

因为self并没有对GCD的block进行持有,没有形成循环引用。目前我还没碰到使用GCD导致循环引用的场景,如果某种场景self对GCD的block进行了持有,则才有可能造成循环引用。


2)大部分动画效果。

当 block 本身不被 self 持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用 weak self 了。最常见的代码就是 UIView 的动画代码:

[UIView animateWithDuration:0.2 animations:^{
    self.alpha = 1;
}];
当动画结束时,UIView 会结束持有这个 block,block 对象就会释放掉,从而 block 会释放掉对于 self 的持有。整个内存引用关系被解除。


3)block并不是对象的属性 / 变量,而是方法的参数 / 临时变量

-  (void) doSomething {
    [self testWithBlock:^{
        [self test];
    }];
}

-  (void) testWithBlock:(void(^)())block {
    block();
}

-  (void) test {
    NSLog(@"test");
}

这里因为block只是一个临时变量,self并没有对其持有,所以没有造成循环引用


以下情况也不需要:虽然Block主体函数用到了MyObject,但MyObject对象没有block属性(block属于Person对象),没有构成互相持有

@interface Person : NSObject

@property (nonatomic, copy) void(^myBlock)();

@end


@implementation Person

- (void)viewDidLoad
{
    MyObject *p = [[MyObject alloc] init];
    
    void(^myBlock)() = ^{
         NSLog(@"------%@", p);
    };

    myBlock();

    // MyObject对象在这里可以正常被释放
}

@end




Block错误使用:常见错误使用是,开发者担心循环引用错误(如上所述不会出现循环引用的情况),使用__weak。比如

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf doSomething];
});

因为将block作为参数传给dispatch_async时,系统会将block拷贝到堆上,而且block会持有block中用到的对象,因为dispatch_async并不知道block中对象会在什么时候被释放,为了确保系统调度执行block中的任务时其对象没有被意外释放掉,dispatch_async必须自己retain一次对象(即self),任务完成后再release对象(即self)。但这里使用__weak,使dispatch_async没有增加self的引用计数,这使得在系统在调度执行block之前,self可能已被销毁,但系统并不知道这个情况,导致block执行时访问已经被释放的self,而达不到预期的结果。



什么时候需要strongSelf???

在 doSomething 内,weakSelf 不会被释放。

__weak __typeof__(self) weakSelf = self;    
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

 [weakSelf doSomething];

});

但,下面的情况除外:

__weak __typeof__(self) weakSelf = self;  
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[weakSelf doSomething];
[weakSelf doOtherThing];

});

在 doSomething 中,weakSelf 不会变成 nil,不过在 doSomething 执行完成,调用第二个方法 doOtherThing 的时候,weakSelf 有可能被释放,于是,strongSelf 就派上用场了:

__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

__strong __typeof(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doOtherThing];

});
__strong 确保在 Block 内,strongSelf 不会被释放。


总结
1 在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。

2 如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。





参考:http://www.jianshu.com/p/14efa33b3562?utm_campaign=maleskine&utm_content=note&utm_medium=reader_share&utm_source=weibo

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值