代码之家  ›  专栏  ›  技术社区  ›  M A Russel

在没有[weak self]的情况下调用可选闭包安全吗?

  •  0
  • M A Russel  · 技术社区  · 1 年前

    我有两节课 A. 和类 B 在课堂上 A. 我已经声明了一个可选闭包,并从类中 B 我称之为关闭。课堂上 B ,你可以看到 callTapA 我调用闭包的函数 [软弱的自我] 为了安全地释放引用,但在 callTapB 函数我正在调用可选闭包并分配一个函数。两者都是正确的,但如何 [软弱的自我] 管理在 callTapB ? 这样使用盖子安全吗?

    class A {
          var onTapA: ((_ name: String) -> Void)?
    
          init(onTapA: ((_ name: String) -> Void)? = nil) {
              self.onTapA = onTapA
          }
    }
    
    class B {
         var newName:String = ""
         let a:A
    
         init(a: A) {
             self.a = a
        
             callTapA()
             callTapB()
         }
    
        
        func callTapA() {
            a.onTapA = { [weak self] name in
                guard let self = self else { return }
                newName = name
            }
        }
        
        func callTapB() {
            a.onTapA = onTapB
        }
        
        func onTapB(name: String) {
            newName = name
        }
    
    }
    
    1 回复  |  直到 1 年前
        1
  •  0
  •   matt    1 年前

    这样使用盖子安全吗

    如果你使用 callTapB :

    • B实例以A实例作为参数进行初始化,并将其存储在强引用(属性)中。
    • 那么 callTapB() 出现并将B实例函数存储在a(属性)的强引用中。

    因此,A和B实例相互保留;这是一个保留周期和内存泄漏。

    如果你只使用 callTapA 另一方面,事实并非如此;两者都是有序发布的实例。

    通过修改代码以包含测试工具和 deinit 实施。我是这样做的:

    import UIKit
    
    class ViewController: UIViewController {
    
        class A {
            var onTapA: ((_ name: String) -> Void)?
    
            init(onTapA: ((_ name: String) -> Void)? = nil) {
                self.onTapA = onTapA
            }
    
            deinit {
                print("farewell from A")
            }
        }
    
        class B {
            var newName:String = ""
            let a:A
    
            init(a: A) {
                self.a = a
    
                // callTapA()
                // callTapB()
            }
    
    
            func callTapA() {
                a.onTapA = { [weak self] name in
                    guard let self = self else { return }
                    newName = name
                }
            }
    
            func callTapB() {
                a.onTapA = onTapB
            }
    
            func onTapB(name: String) {
                newName = name
            }
    
            deinit {
                print("farewell from B")
            }
        }
    
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                let theB = B(a: A())
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                    print(theB)
                }
            }
        }
    }
    

    运行代码两次,一次只使用 callTapA() 未评论,另一个只是 callTapB() 未评论。你会看到,第一种方式,A和B都会有序地消失,但第二种方式,两者都不会。

    请注意,事实上 onTapA 是可选的。它允许您设置 onTapA 初始化后,它与内存的管理方式无关。