文件和初始化
到目前為止,大多數開發者已經可以寫出簡單的Swift應用或者在Playground
實驗Swift語言的新特性。也許你也經歷過這種情況,當你將Playground
中運行正常的代碼拷貝到Swift源文件中卻發生了編譯錯誤,“這到底是怎么回事?Playground
文件和Swift源文件之間到底有什么不同?” 這篇文將告訴你們如何處理Swift項目中的各種文件,以及如何初始化全局數據。
應用中的文件
一個Swift應用必定會包含很多個源文件,基本上每個源文件中都有構成該應用的函數、類和其他一些申明等。Swift應用中的大多數源文件都是不需要按順序訪問的,都是無順序的,你甚至可以在某個模塊的最底部導入需要的源文件(雖然Swift不推薦這種編碼風格)。
不管怎樣,在大多數Swift的源文件中是不允許有最頂層級別的代碼的。這里解釋一下頂層代碼,任何寫在函數體、類之外,或被封裝供他人調用的可執行語句我們稱為頂層代碼。我們之所以不允許出現頂層代碼,是因為它會影響我們判斷程序是從哪里開始運行的。
Playground,REPL,頂層代碼
你可能會奇怪,為什么下面的代碼在Playground
中可以完美執行。由于它并沒有包含任何其他東西,所以它必然是頂層代碼:
println("Hello world")
上面的單行程序在沒有任何其他代碼的情況就可以正常運行,是因為Playground
支持執行頂層代碼。并且在Playground
中引入的文件或者代碼是按自上而下的順序執行的。比如說,你不能在定義某個類型之前去使用它。當然,在Swift的Playground
中也可以定義函數、類和其他在Swift中合法的任何類型,但并沒有必要這么做。Playground
的目的在于讓開發者們能更簡單、更快速的學習Swift語言和實驗新的API,而不用創建大量Swift源文件去做這些事。
除了Playground
,頂層代碼也可以在REPL
(Read-Eval-Print-Loop)中運行或作為腳本在Swift文件啟動時運行。通過腳本使用Swift時,你可以在終端中用#!/usr/bin/xcrun swift
或者xcrun swift myFile.swift
的方式使用Swift文件。
應用程序的入口與“main.swift”
你可能也注意到了,在上面的文章中,我們提到在大多數的源文件中是不允許使用頂層代碼的。但對一個文件除外,那就是main.siwft
文件,該文件的作用類似于Playground
,但是它是隨著你應用的其他源文件一起編譯的。main.swift
文件中允許頂層代碼并且執行順序是自上而下的。實際上,main.siwft
文件中的第一行代碼就默認為是程序的入口。正因為如此,所以我們才能看到在Swift最小的程序只有一行代碼,但它必須要在main.swift
文件中。
在Xcode中,Mac程序模板文件中就包含一個main.swift
文件,但在iOS程序的項目模板中是通過在Swift文件中添加@UIApplicationMain
標簽注明項目入口的。這樣做會讓編譯器忽略main.swift
入口文件,而將標注有@UIApplicationMain
標簽的文件當做入口文件。
全局變量
我們已經知道了Swift是如何判斷程序的執行入口,那么全局變量是如何工作的呢?下面的這行代碼,在運行時需要初始化嗎?
var someGlobal = foo()
在單文件的程序中,代碼是自上而下執行的,這類似于函數中變量的執行方式。這雖然看起來很簡單,但是在復雜的程序中我們就不是很好回答這個問題了。我們從下面三個方面來考慮:
- 限制初始化,像簡單的常量表達式,比如C語言。
- 任意初始化,在應用程序加載執行靜態構造函數時初始化,比如C++語言。
- 延遲初始化,當全局變量第一次被使用的時候初始化,比如Java語言。
我們基本排除第一種情況,因為在Swift中不需要像C語言中的常量表達式。在Swift中,常量通常是在函數調用的時候執行的(內聯)。而且也有更好的理由使用復雜的初始化方法,比如設置一個單例或者實例化一個字典。
第二種情況我們也基本排除,因為它在大型、復雜的程序中的效率很差。因為所有的初始化都要在應用程序啟動之前,但是我們無法預測初始化的順序,所以會有問題。
Swift采用第三種情況,這是最好的方法:允許自定義初始化,在程序啟動時不會因為要進行大量初始化而降低效率,并且我們也可以預知每次初始化完成的順序。
延遲初始化的全局變量(也包括結構體和枚舉中的靜態成員)是在第一次訪問他們的時候才初始化的,并且以dispatch_once
運行,確保了線程安全。你可以更酷的使用dispatch_once
:只需要申明一個全局變量并初始化,再將其訪問級別申明為private
。
總結
Swift語言的設計使得它可以很方便的在Playground
中進行試驗或快捷的編寫腳本。一個完整的程序可以只有一行代碼。當然,你也可以使用Swift編寫出各種復雜的應用程序。你可以通過main.swift
掌控各種初始化的完成時機,或者通過@UIApplicationMain
標簽指定你的iOS應用的程序入口。
本文首發地址:Swift中的文件和初始化