iOS ARC:使用 deinit 保留循环?

iOS ARC: retain cycle with deinit?

iOS 保留周期:

典型的例子是A有B,B有A。

那如果A有B&C,B有C呢?

是否还保留循环?

当保留循环存在时,deinit 不会被 ARC 调用是否有很强的关系?

示例代码如下:

class ViewModel{
    
    
    func doSth(){
        print("123")
    }
    
    
    deinit{
        print("ViewModel")
    }
}


class CustomView: UIView{
    
    var viewModel: ViewModel?
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        viewModel?.doSth()
    }
    
    
    deinit{
        print("CustomView")
    }
}


class ViewController: UIViewController {

    var viewModel = ViewModel()
    
    lazy var widget: CustomView = {
        let v = CustomView(frame: CGRect(origin: .zero, size: CGSize(width: 150, height: 150)))
        v.backgroundColor = UIColor.red
        v.viewModel = viewModel
        return v
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(widget)
        
    }

    deinit{
        print("ViewController")
    }
}

ViewController弹出时,上面的三个deinit方法都被调用了。

所以没有保留周期。

是吗?

判断是否有保留循环的一个简单方法是画一个图表。使用箭头表示强引用,使用虚线箭头表示 weak/unowned 个引用。

如果您可以沿着实线箭头从一个对象返回到同一对象,那么您就有了一个保留周期。

在你的例子中,A 拥有 B 和 C,B 也拥有 C,有 2 个拥有对 C 的引用,但没有“向后”强引用。您无法循环回到 A 或 B,因此没有保留周期。

闭包是另一个不明显的保留循环的潜在来源。如果你有一个引用“self”的“转义”闭包,并且你持有对该闭包的强引用,那么你就有一个保留周期,因为你有一个对闭包和闭包的拥有引用,通过它的 self 引用, 对你有很强的参考意义。

编辑:

我教新开发人员避免保留循环的方法是根据“前向”和“后向”引用来思考。前向引用要强,后向引用要弱

如果你想到一个双向链表,前向指针应该是强的,而后向指针应该是弱的。

如果您拥有一个对象,您指向它的指针应该是强指针。如果您是该对象的代表,请将代表指针视为向后指针。应该是弱点。