【问题标题】:Getting the actual type from a generic return type从泛型返回类型获取实际类型
【发布时间】:2017-06-07 06:02:33
【问题描述】:

我在UIView 上有一个扩展,它加载了特定类型和名称的笔尖:

extension UIView {

    class func fromNib<T : UIView>() -> T? {

        guard let nib = Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0]
            else {
                return nil
        }
        return nib as? T
    }
}

现在在我的Hint 类(它是UIView 的子类)中,我像这样使用上述扩展:

 if let view = Hint.fromNib(){
        self.addSubview(view)
        view.frame = self.bounds         
 }

这不起作用,因为T.self 的结果始终是 UIView,即使 Hint 是调用 fromNib 方法的类。如果我尝试对这样的类进行硬编码:

Bundle(for: Hint.self).loadNibNamed(String(describing: Hint.self), owner: nil, options: nil)

然后一切正常。如何获取实际调用fromNib() 方法的类的类型名称?

这是我的 Hint 类:

class Hint:UIView {

     required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        if let view = UIView.fromNib(){
            self.addSubview(view)
            view.frame = self.bounds

        }

    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        if let view = UIView.fromNib(){
            self.addSubview(view)
            view.frame = self.bounds
        }
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        if let view = UIView.fromNib(){
            self.addSubview(view)
            view.frame = self.bounds
        }
    }

}

所以一切都很简单。一个 nib 文件只有一个标签和一个图像视图,但我想这与当前问题几乎无关。

【问题讨论】:

  • 试试let hint: Hint = UIView.fromNib()。我遇到了同样的问题。
  • 在调用方法之前有没有试过定义类型?即if let view: Hint = Hint.fromNib()
  • 哎呀.. Emilio 以 10 秒的优势击败了我
  • @EmilioPelaez 我刚刚尝试过,所以现在它不会崩溃,但是从笔尖加载的视图不可见(使用我提供的代码)......必须放置一些断点。 ..
  • @Whirlwind 我详细说明了一个答案。我认为使用Hint? 可能会更好,但我的方法没有使用可选参数所以我没有尝试过。

标签: ios swift generics uiview xib


【解决方案1】:

我用完全相同的方法遇到了完全相同的问题,甚至命名相同。我无法使用当前的类类型,因为编译器假定它是 UIView 而不是它的子类。

发生的情况是Hint.fromNib() 最终与UIView.fromNib() 完全相同,因为T 不依赖于它被调用的类,因此T 被推断为UIView。你要做的就是通过指定你期望得到什么样的视图来帮助编译器推断出正确的类型。

let hint: Hint? = UIView.fromNib()

我要补充一点,当您正确使用 guard 时,first[0] 更安全,所以我会将您的方法更改为如下所示:

let bundle = Bundle(for: T.self)
let nib = bundle.loadNibNamed(String(describing: T.self), owner: nil, options: nil)
guard let view = nib?.first as? T else {
  return nil
}
return view

【讨论】:

  • 好的,我也会试试你的代码。此外,这一行 let bundle = Bundle(for: T.self) 不会编译。需要对 AnyClass 进行显式转换...编译器建议这样做:let bundle = Bundle(for: T.self as! AnyClass)
  • 遗憾的是,结果相同。我只会得到想要的结果,即 1) 将笔尖的内容加载到我的自定义视图中并在设计时渲染(在故事板中可见),以及 2) 当然在运行时加载,如果我硬编码 Hint.self 而不是 @987654335 @
  • 我已经添加了一个我的 Hint 类的实现......我希望它可以用来解决这个问题。
【解决方案2】:

你想知道fromNib()被调用的类的类型,即类方法中的self

extension UIView {
    class func fromNib<T : UIView>() -> T? {
        guard
            let nibs = Bundle(for: self).loadNibNamed(String(describing: self), owner: nil, options: nil),
            let nib = nibs.first
            else {
                return nil
        }

        return nib as? T
    }
}

以下内容:

if let viewFromXib = MyXib.fromNib(){            
    view.addSubview(viewFromXib)
    viewFromXib.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: 30, height: 30))
    print(viewFromXib)
}

作品和印刷品:

<TESTS.MyXib: 0x7f9531408aa0; frame = (0 0; 30 30); autoresize = W+H; layer = <CALayer: 0x60000002b8e0>>

我不太明白返回类型 T 是如何推断出来的。


更新:

我正在研究如何推断返回类型 T,答案是:它没有。

这个:

class func fromNib() -> UIView? {
    guard
        let nibs = Bundle(for: self).loadNibNamed(String(describing: self), owner: nil, options: nil),
        let nib = nibs.first
        else {
            return nil
    }

    return nib as? UIView
}

是等价的。

所以即使这个答案是在解决问题,它并没有回答问题。

【讨论】:

  • 谢谢!也是好问题!
  • 用更多(也许是肤浅的)想法更新了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-11
  • 2011-05-11
  • 2013-09-13
  • 2016-10-16
相关资源
最近更新 更多