uiview
的首要問題就是既能從**中初始化,也能從xib
中初始化,兩者有何不同? uiview 是支援nscoding
協議的,當在 xib 或 storyboard 裡存在乙個 uiview 的時候,其實是將 uiview 序列化到檔案裡(xib 和 storyboard 都是以 xml 格式來儲存的),載入的時候反序列化出來,所以:
designated initializer
,理論上來講你繼承自 uiview 的任何子類,該方法最終都會被呼叫,但是有一些類在初始化的時候沒有遵守這個約定,如uiimageview
的initwithimage
和uitableviewcell
的initwithstyle: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 放置乙個背景,你可能會在initwithcoder
或awakefromnib
中這樣寫:
[self
addsubview:
self.backgroundview]; // 通過懶載入乙個背景 view,然後新增到檢視層級上
你的本意是在控制項的最下面放置乙個背景,卻有可能將這個背景覆蓋到控制項的最上方,原因是使用者可能會在 xib 裡寫入這個控制項,然後往它上面新增一些子檢視,這樣一來,使用者新增的這些子檢視會在你新增背景之前先進入檢視層級,你的背景被新增後就擋住了使用者的子檢視。如果你想支援使用者的這種操作,可以把addsubview
替換成insertsubview:atindex:
。
如果你要同時支援initwithframe
和initwithcoder
,那麼你可以提供乙個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 拷貝初始化 ...