初探 iOS 中自定義 UIView 的初始化過程

由于種種原因,簡書等第三方平臺博客不再保證能夠同步更新,歡迎移步 GitHub:https://github.com/kingcos/Perspective/。謝謝!

awakeFromNib()
init(frame:)
init(coder:)

  • Info:
  • macOS 10.12.1
  • Xcode 8.2 Beta 1
  • Swift 3.0

Update

2017.03.07 - UIView 生命周期 Demo

由于本文之前雖有代碼,但沒有相應的 Demo,借探究 UIViewController 生命周期之際,加入了 UIView 生命周期的 Demo。您可以在 https://github.com/kingcos/UIViewController-UIView-LifecycleDemo 查看、下載。

2017.02.27 - CS193p Lecture 04

  • 通常,UIView 應盡可能避免重寫構造器。
  • init(frame:):純代碼(指定構造器);init(coder:):Storyboard(必需可失敗構造器)。若需要構造器,需要同時重寫這兩個構造器:
func setup() {
    // 如果本類有自定義變量,則此處不可初始化他們,
    // 因為只有當初始化后才能調用自己的方法。
}

override init(frame: CGRect) {
    super.init(frame: frame)

    // 初始化變量

    setup()
}

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

    // 初始化變量

    setup()
}
  • awakeFromNib() 只在使用 Storyboard 的 UIView 中被調用。
  • awakeFromNib() 并不是構造器,但它在初始化完成后立即被調用。
  • 所有 Storyboard 中繼承自 NSObject 的對象發送該消息。但順序是不確定的,因此不能在這里調用其他任何 Storyboard 中的對象。

前言

在 StoryBoard 和 Xib 出現之后,iOS UI 開發出現了三足鼎立之勢。本文不涉及 StoryBoard、Xib、純代碼的優劣之分。僅僅涉及幾個初始化方法:awakeFromNib() & init(frame:) & init(coder:),探討他們何時調用,為何調用。

Xib & Nib

ib 是 Interface Builder 的縮寫,即界面構造器。這里簡要說下,Xib 和 Nib 各是什么,有什么區別。

Xib 實際是一個 XML 文件,而 Nib 是二進制文件。當應用編譯時,Xib 文件被翻譯為 Nib。所以在 Xcode 中,我們可以自己新建 Xib 文件來構造 UI,而當編譯時,Xcode 會自動生成相應的 Nib 文件,而不需我們額外關注。關于其詳細介紹,您可以參考文末的資料。

OK! Talk is cheap, show me the code!

Demo

在下面的 Demo 中,統一將自定義 UIView 命名為 MyView。

MyView.swift

import UIKit

class MyView: UIView {
    // methods
}

Interface Builder

如果使用 Interface Builder 拖控件,那么其默認屬于 UIView 類型。為將其改為自定義控件,需要將 Utilities 中 Identity inspector 的 Custom Class 改為 MyView。

Custom Class 改為 MyView

為了方便看出調用順序,將 MyView.swift 改為如下:

import UIKit

class MyView: UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        print("init(frame:)")
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        print("init(coder:)")
        // fatalError("init(coder:) has not been implemented")
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        print("awakeFromNib()")
    }
}

之后運行即可在屏幕上看到該自定義 UIView,控制臺輸出:

init(coder:)
awakeFromNib()

小結

通過打印的輸出,可以看出使用 Interface Builder 載入 View 不會調用 init(frame:) 方法,而是調用了 init(coder:)init(coder:) 是 NSCoding 協議中的方法,NSCoding 是負責編碼解碼,歸檔處理的協議。

required init?(coder aDecoder: NSCoder)

代碼中的 init(coder:) 與平時見到的其他初始化方法有點不同:required 是指其為必要構造器,即子類「必須」重寫該構造器,但當父類的構造器可以完全滿足初始化時,也可不重寫。init? 是指其為可失敗構造器,即其可以 return nil 告知外界構造失敗。若想詳細了解 Swift 中的構造器,可以參考蘋果官方文檔。

init(coder:) 的調用處于 Nib 載入時,而 awakeFromNib() 的調用處于 Nib 載入后。Nib 的載入過程如下:

  1. Nib 文件內容和引用的資源文件加載到內存;
  2. 反歸檔存儲于 Nib 文件的圖像數據對象并初始化;
  3. 遵從 NSCoding 的對象(UIView & UIViewController)調用 init(coder:)
  4. 其他對象調用其他構造器方法
  5. 建立對象間連接:Outlet & Action
  6. 實現 awakeFromNib() 的對象調用該方法

需要注意的是,awakeFromNib() 中需要調用父類的該方法以保證父類的進行額外初始化。而在本例中重寫的 init(coder:) 目的主要是查看調用順序,并沒有加入特別的操作。因此在實際使用中,如果使用 Interface Builder,可以不重寫該方法。

Code

MyView.swift

import UIKit

class MyView: UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        print("init(frame:)")
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        print("awakeFromNib")
    }
}

ViewController.swift

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myView = MyView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
        myView.backgroundColor =  .black
        view.addSubview(myView)
    }
    
}

之后運行即可在屏幕上看到該自定義 UIView,控制臺輸出:

init(frame:)

小結

通過純代碼創建自定義 UIView,便只調用 init(frame:) 方法,不涉及 Nib 的方法,因此不會調用 awakeFromNib()init(coder:) 方法。而由于 init(coder:) 為必要構造器,因此重寫 init(frame:) 時,必須實現該方法。

有時,為了便于從 Interface Builder 和純代碼都能創建自定義 UIView 對象,可以將 init(coder:) 方法改為:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    // fatalError("init(coder:) has not been implemented")
}

若保留 fatalError(),則從 Nib 初始化時會無條件輸出語句并停止運行。

后記

可能是強迫癥作祟,學習中每遇到一個知識點,都想要查看官方文檔或者 Google 出為什么,然后自己敲代碼驗證,再總結出一篇文章,投稿給簡書、掘金。一篇文章有時要耗費一兩天,因為查閱的資料都是略有過時且幾乎全為英文,但自己挺享受這樣的學習狀態,也很享受分享給大家之后獲得的收藏所帶來的鼓勵。最近也看了很多實習生的招聘,現在深感基礎的重要,未來可能會傾向一些基礎,例如數據結構、算法、網絡等知識。也希望自己在寒假或下學期能找一份 iOS 實習,雖然自己也有所涉獵 Android 等其他的一些技術棧,但還是對 iOS 最感興趣。Come on!

參考資料

Nib Files
Initialization

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容