博客> Block到底什么时候用weakSelf
Block到底什么时候用weakSelf
2017-11-21 18:36 评论:0 阅读:1119 早起的虫子被鸟吃
基础巩固

retainCycleDemo 欢迎下载并star哦 原文出处 转载注明出处 谢谢

从引用计数开始

iOS里内存管理是通过引用计数来确定对象是否应该被销毁。对象被引用(retain),引用计数(retainCount)+1,对象使用完了,要释放(release),release会使引用计数-1,当对象的retainCount == 0时,对象将被销毁(delloc).

插曲

我并没有经历MRC的时代,入行IOS就是就是ARC了,但是有时候一些奇怪的BUG还是应该从OC的底层去考虑。因此,需要补充一下OC中的AutoReleasePool的相关知识了。AutoReleasePool由系统控制,定时的对放入其中的对象进行release操作,这里就引出了strong和weak修饰符的真实作用了,strong指针在系统轮询释放的时候,保持retainCount,而weak指针修饰对象的retainCount将被-1.用OC在main文件里面,你就会发现这个秘密。虽然swift里看不见了,但是swift底层还是OC。

关于MRC(manage retain count),就是你自己管理retain count了,自己插入retain,release和自己通过autoRelease放进自动释放池,当然了你还可以强制的调delloc将对象销毁。

ARC时代,你再也不用将精力花费在内存管理上了,因为编译器自动的帮你插入了这些内存管理代码,但是,并不意味着你就可以高枕无忧了。

正式的内容

关于block,出门右拐Block for iOS

对象能被正确的销毁很重要,否则内存管理将出大问题!

利用block反向传值,push到下一个界面,并输入内容,然后返回上一个界面,同时显示输入的内容。

displayController:

class displayViewController: UIViewController {

    @IBOutlet weak var clickBtn: UIButton!
    @IBOutlet weak var displayLabel: UILabel!
    var inputCon : textFiledViewController!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        inputCon = textFiledViewController()
    }

    @IBAction func buttonClicked(_ sender: UIButton) {
        inputCon.textBlock = {[weak self] text in
            self?.displayLabel.text = text
        }
        self.navigationController?.pushViewController(inputCon, animated: true)
    }

    deinit {
        print("delloc")
    }

}

textFiledViewController:

class textFiledViewController: UIViewController {

    var inputBox : UITextField!
    var textBlock : ((_ text:String)->Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.cyan
        // Do any additional setup after loading the view.
        inputBox = UITextField(frame: CGRect(x: 50, y: 100, width: 200, height: 44))
        inputBox.backgroundColor = UIColor.white
        view.addSubview(inputBox)
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if textBlock != nil {
            textBlock!(inputBox.text!)
        }
    }
}

在dispalayController中,如果block回调处,不使用weak,就会导致循环引用。 因为在displayController中textFiledViewController是作为属性存在的,是一个strong类型指针(原谅我,没用OC演示)。在block会调处,block会捕获self以及displayLabel,而在textFiledViewController中,block又是其copy属性,这下麻烦了,循环引用了,任何时候,这两个控制器所生成的对象的retainCount都不为0,他们将无法被销毁了。

image

所以,为了打破这种循环,我们需要将指向self的指针变成weak类型,swift里面很简单,[weak self]即可。

inputCon.textBlock = {[weak self] text in
            self?.displayLabel.text = text
        }

进一步探讨

是不是所有的block都要用[weak self]呢?

当然不是,使用过Masonry和SnapKit的同学可能都知道,在这样的block里面,不用weak也很不错。

OK,OK其实原理还是上面的,没有形成循环引用,自然就不用多此一举了。

以SnapKit布局为例:

view.snp.makeConstraints { (make) in
            make.right.equalTo(self.view.snp.right).offset(-10)
            make.top.equalTo(self.view.snp.top).offset(80)
            make.size.equalTo(CGSize(width: Int(MENU_ITEM_W), height: Int(height)))
        }

为啥不会循环引用呢?明明view是controller的strong属性,block又捕获了self和view。

因为snapKit的block不是view的copy属性,因此,这个闭环没法形成。

用代码来解释会比较清楚,我们来仿snapKit/Masonry来写一个加法计算得东东吧。

caculatorMake:

-------.h
@interface caculatorMake : NSObject

@property (nonatomic, assign) int result;

-(caculatorMake *(^)(int))add;

@end

-------.m
import "caculatorMake.h"

@implementation caculatorMake
-(caculatorMake *(^)(int))add{

    return ^caculatorMake *(int value){
        _result += value;
        return self;
    };
}

NSObject+caculatorManager:

-------.h
#import 
@class caculatorMake;
@interface NSObject (caculatorManager)
+(int)makeCaculators:(void(^)(caculatorMake *make))caculatorMaker;
@end

-------.m
 import "caculatorMake.h"

@implementation NSObject (caculatorManager)

+(int)makeCaculators:(void(^)(caculatorMake *make))caculatorMaker{
    caculatorMake *manager = [[caculatorMake alloc] init];
    caculatorMaker(manager);
    return manager.result;
}

@end

test:

-----.h
@interface test : NSObject

-(void)doTest;

@end

-----.m
@interface test ()

@property (nonatomic, assign) int num1;
@property (nonatomic, assign) int num2;

@end

@implementation test

-(void)doTest{
    _num1 = 1;
    _num2 = 2;
    int result = [NSObject makeCaculators:^(caculatorMake *make) {
        make.add(_num1).add(_num2);
    }];
    NSLog(@"%i", result);
}

@end

看到了吧,这里block只是一个stackBlock,纯粹为了计算而存在.它都活不出它的作用域。所以,如果你想通过block实现循环引用,就在正式内容处那样搞,并且不用weakSelf,保证让你循环引用。

而其他的,当block只是stackBlock的时候,并不会导致循环引用。

PS:block分为stackBlock和mallocBlock,更多内容,出门右拐。

Tips

好吧,你说的我都知道了,那我怎么代码那么多,我怎么确定我没有循环引用呢?

OC:在控制器的-(void)delloc里面打印一下 swift:调deinit函数

好吧,控制器很多?我不想每个控制器都写这么个玩意儿~~~OK,OK你需要methodSwizzling。出门右拐ios rintime里有RumTime for iOS

觉得还可以,咱们互粉一下吧?

收藏
3
sina weixin mail 回到顶部