選擇正確的初始化方式

2021-07-22 03:45:23 字數 3559 閱讀 5507

uiview的首要問題就是既能從**中初始化,也能從xib中初始化,兩者有何不同? uiview 是支援nscoding協議的,當在 xib 或 storyboard 裡存在乙個 uiview 的時候,其實是將 uiview 序列化到檔案裡(xib 和 storyboard 都是以 xml 格式來儲存的),載入的時候反序列化出來,所以:

雖然 initwithframe 是 uiview 的designated initializer,理論上來講你繼承自 uiview 的任何子類,該方法最終都會被呼叫,但是有一些類在初始化的時候沒有遵守這個約定,如uiimageviewinitwithimageuitableviewcellinitwithstyle:reuseidentifier: 的構造器等,所以我們在寫自定義控制項的時候,最好只假設父檢視的 designated initializer 被呼叫。

如果控制項在初始化或者在使用之前必須有一些引數要設定,那我們可以寫自己的 designated initializer 構造器,如:

- (instancetype)initwithname:(nsstring *)name;
- (instancetype)initwithname:(nsstring *)name 

- (instancetype)initwithname:(nsstring *)name frame:(cgrect)frame

return

self;

}

並且你要考慮到,因為你的控制項是繼承自 uiview 或 uicontrol 的,那麼使用者完全可以不使用你提供的構造器,而直接呼叫基類的構造器,所以最好重寫父類的 designated initializer,使它呼叫你提供的 designated initializer ,比如父類是個 uiview:

- (instancetype)initwithframe:(cgrect)frame
這樣當使用者從**裡初始化你的控制項的時候,就總是逃脫不了你需要執行的初始化**了,哪怕使用者直接呼叫init方法,最終還是會回到父類的 designated initializer 上。

從xib或者storyboard中載入

當控制項從 xib 或 storyboard 中載入的時候,情況就變得複雜了,首先我們知道有 initwithcoder 方法,該方法會在物件被反序列化的時候呼叫,比如從檔案載入乙個 uiview 的時候:

uiview *view = [[uiview alloc] init];

nsdata *data = [nskeyedarchiver archiveddatawithrootobject:view];

[[nsuserdefaults standarduserdefaults] setobject:data forkey:@"keyview"];

[[nsuserdefaults standarduserdefaults] synchronize];

data = [[nsuserdefaults standarduserdefaults] objectforkey:@"keyview"];

view = [nskeyedunarchiver unarchiveobjectwithdata:data];

nslog(@"%@", view);

執行unarchiveobjectwithdata的時候,initwithcoder會被呼叫,那麼你有可能會在這個方法裡做一些初始化工作,比如恢復到儲存之前的狀態,當然前提是需要在encodewithcoder中預先儲存下來。

不過我們很少會自己直接把乙個 view 儲存到檔案中,一般是在 xib 或 storyboard 中寫乙個 view,然後讓系統來完成反序列化的工作,此時在initwithcoder呼叫之後,awakefromnib方法也會被執行,既然在awakefromnib方法裡也能做初始化操作,那我們如何抉擇?

一般來說要盡量在initwithcoder中做初始化操作,畢竟這是最合理的地方,只要你的控制項支援序列化,那麼它就能在任何被反序列化的時候執行初始化操作,這裡適合做全域性資料、狀態的初始化工作,也適合手動新增子檢視。

awakefromnib相較於initwithcoder的優勢是:當awakefromnib執行的時候,各種iboutlet也都連線好了;而initwithcoder呼叫的時候,雖然子檢視已經被新增到檢視層級中,但是還沒有引用。如果你是基於 xib 或 storyboard 建立的控制項,那麼你可能需要對 iboutlet 連線的子控制項進行初始化工作,這種情況下,你只能在awakefromnib裡進行處理。同時 xib 或 storyboard 對靈活性是有打折的,因為它們建立的**無法被繼承,所以當你選擇用 xib 或 storyboard 來實現乙個控制項的時候,你已經不需要對靈活性有很高的要求了,唯一要做的是要保證使用者一定是通過 xib 建立的此控制項,否則可能是乙個空的檢視,可以在initwithframe裡放置乙個斷言或者異常來通知控制項的使用者。

最後還要注意檢視層級的問題,比如你要給 view 放置乙個背景,你可能會在initwithcoderawakefromnib中這樣寫:

[self

addsubview:

self.backgroundview]; // 通過懶載入乙個背景 view,然後新增到檢視層級上

你的本意是在控制項的最下面放置乙個背景,卻有可能將這個背景覆蓋到控制項的最上方,原因是使用者可能會在 xib 裡寫入這個控制項,然後往它上面新增一些子檢視,這樣一來,使用者新增的這些子檢視會在你新增背景之前先進入檢視層級,你的背景被新增後就擋住了使用者的子檢視。如果你想支援使用者的這種操作,可以把addsubview替換成insertsubview:atindex:

如果你要同時支援initwithframeinitwithcoder,那麼你可以提供乙個commoninit方法來做統一的初始化:

- (id)initwithcoder:(nscoder *)adecoder 

return

self;

}- (id)initwithframe:(cgrect)frame

return

self;

}- (void)commoninit

awakefromnib方法裡就不要再去呼叫commoninit了。

C 初始化方式

變數被預設初始化由變數型別和定義變數的位置決定,如果內建型別定義的變數在任何函式體之外即全域性變數,則被預設初始化為相關型別的預設值,如int型預設值為0,如果定義的變數位置在任何函式體之內即區域性變數,則不被初始化,乙個未被初始化的變數是未定義的,將會報錯。如果使用等號 初始化乙個變數,實際上執行...

變數初始化的方式

當物件在建立時獲得了乙個特定的值,我們說這個物件被初始化了。用於初始化變數的值可以是任意複雜的表示式。當一次定義了兩個或多個變數時,物件的名字在定義後就馬上可以使用了。例如我們可以這樣使用 使用剛剛定義的price初始化discount double price 109.99,discount pr...

C 類內初始化方式的選擇細節

以下所有栗子未經特殊說明,全部實現於c 11下 在c 中有很多種初始化方式,如下栗子 string s0 預設初始化 string s1 s0 拷貝初始化 string s2 hello world 直接初始化 string s3 列表初始化 string s4 hello world 拷貝初始化 ...