【问题标题】:What's the difference between these two declarations of delegates?这两个代表声明有什么区别?
【发布时间】:2021-08-19 09:58:13
【问题描述】:

我正在学习代表,但我不明白为什么

let friendsFunctionsDelegate = FriendsFunctionsDelegate()
let friendsFunctions = FriendsFunctions()

friendsFunctionsDelegate.delegate = friendsFunctions

是正确的,而

let friendsFunctionsDelegate = FriendsFunctionsDelegate()
friendsFunctionsDelegate.delegate = FriendsFunction()

错了。

这里是完整的代码:

protocol FriendsDelegate: AnyObject {
    func orderPizza()
    func takeABreak()
}

class FriendsFunctionsDelegate {
    weak var delegate: FriendsDelegate? = nil
    
    func buyPizza() {
        delegate?.orderPizza()
    }
    
    func sleep() {
        delegate?.takeABreak()
    }
}

class FriendsFunctions: FriendsDelegate {
    func orderPizza() {
        print("I ordered a pizza")
    }
    
    func takeABreak() {
        print("I'm going to sleep")
    }
}

let friendsFunctionsDelegate = FriendsFunctionsDelegate()
let friendsFunctions = FriendsFunctions()

friendsFunctionsDelegate.delegate = friendsFunctions

【问题讨论】:

    标签: swift delegates swift-protocols


    【解决方案1】:

    ARC(用于自动引用计数)

    ARC 是自动管理对象的内存分配/释放的系统。

    它的工作方式是,只要您的代码中有至少 1 个 对对象的强引用,它就会保留在内存中请注意,默认情况下,任何未定义为弱的属性定义都是隐式强的。

    当指定一个属性为弱时,它不会将“持有引用计数”加一。

    您遇到的问题...

    在您的 first 示例中,当您第一次创建 let 常量时,您持有对它的 strong 引用,然后将其分配给弱变量。 然后你有 =>

    1. 对对象的1个弱引用(存储在friendsFunctionsDelegate.delegate上)
    2. 1 对对象的强引用(let 常量let friendsFunctions = FriendsFunctions()

    因此,ARC 释放对象(强引用 >= 1)=> 它正在工作 ✅

    在您的 second 示例中,当您直接实例化 + 将委托分配给弱变量 WITHOUT 时,首先创建一个常量。 然后你有 =>

    1. 对对象的1个弱引用(存储在friendsFunctionsDelegate.delegate上)
    2. 0 对对象的强引用

    因此,ARC 在分配后直接解除分配(从内存中释放)对象(强引用 == 0)=> 不工作 ❌

    结论

    作为结论,您需要在某处保留对该委托对象的强引用。

    委托模式中的弱用法

    在使用委托时,我们使用弱属性来防止内存保留循环。当您有一个对象(对象 A)持有对另一个对象(对象 B)的强引用时,就会发生这种情况,而另一个对象(对象 B)也对第一个对象(对象 A)具有强引用。

    • A => B 强
    • B => 强者

    => ⚠️保留周期⚠️

    当您尝试从内存中删除对象 A 或 B 时,强引用计数仍为 1,然后您会出现内存泄漏,内存中充满了未使用的对象,这可能会导致应用可用。解决方案是将这两个引用之一定义为弱(不是两者)。应用委托模式时,您会将持有对委托的引用的属性定义为弱。

    几个额外的 cmets:

    1. 注意可能会误导的命名。委托关键字应该只附加到协议而不是类名。如果你删除它,虽然你会有重叠,这暗示命名可以更恰当地定义。
    2. 当您将属性定义为可选并且您希望它默认为零时,您不必显式指定= nil。虽然你仍然可以做到;)
    3. 最佳实践告诉您,当您希望类符合委托/协议时,您应该使用扩展而不是直接符合类定义。
    4. 委托模式应该是一种盲目的交流模式,所以你的班级不应该知道超出范围的函数。然后将委托方法的命名修改为func didBuyPizza()func didTakeABreak() 或类似名称。

    【讨论】:

    • 感谢您的洞察力和额外的 cmets。我刚刚从我正在学习的课程的操场上复制了这段代码,因此了解有关这些概念的更多信息非常有用。
    【解决方案2】:

    那个

    let friendsFunctions = FriendsFunctions() // holds a strong reference
    friendsFunctionsDelegate.delegate = friendsFunctions
    

    因为它具有强引用而工作,而这

    friendsFunctionsDelegate.delegate = FriendsFunction()
    

    不要因为两个部分(lhs & rhs)都是weak,所以不会发生保留(delegateweak 属性)

    【讨论】:

      【解决方案3】:

      注意FriendsFunctionsDelegate.delegate 属性是一个弱引用:

      weak var delegate: FriendsDelegate? = nil
      ^^^^
      

      如果你这样做了

      friendsFunctionsDelegate.delegate = FriendsFunction()
      

      您创建了一个FriendsFunction 对象,唯一引用它的对象是friendsFunctionsDelegate,通过它的delegate 属性。但这是一个弱参考。没有对新的FriendsFunction 对象的强引用!因此,它会在创建后立即被释放。

      您应该在此处看到一条警告:

      实例将被立即解除分配,因为属性“委托”是“弱”

      另一方面,如果先将新创建的FriendsFunction对象放入let常量中,

      let friendsFunctions = FriendsFunctions()
      

      let 常量,friendsFunctions 将是对FriendsFunctions 对象的强引用,因为它不是weak。而且由于至少有一个强引用,所以在所有强引用消失之前,不会释放该对象。

      有关详细信息,请参阅 Swift 指南中的 Automatic Reference Counting

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-06-07
        • 2012-04-01
        • 2016-05-08
        • 2016-08-01
        • 1970-01-01
        相关资源
        最近更新 更多