代码之家  ›  专栏  ›  技术社区  ›  Eddie

swift中的动态子类实例化

  •  0
  • Eddie  · 技术社区  · 6 年前

    我是从Objective-C来到斯威夫特的,Objective-C可以做很多事情,但斯威夫特要复杂得多。例如OOP动态初始化器。

    例如,我的代码在Objective-C中工作:

    @interface CommonVC: UIViewController
    + (instancetype)showFrom:(UIViewController *)vc;
    @end
    
    @implementation CommonVC
    
    + (instancetype)showFrom:(UIViewController *)vc {
        CommonVC *instance = [self instantiateFrom:vc.nibBundle];
        [vc presentViewController:instance animated:YES completion:nil];
        return instance;
    }
    
    // this is like convenience initializer.
    + (instancetype)instantiateFrom:(NSBundle *)aBundle {
        return [self.alloc initWithNibName:NSStringFromClass(self.class) bundle:aBundle];
    }
    
    @end
    
    @interface SubClassVC: CommonVC
    @end
    

    然后像这样使用子类或超类:

    SubClassVC *subVC = [SubClassVC showFrom:self];
    // or in swift:
    SubClassVC.show(from: self)
    

    然而,在swift中,实现这样的功能似乎是不可能的。我试过一些,但总是有编译错误。这里有一个:

    class CommonVC: UIViewController {
    
        class func show(from sender: UIViewController) -> Self {
            let vc = self(sender: sender) // Compiler error: Constructing an object of class type 'Self' with a metatype value must use a 'required' initializer
            sender.present(vc, animated: true, completion: nil)
            return unsafeDowncast(vc, to: self)
        }
    
        convenience init(sender: UIViewController) {
            self.init(nibName: type(of: self).className, bundle: sender.nibBundle)
            loadView()
        }
    }
    

    那么,如何从超类编写viewController的通用方便初始值设定项,然后用子类调用它呢?

    show(from:) 有一个不同的介绍,而不是这个简单的 present(_:animated:completion:)

    class CommonVC: UIViewController {
    
        class func show(from sender: UIViewController) -> Self {
            let vc = self.init(nibName: type(of: self).className, bundle: sender.nibBundle) // Compiler error: Constructing an object of class type 'Self' with a metatype value must use a 'required' initializer
            vc.setupAfterInitialize()
            sender.present(vc, animated: true, completion: nil)
            return unsafeDowncast(vc, to: self)
        }
    
        convenience init(sender: UIViewController) {
            self.init(nibName: type(of: self).className, bundle: sender.nibBundle)
            setupAfterInitialize()
        }
    
        internal func setupAfterInitialize() {
            // do stuff
            loadView()
        }
    }
    

    代码看起来很蠢,不方便初始化 convenience .

    现在,我不能使用class函数 显示(发件人:) 但要将演示文稿移到外面,制作如下内容:

    CommonVC.show(from: self)
    SubClassVC(sender: self).present()
    
    // instead of this simple presentation:
    // SubClassVC.show(from: self)
    

    class func show<T: CommonVC>(from sender: UIViewController) -> Self {
        T.init(nibName: type(of: self).className, bundle: sender.nibBundle)
        ....
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Pete Morris    6 年前

    当您从Objective-C转换到Swift时,很容易将Objective-C风格简单地转换为Swift代码。但斯威夫特在某些方面有本质的不同。

    一个很好的快速经验法则,来自苹果,是:“总是从一个协议开始”。。。

    实际上,使用协议和扩展很容易实现您想要的:

    protocol Showable {
        init(className: String, bundle: Bundle?)
        static func show(from: UIViewController) -> Self
    }
    
    extension Showable where Self: UIViewController {
    
        init(className: String, bundle: Bundle?) {
            self.init(nibName: className, bundle: bundle)
        }
    
        static func show(from: UIViewController) -> Self {
            let nibName = String(describing: self)
            let instance = self.init(className: nibName, bundle: from.nibBundle)
            from.present(instance, animated: true, completion: nil)
            return instance
        }
    
    }
    

    在上面的代码中,我声明了 Showable 协议和提供默认实现的扩展,该实现应用于采用类是 UIViewController .

    最后,将此功能提供给

    extension UIViewController: Showable { }
    

    添加了这两个简短的代码片段后,您现在可以执行问题中描述的操作(只要视图控制器实例存在一个适当命名的nib):

    let secondController = SecondViewController.show(from: self)
    ThirdController.show(from: secondController)
    

    这就是斯威夫特的魅力所在。你所有的 UIView控制器 子类现在免费获得这个功能;不需要继承。