Swift中的全局初始化与viewDidLoad初始化之间是否存在差异?

时间:2016-12-29 22:59:36

标签: swift scope initialization viewdidload

如果我有一个类并初始化一个这样的变量:

class TestClass: UIViewController {
    var thisInt: Int = 10
}

与初始化不同:

class TestClass: UIViewController {
    var thisInt: Int!

    override func viewDidLoad() {
        super.viewDidLoad()
        thisInt = 10
    }
}

我想我的主要问题在于全局初始化发生的时间,并且有时候一个人被调用的次数超过另一次调用普通的iOS编程(没有对本地开发做任何大的事情)。我明白在viewDidLoad中这样做会限制我使用弱或可选,但我更关心其他任何差异。

2 个答案:

答案 0 :(得分:3)

您可以通过添加"计算"轻松找到答案。视图控制器的属性:

class TestClass: UIViewController {
    let a = {()->Int in print("global initialization"); return 10 }()
}

并添加

print("didFinishLaunching")

在app的委托didFinishLaunchingWithOptions方法中。

您获得的订单是

global initialization
didFinishLaunching

这意味着全局初始化程序在应用程序生命周期开始之前运行。

现在,为了更进一步,您可以添加一个包含以下内容的main.swift文件

print("Before UIApplicationMain")

UIApplicationMain(CommandLine.argc, unsafeBitCast(CommandLine.unsafeArgv, to: UnsafeMutablePointer<UnsafeMutablePointer<Int8>>.self), nil, NSStringFromClass(AppDelegate.self))

并从AppDelegate类中删除(或评论)@UIApplicationMain装饰。这将指示编译器使用main.swift中的代码进行应用程序初始化,而不是讨论中装饰器提供的默认行为(尽管我们提供了一个自定义的几乎相同的实现)。

你在第二种方法中得到的是

Before UIApplicationMain
global initialization
didFinishLaunching

表示在加载情节提要时执行实例属性代码。

现在,为了获得更多洞察力,让我们尝试找出静态实例变量之间的差异。为此,我们将添加一个测试类:

class PropertyInitializationTest {
    static let staticProp = {()->String in print("static initialization of InitializationTest"); return "" }()
    let instanceProp = {()->String in print("instance initialization of InitializationTest"); return "" }()

    init() {
        print("In initializer")
    }
}

,更新AppDelegate确实完成了启动:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        print("didFinishLaunching")
        print("before instantiating InitializationTest")
        _ = PropertyInitializationTest()
        print("after instantiating InitializationTest")
        // Override point for customization after application launch.
        return true
    }

我们获得的输出是:

Before UIApplicationMain
global initialization
didFinishLaunching
before instantiating InitializationTest
instance initialization of InitializationTest
In initializer
after instantiating InitializationTest

,它确认实例属性在实例化类时以及在任何初始化代码运行之前设置。

但是等等!静态属性怎么样?没有迹象表明它已经初始化了。看起来静态属性根据定义是惰性的,只有在第一次访问时才会初始化。

更新应用确实完成启动代码确认了这一点。

print("didFinishLaunching")
print("before instantiating InitializationTest")
_ = PropertyInitializationTest()
print("after instantiating InitializationTest")
_ = PropertyInitializationTest.staticProp
print("after instantiating InitializationTest")

给出以下输出:

Before UIApplicationMain
global initialization
didFinishLaunching
before instantiating InitializationTest
instance initialization of InitializationTest
In initializer
after instantiating InitializationTest
static initialization of InitializationTest
after instantiating InitializationTest

总结:

  • 实例属性在类初始化程序运行之前接收编译时值(如果已设置),因此在该类的任何代码执行之前
  • 静态属性只有在首次访问时才会收到它们的值,它们本质上是懒惰的

答案 1 :(得分:1)

是的,有差异。虽然您的两个示例在技术上都可行,但您的第一个代码段是最常见的。

我将向您展示一个示例,当您确实需要按照第二个代码段中的描述实现它时:

class ImageDisplayingViewController: UIViewController {
    @IBOutlet weak var thumbImageView: UIImageView!

    var choosenImageTag: Int!
    var choosenImage: UIImage!

    override func viewDidLoad() {
        super.viewDidLoad()
        thumbImageView.tag = imageTag
        thumbImageView.image = image
    }
}

假设您有一个视图控制器,您可以在其中选择图像,然后导航到下一个视图控制器以显示此图像(和标记,以便在示例中保留Int)。您可以通过调用:

prepare(for segue:, sender:)中传递该图像和标记
destinationViewController.choosenImage = choosenImage
destinationViewController.choosenImageTag = 10

然后,ImageDisplayingViewController实际上会以viewDidLoad()方式加载图片,您可以确保自己的网点已初始化。

如果您只是尝试直接在prepare(for segue:, sender:)方法中加载图片,则会出现崩溃,因为网点尚未初始化。

destinationViewController.thumbImageView.image = choosenImage  // crash
destinationViewController.thumbImageView.tag = 10