【问题标题】:Swift - Objective-C load class method?Swift - Objective-C 加载类方法?
【发布时间】:2014-09-13 22:15:18
【问题描述】:

在 Objective-C 中,NSObject 有一个名为 load 的类方法,该方法在第一次加载类时被调用。 Swift 中的等价物是什么?

@implementation MyClass

+ (void)load
{
   [self registerClass];
}

@end

【问题讨论】:

    标签: objective-c swift


    【解决方案1】:

    斯威夫特 5

    方法'load()'定义了Objective-C类方法'load',Swift不允许这样做

    您现在不能覆盖load() 方法,但是有一种合法的方法可以在应用启动时调用您的swift 类的自定义swift_load() 方法。需要使 ObjC 类将其自己的 load() 方法重定向到您的 swift 类。

    只需将下一个SwiftLoader.m 文件添加到您的项目中:

    // SwiftLoader.m
    
    @interface SwiftLoader : NSObject
    
    @end
    
    @implementation SwiftLoader
    
    + (void)load {
        SEL selector = @selector(swift_load);
        
        int numClasses = objc_getClassList(NULL, 0);
        Class* classes = (Class *)malloc(sizeof(Class) * numClasses);
        numClasses = objc_getClassList(classes, numClasses);
        
        for (int i = 0; i < numClasses; i++) {
            Class class = classes[i];
            Method method = class_getClassMethod(class, selector);
            if (method != NULL) {
                IMP imp = method_getImplementation(method);
                ((id (*)(Class, SEL))imp)(class, selector);
            }
        }
        free(classes);
    }
    

    现在您可以将swift_load 类方法添加到您的任何 swift 类中以进行设置任务:

    class MyClass {
        @objc
        class func swift_load() {
            print("load")
        }
    }
    

    【讨论】:

      【解决方案2】:

      this PR 中删除了使用此方法的功能。

      它提供了一些理由:

      Swift 的语言模型不保证会真正使用类型元数据,这使得覆盖 initialize() 容易出错,并且并不比手动调用初始化函数更好。为 Swift 3 的兼容性发出警告,并拒绝在 Swift 4 中覆盖 +initialize 的尝试。

      【讨论】:

        【解决方案3】:

        Swift 中的等价物是什么?

        没有。

        与存储实例属性不同,您必须始终为存储类型属性赋予默认值。这是因为类型本身没有初始化器,可以在初始化时为存储的类型属性赋值。

        来源:“Swift 编程语言”,Apple 出品

        类型在 Swift 中根本没有初始化器。正如这里的几个答案所建议的那样,您可以通过使用 Objective-C 桥并让您的 Swift 类继承自 NSObject 或使用 Objective-C 加载程序引导代码来解决这个问题,但如果您有一个 100% 的 Swift 项目,你基本上只有两种选择:

        1. 在代码中的某处手动初始化您的类:
        class MyCoolClass {
        
            static func initClass ( ) {
                // Do your init stuff here...
            }
        }
        
        
        // Somewhere in a method/function that is guaranteed to be called
        // on app startup or at some other relevant event:
        MyCoolClass.initClass()
        
        1. 在所有需要初始化类的方法中使用一次运行模式:
        class MyCoolClass {
        
            private static var classInitialized: Bool = {
                 // Do your init stuff here...
                 // This code will for sure only run once!
                 return true
            }()
        
            private static func ensureClassIsInitialized ( ) { 
                _ = self.classInitialized 
            }
        
        
            func whateverA ( ... ) {
                MyCoolClass.ensureClassIsInitialized()
                // Do something...
            }
        
            func whateverB ( ... ) {
                MyCoolClass.ensureClassIsInitialized()
                // Do something...
            }
        
        
            func whateverC ( ... ) {
                MyCoolClass.ensureClassIsInitialized()
                // Do something...
            }
        
        }
        

        这遵循一个干净的模式,即 Swift 中没有代码“自动”运行,代码只有在其他代码指示它运行时才会运行,这提供了清晰和可跟踪的代码流。

        对于特殊情况可能有更好的解决方案,但您尚未提供任何信息说明您为什么需要该功能。

        【讨论】:

          【解决方案4】:

          在 Swift 1.2 之前:

          override class func load() {
             NSLog("load");
          }
          

          编辑:

          从 Swift 1.2 开始,您不能再覆盖 load 方法。请查看 initialize 方法,但它的行为与加载不同,它在第一次在某处引用该类时被调用,而不是在应用程序初始加载时调用

          【讨论】:

          • 请注意,这仅适用于您的班级来自NSObject
          • @JackLawrence:它对我有用。 import Foundation @objc class MyClass { class func load() { println("sdfsdf") } }
          • 这不再有效。您将收到错误“方法 'load()' 定义了 Objective-C 类方法 'load',这在 Swift 中是不允许的”
          • Swift 2.1:internal override class func initialize() { } 用于 NSObject 子类。我没有发现 initialize() 可以在纯 Swift 对象中使用。
          • 在 Swift 中不再允许覆盖 initialize
          【解决方案5】:

          在 Swift 2.0 中,请使用此方法

          public override class func initialize()
          

          【讨论】:

          • @VladimirKofman 它将在您的类第一次使用时被调用,这不一定是在加载时。但是,该类必须是 @objc
          【解决方案6】:

          我得到了 swift 1.2~5 可行方法的结论:

          Doable      |swift-load |swift-initialze|bridgeToOC-load|bridgeToOC-initialze|
          ---         |----       |---            |---            |---                 |
          swift1.2~4.2|X          |O              |O              |O                   |
          swift4.2~5.0|X          |X              |O              |O                   |
          swift5.0~?  |X          |X              |X              |O                   |
          

          以及如何制作?看这里
          https://medium.com/post-mortem/using-nsobjects-load-and-initialize-from-swift-f6f2c6d9aad0


          但我认为这是另一种让它更快的方法。在没有加载/初始化的情况下使用运行时和协议。

          http://jordansmith.io/handling-the-deprecation-of-initialize/

          【讨论】:

            【解决方案7】:

            更新:从 Swift 5 开始,Swift 类上的类扩展和类别不允许有 +load 方法,不过 +initialize 似乎没有被禁止。

            虽然 Swift 不提供直接等效,但使用 Objective-C 和类别可以相对优雅地实现这一点。 Foo 仍应继承自 NSObject 并具有类/静态非私有 swiftyLoad/Initialize 方法,这些方法可从 Loader.m 中的 Objective-C 调用,您将其与 Foo.swift 一起包含在编译源中:

            # Loader.m
            
            #import <Foundation/Foundation.h>
            #import <MyProject/MyProject-Swift.h>
            
            // This was a more concise solution in Swift 4.2 and earlier. If you try
            // this with Swift 5 you'd get "Swift class extensions and categories
            // on Swift classes are not allowed to have +load methods" runtime error.
            
            // @implementation Foo (private)
            //     + (void)load { [self swiftyLoad]; }
            //     + (void)initialize { [self swiftyInitialize]; }
            // @end
            
            // This is what you'd want to use with Swift 5 onwards, basically 
            // just a pure Objective-C defined class.
            
            @interface Loader : NSObject
            @end
            
            @implementation Loader : NSObject
                + (void)load { [Foo swiftyLoad]; }
                + (void)initialize { [Foo swiftyInitialize]; }
            @end
            
            
            # Foo.swift
            
            import Foundation
            public class Foo: NSObject {
                @objc public static func swiftyLoad() {
                    Swift.print("Rock 'n' roll!")
                }
                @objc public static func swiftyInitialize() {
                    Swift.print("Hello initialize!")
                }
            }
            

            最好的部分是不需要弄乱桥接头或其他任何东西,它就可以工作。有几个陷阱,尤其是在静态库中使用这种方法时,请查看 Medium 上的完整 post 了解详细信息! ✌️

            【讨论】:

            • NSObject noo :(
            • 这不再适用于 swift5。 medium.com/post-mortem/…
            • 中篇文章不久前已针对 Swift 5 进行了更新。我在几个生产应用程序中使用它。还更新了答案,再试一次。
            【解决方案8】:

            对于 Swift 2 或 3(即后 Swift 1.2),您可以使用:

            class MySwiftClass: NSObject {
                internal override class func initialize() {
                    DoStuff()
                    super.initialize()
                }
            }
            

            但是,如您所见,您的类需要(直接或间接)继承自 NSObject。这是必需的,因为 initialize() 由 ObjC 运行时调用。

            并且initialize()方法只会在MySwiftClass被引用时被调用。所以不会像load()那么神奇。

            但它也会更安全。例如:包含一个框架(比如说,只需将其添加到您的Podfile)不会让框架在您的应用程序启动后神秘地开始运行,而无需在您的应用程序中添加一行代码项目(至少……我希望!?)。

            【讨论】:

              【解决方案9】:

              在 Swift 1.2 中删除了对覆盖加载的支持

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2021-11-26
                • 2019-01-02
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多