OC基礎--構造方法 id類型,oc構造
new方法實現原理:
new做了三件事情
1.開闢儲存空間 + alloc 方法
2.初始化所有的屬性(成員變數) - init 方法
3.返回對象的地址
[Person new]; == [[Person alloc] init];
alloc: 1.開闢儲存空間 2.將所有的屬性設定為0 3.返回當前執行個體對象的地址
init: 1.初始化成員變數, 但是預設情況下init的實現是什麼都沒有做 2.返回初始化後的執行個體對象地址
注意: alloc返回的地址, 和init返回的地址是同一個地址
構造方法的概念及用途:
在OC中init開頭的方法, 我們稱之為構造方法
用於初始化一個對象, 讓某個對象一建立出來就擁有某些屬性和值
重寫init方法, 在init方法中初始化成員變數:
重寫init方法必須按照蘋果規定的格式重寫, 如果不按照規定會引發一些未知的錯誤
1.必須先初始化父類, 再初始化子類
子類繼承自父類 那麼子類擁有父類所有成員 子類必須調用父類的構造方法對這些成員進行初始化
2.必須判斷父類是否初始化成功, 只有父類初始化成功才能繼續初始化子類
為了防止父類的初始化方法release掉了self指向的空間並重新alloc了一塊空間。還有[super init]可能alloc失敗,這時就不再執行if中的語句
3.返回當前對象的地址
super 和 self 指向的是相同的訊息接收者 即誰調用就代表誰
- (instancetype)init { // 1.初始化父類 只要父類初始化成功 , 就會返回對應的地址, 如果初始化失敗, 就會返回nil nil == 0 == 假 == 沒有初始化成功 self = [super init]; // 2.判斷父類是否初始化成功 if (self != nil) { // 3.初始化子類 設定屬性的值 _age = 6; } // 4.返回地址 return self; }
簡版:
- (instancetype)init { // 注意: 不要把 = 號寫為 == 一定要將[super init]的傳回值賦值給self if (self = [super init]) { // 初始化子類 _age = 6; } return self; }
自訂構造方法:
自訂構造方法 其實就是自訂一個init方法:
1.一定是對象方法
2.一定返回id/instancetype
3.方法名稱一定以init開頭
#import "Person.h"@implementation Person// 重寫init方法- (instancetype)init{ if (self = [super init]) { self.name = @"王二小"; self.age = 12; } return self;}// 自訂構造方法 一個類可以有0個或者多個自訂構造方法- (instancetype)initWithName:(NSString *)name{ if (self = [super init]) { _name = name; } return self;}// 自訂構造方法可以有1個或多個參數- (instancetype)initWithName:(NSString *) name andAge:(int) age{ if (self = [super init]) { _name = name; _age = age; } return self;}@end
自訂構造方法在繼承中得表現:
誰聲明的成員就由誰去初始化(父類的屬性交給父類去處理 子類方法只處理自己專屬的屬性)
#import "Student.h" @implementation Student/*- (instancetype)initWithStudentNO:(NSString *)studentNO andName:(NSString *)name andAge:(int)age{ if (self = [super init]) { self.name = name; self.age = age; self.studentNO = studentNO; } return self;} */- (instancetype)initWithStudentNO:(NSString *)studentNO andName:(NSString *)name andAge:(int)age{ // 由父類的構造方法去初始化 name 和 age 屬性 if (self = [super initWithName:name andAge:age]) { _studentNO = studentNO; } return self;}@end
調用圖解:
自訂類Factory 方法:
什麼是類Factory 方法:
用於快速建立對象的類方法, 我們稱之為類Factory 方法
類Factory 方法中主要用於 給對象分配儲存空間和初始化這Block Storage空間
自訂類Factory 方法是蘋果的一個規範, 一般情況下, 我們會給一個類提供自訂構造方法和自訂類Factory 方法用於建立一個對象
規範:
1.一定是類方法 +
2.方法名稱以類的名稱開頭, 首字母小寫
3.一定有傳回值, 傳回值是id/instancetype
舉例:
[[NSString alloc] init]; [NSString string]; [[NSString alloc] initWithString:(NSString *)]; [NSString stringWithString:(NSString *)]; [[NSArray alloc] init]; [NSArray array]; [NSArray alloc] initWithObjects:(id), ..., nil]; [NSArray arrayWithObjects:(id), ..., nil];// 自訂類Factory 方法+ (instancetype)personWithName:(NSString *)name andAge:(int)age{ Person * person = [[Person alloc] init]; person.name = name; person.age = age; return person;}
自訂類Factory 方法在繼承中注意點:
由於子類預設會繼承父類所有的方法和屬性, 所以類Factory 方法也會被繼承
在類Factory 方法中建立對象一定不要使用類名來建立 一定要使用self來建立
self在類方法中就代表類對象 (誰調用當前方法, self就代表誰)
父類的類Factory 方法建立執行個體對象時是使用父類的類名建立的, 如果子類調用父類的類Factory 方法建立執行個體對象,建立出來的還是父類的執行個體對象
@interface Person : NSObject+ (id)person;@end @implementation Person+ (id)person{ return [[Person alloc] init];}@end @interface Student : Person@property NSString *name;@end @implementation Student @end int main(int argc, const char * argv[]){ Student *stu = [Student person];// 等效於 [[Person alloc] init] 需要該為 [[self alloc] init] [stu setName:@"lnj"]; // 報錯, 因為Person中沒有setName} // 自訂類Factory 方法 使用self 而不是 類名+ (instancetype)personWithName:(NSString *)name andAge:(int)age{ // Person * person = [[Person alloc] init]; Person * person = [[self alloc] init]; person.name = name; person.age = age; return person;}
id類型:
id是一個資料類型, 並且是一個動態資料類型
既然是資料類型, 所以就可以用來
1.定義變數
2.作為函數的參數
3.作為函數的傳回值
id == NSObject * 萬能指標
id和NSObject *的區別:
NSObject *是一個待用資料類型
id 是一個動態資料類型
預設情況下所有的資料類型都是待用資料類型
待用資料類型的特點:
在編譯時間就知道變數的類型,
知道變數中有哪些屬性和方法
在編譯的時候就可以訪問這些屬性和方法,
如果是通過待用資料類型定義變數, 如果訪問不了屬於待用資料類型的屬性和方法, 那麼編譯器就會報錯
動態資料類型的特點:
在編譯的時候編譯器並不知道變數的真實類型, 只有在啟動並執行時候才知道它的真實類型
如果通過動態資料類型定義變數, 如果訪問了不屬於動態資料類型的屬性和方法, 編譯器不會報錯
- 通過待用資料類型定義變數, 不能調用子類特有的方法
- 通過動態資料類型定義變數, 可以調用子類特有的方法
- 通過動態資料類型定義的變數, 可以調用私人方法
- 弊端: 由於動態資料類型可以調用任意方法, 所以有可能調用到不屬於自己的方法, 而編譯時間又不會報錯, 所以可能導致運行時的錯誤
- 應用情境: 多態, 可以減少代碼量, 避免調用子類特有的方法需要強制類型轉換
- 為了避免動態資料類型引發的運行時的錯誤, 一般情況下如果使用動態資料類型定義一個變數, 在調用這個對象的方法之前會進行一次判斷, 判斷當前對象是否能夠調用這個方法
id obj = [Student new]; // isKindOfClass , 判斷指定的對象是否是某一個類, 或者是某一個類的子類 if ([obj isKindOfClass:[Student class]]) { [obj eat]; }
id obj = [Student new]; if ([obj isMemberOfClass:[Student class]]) { // isMemberOfClass : 判斷指定的對象是否是當前指定的類的執行個體 [obj eat]; }
instancetype和id的區別:
instancetype == id == 萬能指標 == 指向一個對象
id在編譯的時候不能判斷對象的真實類型
instancetype在編譯的時候可以判斷對象的真實類型
(一個在編譯時間不知道真實類型, 一個在編譯時間知道真實類型)
id可以用來定義變數, 可以作為傳回值, 可以作為形參
instancetype只能用於作為傳回值
它會進行類型檢查,如果建立出來的對象,賦值了不相干的對象就會有一個警告資訊,防止出錯
注意: 以後但凡自訂構造方法, 傳回值盡量使用instancetype, 不要使用id