self vs" generic typet"停止延期时的差异

时间:2018-05-24 16:44:52

标签: ios runtime protocols swift-extensions class-extensions

我遇到了一个我不理解的有趣行为。以下是产生此行为的代码:

import UIKit

protocol UIViewNibLoading {
    static var nibName: String { get }
}

extension UIView : UIViewNibLoading {

    static var nibName: String {
        return String(describing: self)
    }

}

extension UIViewNibLoading where Self : UIView {

    static func loadFromNib<T: UIViewNibLoading>() -> T {
        print(T.nibName)
        print(nibName)
        return UINib(nibName: nibName, bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! T
        // CRASH: return UINib(nibName: T.nibName, bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! T
    }

}

这是执行此代码时控制台的输出:

UIView
MyCustomViewSubclass

当我在我的自定义类上调用loadFromNib方法时。它会产生两种不同的行为,具体取决于我如何获得nibName

  1. T.nibName:返回字符串UIView
  2. nibName:返回字符串MyCustomViewSubclass
  3. 你知道这里发生了什么吗?为什么selfT在运行时不是同一个对象?这是我发现的另一个有趣的事情。当您将断点放入nibName getter:

    时,您可以在调试器中看到以下内容

    T.nibName: T.nibName nibName: nibName

    这称为:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        if section == WidgetAddTableViewController.SectionIndexRecent {
            return WidgetAddHeaderView.loadFromNib()
        } else if section == WidgetAddTableViewController.SectionIndexFreeAndPremium {
            return WidgetAddFilterHeaderView.loadFromNib()
        }
        return nil
    }
    

    感谢您的任何解释。

1 个答案:

答案 0 :(得分:1)

self在运行时解析。 T在编译时解析。所以在编译时,你的代码行为如下:

let returnValue: UIView? = WidgetAddHeaderView.loadFromNib()
return returnValue

loadFromNib在其返回类型上是通用的。给定此代码,唯一有效的返回类型是UIView。同样,这是在编译时决定

另一方面,

self只是一个变量。它是一个如此略微特殊的变量,但它实际上只是一个变量。它具有运行时值。因此,type(of: self)在运行时进行评估。动态调度在运行时处理。

错误在于你并不是真的想要返回“一些符合UIViewNibLoading的未知T”(这就是你说通过使返回类型通用而返回)。你要返回的是Self,静态函数所属的类(在编译时确定)。所以你这样说:

extension UIViewNibLoading where Self : UIView {

    static func loadFromNib() -> Self {
        print(nibName)
        return UINib(nibName: nibName, bundle: nil)
            .instantiate(withOwner: nil, options: nil)[0] as! Self
    }   
}

或者你可以承诺更少(因为你的来电者实际上并不关心)并且这样做:

extension UIViewNibLoading where Self : UIView {

    static func loadFromNib() -> UIView {
        print(nibName)
        return UINib(nibName: nibName, bundle: nil)
            .instantiate(withOwner: nil, options: nil)[0]
    }
}

但没有理由让这种方法变得通用,事实上它就像你所见的那样伤害了你。