Using 'self' in class extension functions in Swift

前端 未结 2 508
执念已碎
执念已碎 2020-11-28 14:14

I\'m looking to be able to pull out an instance of a UIView subclass from a Nib.

I\'d like to be able to call MyCustomView.instantiateFromNib() and have an instance

相关标签:
2条回答
  • 2020-11-28 14:30

    Using the approach from How can I create instances of managed object subclasses in a NSManagedObject Swift extension? you can define a generic helper method which infers the type of self from the calling context:

    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
        }
    }
    

    This compiles and works as expected in my quick test. If MyCustomView is your UIView subclass then

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

    gives you an instance of MyCustomView, and the type is inferred automatically.


    Update for 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
        }
    }
    
    0 讨论(0)
  • 2020-11-28 14:36

    I believe the conditional expression you're looking for is topLevelObject.dynamicType == self

    Combining this with unsafeBitCast (which, by Apple's own documentation, "Breaks the guarantees of Swift's type system"), we can forcefully downcast topLevelObject to self's type. This should be safe because we already made sure that topLevelObject is the same type as self

    This is one way to get around the helper method using generics that Martin R described.

    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
        }
    }
    

    Note that Apple also says in their documentation for unsafeBitCast:

    There's almost always a better way to do anything.

    So be careful!

    0 讨论(0)
提交回复
热议问题