Использование 'self' в функциях расширения класса в Swift




(2)

Я ищу возможность извлечь экземпляр подкласса UIView из Nib.

Я хотел бы иметь возможность вызывать MyCustomView.instantiateFromNib () и иметь экземпляр MyCustomView. Я почти готов просто перенести рабочий код Objective-C, который у меня есть, через заголовок моста, но решил, что сначала попробую идиоматический подход. Это было два часа назад.

extension UIView {
    class func instantiateFromNib() -> Self? {

        let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)

        for topLevelObject in topLevelObjects {
            if (topLevelObject is self) {
                return topLevelObject
            }
        }

        return nil
    }
}

Теперь if (topLevelObject is self) { неверно, потому что "Ожидаемый тип после 'is'". То, что я попробовал после этого, показывает многое из того, что я не понимаю в системе типов Swift.

  • if (topLevelObject is Self) {
  • if (topLevelObject is self.dynamicType) {
  • if (topLevelObject is self.self) {
  • Миллион других вариаций, которые даже не ошибаются .

Любое понимание приветствуется.


Используя подход из Как я могу создать экземпляры подклассов управляемых объектов в расширении NSManagedObject Swift? Вы можете определить универсальный вспомогательный метод, который выводит тип self из вызывающего контекста:

extension UIView {

    class func instantiateFromNib() -> Self? {
        return instantiateFromNibHelper()
    }

    private class func instantiateFromNibHelper<T>() -> T? {
        let topLevelObjects = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)

        for topLevelObject in topLevelObjects {
            if let object = topLevelObject as? T {
                return object
            }
        }
        return nil
    }
}

Это компилируется и работает, как и ожидалось в моем быстром тесте. Если MyCustomView является вашим подклассом UIView то

if let customView = MyCustomView.instantiateFromNib() {
    // `customView` is a `MyCustomView`
    // ...
} else {
    // Not found in Nib file
}

дает вам экземпляр MyCustomView , и тип выводится автоматически.

Обновление для Swift 3:

extension UIView {

    class func instantiateFromNib() -> Self? {
        return instantiateFromNibHelper()
    }

    private class func instantiateFromNibHelper<T>() -> T? {
        if let topLevelObjects = Bundle.main.loadNibNamed("CustomViews", owner: nil, options: nil) {
            for topLevelObject in topLevelObjects {
                if let object = topLevelObject as? T {
                    return object
                }
            }
        }
        return nil
    }
}

Я считаю, что условное выражение, которое вы ищете, это topLevelObject.dynamicType == self

Комбинируя это с unsafeBitCast (который, согласно собственной документации Apple, «нарушает гарантии системы типов Swift») , мы можем принудительно topLevelObject до типа self . Это должно быть безопасно, потому что мы уже убедились, что topLevelObject имеет тот же тип, что и self

Это один из способов обойти вспомогательный метод с использованием обобщений, описанных Мартином Р.

extension UIView {
    class func instantiateFromNib() -> Self? {

        let bundle = NSBundle.mainBundle().loadNibNamed("CustomViews", owner: nil, options: nil)

        for topLevelObject in topLevelObjects {
            if topLevelObject.dynamicType == self {
                return unsafeBitCast(topLevelObject, self)
            }
        }
        return nil
    }
}

Обратите внимание, что Apple также говорит в своей документации для unsafeBitCast :

Почти всегда есть лучший способ сделать что-нибудь.

Так что будьте осторожны!





swift