標籤:
1. property
- 基本使用 - - - 編譯器只要看到@property, 就會自動產生某一個屬性的getter/setter方法的聲明
2. @synthesize
- @synthesize是一個編譯器指令, 它可以簡化我們getter/setter方法的實現
- 什麼是實現: 在聲明後面寫上大括弧就代表著實現
- 在@synthesize後面告訴編譯器, 需要實現哪個@property產生的聲明
- 告訴@synthesize, 需要將傳入的值賦值給誰和返回誰的值給調用者
- 如果在@synthesize後面沒有告訴系統將傳入的值賦值給誰, 系統預設會賦值給和@synthesize後面寫得名稱相同的成員變數
3. property的增強:
- 預設@property會將傳入的屬性賦值給_開頭的成員變數
- @property有一個弊端: 它只會產生最簡單的getter/setter方法的聲明和實現, 並不會對傳入的資料進行過濾
- 如果想對傳入的資料進行過濾, 那麼我們就必須重寫getter/setter方法
- 如果重寫了setter方法, 那麼property就只會產生getter方法
- 如果重寫了getter方法, 那麼property就只會產生setter方法
- 如果同時重寫了getter/setter方法, 那麼property就不會自動幫我們產生私人的成員變數
- 如果不想對傳入的資料進行過濾, 僅僅是提供一個方法給外界操作成員變數, 那麼就可以使用@property
- 如果利用@property來產生getter/setter方法, 那麼我們可以不寫成員變數, 系統會自動給我們產生一個_開頭的成員變數
- 注意: @property自動幫我們產生的成員變數是一個私人的成員變數, 也就是說是在.m檔案中產生的, 而不是在.h檔案中產生的
- property的修飾符
- 如果給一個屬性同時提供了getter/setter方法, 那麼我們稱這個屬性為可讀可寫屬性(readwrite — 預設就是這個)
- 如果只提供了getter方法, 那麼我們稱這個屬性為唯讀屬性(readonly)
- 如果只提供了setter方法, 那麼我們稱這個屬性為唯寫屬性(readonly)
- 如果既沒有提供getter也沒有提供setter方法, 那麼我們稱這個屬性為私人屬性
- 程式員之間有一個約定, 一般情況下擷取BOOL類型的屬性的值, 我們都會將擷取的方法名稱改為isXXX
- @property(getter=isMarried) BOOL married; // 是否已婚
- 這麼寫, married的getter方法名, 就是isMarried
- 同理, 也可以用setter=方法名, 把setter的方法名也重寫了
5. id (動態資料類型)
- id是一個資料類型, 並且是一個動態資料類型
- 預設情況下所有的資料類型都是待用資料類型
- 待用資料類型的特點:
- 在編譯時間就知道變數的類型,
- 知道變數中有哪些屬性和方法
- 在編譯的時候就可以訪問這些屬性和方法,
- 並且如果是通過待用資料類型定義變數, 如果訪問了不屬於待用資料類型的屬性和方法, 那麼編譯器就會報錯
- 動態資料類型的特點:
- 在編譯的時候編譯器並不知道變數的真實類型, 只有在啟動並執行時候才知道它的真實類型
- 並且如果通過動態資料類型定義變數, 如果訪問了不屬於動態資料類型的屬性和方法, 編譯器不會報錯
- id == NSObject * 萬能指標
- id和NSObject *的區別:
- NSObject *是一個待用資料類型
- id 是一個動態資料類型
- 通過待用資料類型定義變數, 不能調用子類特有的方法
- 通過動態資料類型定義變數, 可以調用子類特有的方法
- 通過動態資料類型定義的變數, 可以調用私人方法
- 弊端: 由於動態資料類型可以調用任意方法, 所以有可能調用到不屬於自己的方法, 而編譯時間又不會報錯, 所以可能導致運行時的錯誤
- 應用情境: 多態, 可以減少代碼量, 避免調用子類特有的方法需要強制類型轉換
- 為了避免動態資料類型引發的運行時的錯誤, 一般情況下如果使用動態資料類型定義一個變數, 在調用這個對象的方法之前會進行一次判斷, 判斷當前對象是否能夠調用這個方法
id obj = [Student new]; /* if ([obj isKindOfClass:[Student class]]) { // isKindOfClass , 判斷指定的對象是否是某一個類, 或者是某一個類的子類 [obj eat]; } */ if ([obj isMemberOfClass:[Student class]]) { // isMemberOfClass : 判斷指定的對象是否是當前指定的類的執行個體 [obj eat]; }
6. new方法實現原理
- new做了三件事情
- 開闢儲存空間 + alloc 方法
- 初始化所有的屬性(成員變數) - init 方法
- 返回對象的地址
- 樣本
// alloc做了什麼事情: 1.開闢儲存空間 2.將所有的屬性設定為0 3.返回當前執行個體對象的地址 Person *p1 = [Person alloc]; // 1.初始化成員變數, 但是預設情況下init的實現是什麼都沒有做 2.返回初始化後的執行個體對象地址 Person *p2 = [p1 init]; // [[Person alloc] init]; // 注意: alloc返回的地址, 和init返回的地址是同一個地址 NSLog(@"p1 = %p, p2 = %p", p1, p2);
7. 構造方法基本概念
- 在OC中init開頭的方法, 我們稱之為構造方法
- 構造方法的用途: 用於初始化一個對象, 讓某個對象一建立出來就擁有某些屬性和值
- 重寫init方法, 在init方法中初始化成員變數
- 注意: 重寫init方法必須按照蘋果規定的格式重寫, 如果不按照規定會引發一些未知的錯誤
- 必須先初始化父類, 再初始化子類
- 必須判斷父類是否初始化成功, 只有父類初始化成功才能繼續初始化子類
- 返回當前對象的地址
- 一定要將[super init]的傳回值賦值給self
8. instancetype和id的區別
instancetype == id == 萬能指標 == 指向一個對象
- id在編譯的時候不能判斷對象的真實類型
- instancetype在編譯的時候可以判斷對象的真實類型
id和instancetype除了一個在編譯時間不知道真實類型, 一個在編譯時間知道真實類型以外, 還有一個區別
- id可以用來定義變數, 可以作為傳回值, 可以作為形參
- instancetype只能用於作為傳回值
注意: 以後但凡自訂構造方法, 傳回值盡量使用instancetype, 不要使用id
9. 自訂構造方法
- 自訂構造方法:
- 其實就是自訂一個init方法
- 一定是對象方法
- 一定返回id/instancetype
- 方法名稱一定以init開頭
- (instancetype)initWithAge:(int)age;
- 一個類可以有0個或者多個自訂構造方法
- 自訂構造方法可以有1個或多個參數
- (instancetype)initWithAge:(int)age andName:(NSString *)name;
- 自訂構造方法在繼承中的表現
- 注意: 自己的事情自己做 ( 屬性是在哪個類裡定義的, 就應該由這個類始終負責賦值 )
- 自訂構造方法init的順序: 子類init => 父類init => NSObject的 init => NSObject的return => 父類的return => 子類的return
- 注意: 屬性名稱, 不要以new開頭, 有可能引發未知錯誤 ; 方法名也不要以new開頭
11. 類Factory 方法的基本概念
- 什麼是類Factory 方法:
- 用於快速建立對象的類方法, 我們稱之為類Factory 方法
- 類Factory 方法中主要用於 給對象分配儲存空間和初始化這Block Storage空間
- 規範:
- 一定是類方法 +
- 方法名稱以類的名稱開頭, 首字母小寫
- 一定有傳回值, 傳回值是id/instancetype
- 自訂類Factory 方法是蘋果的一個規範, 一般情況下, 我們會給一個類提供自訂構造方法和自訂類Factory 方法用於建立一個對象
- 類Factory 方法在繼承中的注意點
- 注意: 但凡自訂類Factory 方法, 在類Factory 方法中建立對象, 一定要用self來建立
- self在類方法就代表類對象, 到底代表哪個類對象?? 誰調用當前方法, self就代表誰
13. 類的本質
- 所有類的”類對象”的繼承關係就是”元類對象”的繼承關係
- 每個對象都有isa,
- 執行個體對象isa => 類對象
- 類對象isa => 元類對象
- 元類對象isa => 根元類對象
- 根元類對象isa => 根元類對象(指向它自己) 根元類對象, 就是NSObject
14.類對象的擷取和使用情境
- 如何擷取類對象
- 文法: Class test = [類名 class];
- 注意: 一個類在記憶體中只有一分類對象
- 類對象的應用情境
15.類的啟動過程
- 只要程式啟動, 就會將類的代碼載入到記憶體中. 放到代碼區
- load方法會在當前類被載入到記憶體的時候調用, 有且僅會調用一次
- 如果存在繼承關係, 會先調用父類的load方法, 再調用子類的load方法
+ (void) load {}
- initialize方法在當前類第一次被使用的時候就會調用(建立類對象的時候)
- initialize方法在整個程式的運行過程中只會被調用一次, 無論你使用多少次這個類都只會調用一次
- initialize用於對某一個類進行一次性的初始化
+ (void) initialize { }
16. SEL類型
- SEL類型的第一個作用, 配合對象/類來檢查對象/類中有沒有實現某一個方法
- respondToSelector 判斷某個對象有沒有實現規定的方法 (會根據調用對象自動識別對象方法和類方法)
SEL sel = @selector(setAge:); Person *p = [Person new]; // 判斷p對象中有沒有實現-號開頭的setAge:方法 //如果P對象實現了setAge:方法,那麼就會返回YES //如果P對象沒有實現setAge:方法,那麼就會返回NO BOOL flag = [p respondsToSelector:sell]; NSLog(@"flag = %i",flag); // respondsToSelector注意點: 如果是通過一個對象來調用該方法, 那麼會判斷該對象有沒有實現-號開頭的方法 // 如果是通過類來調用該方法, 那麼會判斷該類有沒有實現+號開頭的方法 SEL sel1 = @selector(test); flag = [p respondsToSelector:sel1]; NSLog(@"flag = %i",flag); flag = [Person respondsToSelector:sel1]; NSLog(@"flag = %i",flag);
- SEL類型的第二個作用, 配合對象/類來調用某一個SEL方法
- performSelector 只能傳對象, 可以傳0 ~ 2個參數
SEL sel = @selector(demo); Person *p = [Person new]; // 調用p對象中sel類型對應的方法 [p performSelector:sel1]; SEL sel1 = @selector(signalWithNumber:); // withObject: 需要傳遞的參數 // 注意: 如果通過performSelector調用有參數的方法, 那麼參數必須是物件類型 // 也就是說方法的形參必須接受的是一個對象, 因為withObject只能傳遞一個對象 [p performSelector:sel1 withObject:@"13900000001"]; SEL sel2 = @selector(setAge:); [p performSelector:sel2 withObject:@(5)]; NSLog(@"age = %i",p.age); // 注意:performSelector最多隻能傳遞2個參數 SEL sel3 = @selector(sendMessageWithNumber:andContent:); [p performSelector:sel3 withObject:@"13900000001" withObject:@"abcdef"];
Car *c = [Car new]; SEL sel = @selector(run); Person *p = [Person new]; [p makeObject:c andSel:sel];
Foundation => Objective-C _ Part3