关于ios:Swift 3.1弃用了initialize()。

Swift 3.1 deprecates initialize(). How can I achieve the same thing?

(P)Objective-C declares a class function,EDOCX1 commercial 0,that is run once for each class,before it is used.它被用作交换方法执行(SWIZLING)的一个切入点,贯穿其他事项。(p)(P)Swift 3.1 precates this function with a warning:(p)布尔奇1(P)如何才能解决这一问题,同时保持同样的行为和特点,即我目前正在使用EDOCX1(英文)字母"Enry Point"进行工作?(p)


简单/简单的解决方案

常见的应用程序入口点是应用程序委托的applicationDidFinishLaunching。我们可以简单地向每个类添加一个静态函数,我们希望在初始化时通知它,然后从这里调用它。

第一个解决方案简单易懂。对于大多数情况,这是我的建议。尽管下一个解决方案提供的结果与最初的initialize()功能更相似,但它也会导致应用程序启动时间稍长。我不再认为在大多数情况下,值得付出努力、性能降低或代码复杂性。简单代码就是好代码。

继续阅读另一个选项。你可能有理由需要它(或者它的一部分)。

不是那么简单的解决方案

第一个解决方案不一定能很好地扩展。如果您正在构建一个框架,您希望代码在其中运行,而无需任何人从应用程序委托调用它,该怎么办?

第一步

定义以下swift代码。目的是为您希望灌输类似于initialize()的行为的任何类提供一个简单的入口点-现在只需符合SelfAware即可实现。它还提供了一个单独的函数来为每个一致性类运行这个行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protocol SelfAware: class {
    static func awake()
}

class NothingToSeeHere {

    static func harmlessFunction() {

        let typeCount = Int(objc_getClassList(nil, 0))
        let types = UnsafeMutablePointer<AnyClass?>.allocate(capacity: typeCount)
        let autoreleasingTypes = AutoreleasingUnsafeMutablePointer<AnyClass?>(types)
        objc_getClassList(autoreleasingTypes, Int32(typeCount))
        for index in 0 ..< typeCount { (types[index] as? SelfAware.Type)?.awake() }
        types.deallocate(capacity: typeCount)

    }

}

第二步

这一切都很好,但是我们仍然需要一种方法来在应用程序启动时实际运行我们定义的函数,即NothingToSeeHere.harmlessFunction()。在此之前,此答案建议使用Objective-C代码来完成此操作。然而,似乎我们只需要使用swift就可以做我们需要的事情。对于不能使用uiapplication的MacOS或其他平台,需要更改以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
extension UIApplication {

    private static let runOnce: Void = {
        NothingToSeeHere.harmlessFunction()
    }()

    override open var next: UIResponder? {
        // Called before applicationDidFinishLaunching
        UIApplication.runOnce
        return super.next
    }

}

步骤三

现在我们有了一个应用程序启动的入口点,以及一种从您选择的类中连接到这个入口点的方法。剩下要做的就是:不执行initialize(),而是遵循SelfAware并实现定义的方法awake()


我的方法基本上与ADIB的方法相同。下面是一个使用核心数据的桌面应用程序的示例;这里的目标是在任何代码提到它之前注册我们的自定义转换器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    override init() {
        super.init()
        AppDelegate.doInitialize
    }

    static let doInitialize : Void = {
        // set up transformer
        ValueTransformer.setValueTransformer(DateToDayOfWeekTransformer(), forName: .DateToDayOfWeekTransformer)
    }()

    // ...
}

好的是,这对任何类都有效,就像initialize所做的那样,前提是您涵盖了所有的基础——也就是说,必须实现每个初始值设定项。下面是一个文本视图的示例,它在任何实例出现在屏幕上之前配置自己的外观代理;该示例是人工的,但封装非常好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CustomTextView : UITextView {

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame:frame, textContainer: textContainer)
        CustomTextView.doInitialize
    }

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

    static let doInitialize : Void = {
        CustomTextView.appearance().backgroundColor = .green
    }()

}

这说明了这种方法的优势比应用程序委托好得多。只有一个应用委托实例,所以这个问题不是很有趣;但是可以有很多CustomTextView实例。然而,行CustomTextView.appearance().backgroundColor = .green将只执行一次,因为它是静态属性的初始值设定项的一部分。这与类方法initialize的行为非常相似。


如果您想修复您的方法,以纯粹的快速方式旋转:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public protocol SwizzlingInjection: class {
    static func inject()
}

class SwizzlingHelper {

    private static let doOnce: Any? = {
        UILabel.inject()
        return nil
    }()

    static func enableInjection() {
        _ = SwizzlingHelper.doOnce
    }
}

extension UIApplication {

    override open var next: UIResponder? {
        // Called before applicationDidFinishLaunching
        SwizzlingHelper.enableInjection()
        return super.next
    }

}

extension UILabel: SwizzlingInjection
{
    public static func inject() {
        // make sure this isn't a subclass
        guard self === UILabel.self else { return }

        // Do your own method_exchangeImplementations(originalMethod, swizzledMethod) here

    }
}

由于objc_getClassList是objective-c,它不能得到超类(如uilabel),而只能得到所有子类,但是对于uikit相关的swizzing,我们只想在超类上运行一次。只需对每个目标类运行inject(),而不是循环整个项目类。


稍微增加@jordansmith的优秀课程,确保每个awake()只被称为一次:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protocol SelfAware: class {
    static func awake()
}

@objc class NothingToSeeHere: NSObject {

    private static let doOnce: Any? = {
        _harmlessFunction()
    }()

    static func harmlessFunction() {
        _ = NothingToSeeHere.doOnce
    }

    private static func _harmlessFunction() {
        let typeCount = Int(objc_getClassList(nil, 0))
        let types = UnsafeMutablePointer<AnyClass?>.allocate(capacity: typeCount)
        let autoreleasingTypes = AutoreleasingUnsafeMutablePointer<AnyClass?>(types)
        objc_getClassList(autoreleasingTypes, Int32(typeCount))
        for index in 0 ..< typeCount { (types[index] as? SelfAware.Type)?.awake() }
        types.deallocate(capacity: typeCount)
    }
}

您还可以使用静态变量,因为这些变量已经是惰性的,并在顶级对象的初始值设定项中引用它们。这对于没有应用程序委托的应用程序扩展之类的应用程序很有用。

1
2
3
4
5
6
7
8
9
10
class Foo {
    static let classInit : () = {
        // do your global initialization here
    }()

    init() {
        // just reference it so that the variable is initialized
        Foo.classInit
    }
}


如果你喜欢纯迅捷?!那么我对这类事情的解决方案是在_UIApplicationMainPreparations时间开始:

1
2
3
4
5
6
7
@UIApplicationMain
private final class OurAppDelegate: FunctionalApplicationDelegate {
    // OurAppDelegate() constructs these at _UIApplicationMainPreparations time
    private let allHandlers: [ApplicationDelegateHandler] = [
        WindowHandler(),
        FeedbackHandler(),
        ...

这里的模式是,我通过将UIApplicationDelegate分解为各个处理程序可以采用的各种协议来避免大规模的应用程序委托问题,以防您感到疑惑。但重要的一点是,尽早开始工作的一个简单快捷的方法是在初始化@UIApplicationMain类时调度您的+initialize类型的任务,就像在这里构造allHandlers一样。对于大多数人来说,江户时间应该足够早!


  • 把你的班级标记为@objc
  • NSObject继承
  • 将objc类别添加到类中
  • 分类实施initialize
  • 例子

    SWIFT文件:

    1
    2
    3
    4
    //MyClass.swift
    @objc class MyClass : NSObject
    {
    }

    Objc文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //MyClass+ObjC.h
    #import"MyClass-Swift.h"

    @interface MyClass (ObjC)

    @end

    //MyClass+ObjC.m
    #import"MyClass+ObjC.h"

    @implement MyClass (ObjC)

    + (void)initialize {
        [super initialize];
    }

    @end

    我认为这是一种权宜之计。

    我们还可以在Objective-C代码中编写initialize()函数,然后通过桥接引用使用它。

    希望最好的方法……