Objective -C基礎知識點整理筆記持續更新......

Deft_MKJing宓珂璟發表於2016-12-19

1.總結類方法和例項方法:

類方法,也稱靜態方法,指的是用static關鍵字修飾的方法此方法屬類本身的方法,不屬於類的某一個例項(物件)。類方法中不可直接使用例項變數。其呼叫方式有三種:可直接呼叫、類名.方法名、物件名.方法名。例項方法指的是不用static關鍵字修飾的方法每個例項物件都有自身的例項方法,互相獨立,不共享一個。其呼叫方式只能是物件名.方法名。 
用修飾符static宣告的方法為靜態方法,不用修飾符static宣告的方法為例項方法不管類生成或未生成物件,類的靜態方法都可以被使用,使用格式為:類名.靜態方法名。靜態方法只能使用該靜態方法所在類的靜態資料成員和靜態方法。這是因為使用靜態方法時,該靜態方法所在類可能還沒有物件,即使有物件,由於用類名.靜態方法名方式呼叫靜態方法,靜態方法沒有this指標來存放物件的地址,無法判定應訪問哪個物件的資料成員。在類建立物件後,例項方法才能被使用,使用格式為:物件名.例項方法名。例項方法可以使用該方法所在類的所有靜態成員和例項成員

函式又叫方法(Methods)
方法Methods又可稱為函式。在Objective-C 裡的類可以定義兩種方法。一種是例項的方法,一種是類的方法例項的方法侷限於某個類的例項,也就是必須定義這個類的例項之後,才能被呼叫執行的方法。類的方法不需要建立例項,直接通過類的名稱就可以被呼叫執行的方法。
定義一個方法需要:方法名(一個或者多個關鍵字),返回值型別,引數型別和引數名。下面這的圖詳細的說明了如何定義一個例項的方法,其中負號[-]表示該方法為例項的方法,該方法的名稱加上各個關鍵字包括冒號即為[insertValue:atIndex:]
Objective-C 呼叫方法是通過傳送訊息給對應的例項物件。傳送訊息的方式其實是和一般的程式語言一樣就是呼叫例項物件的方法,Objective-C獨特的地方就是方法的呼叫是通過一個方法名+零個或多個標示符+零個或多個引數,然而一般的程式語言只需要一個方法名+零個或多個引數就可以了Objective-C 裡面之所以把呼叫方法稱為傳送訊息,大概是因為所有的訊息傳送之後都是動態傳遞給例項物件的並且,如果一個子類定義了一個和父類相同方法名+標示符的方法之後,子類會先收到該訊息,然後選擇性的是否繼續將該訊息傳遞給父類。
傳送訊息是通過一對方括號[]來實現的。在括號的裡面,例項物件在左邊,訊息以及引數等的定義在右邊。例如:
[anObject insertValue:anObj atIndex:1]; 
[anObject insertValue:anObj atIndex:1];

為了避免生成多餘的臨時變數,Objective-C 容許直接使用訊息的結果。如下例:
[[anObject getArray] insertValue:[anObject getValueToInsert] atIndex:0]; 
[[anObject getArray] insertValue:[anObject getValueToInsert] atIndex:0];

類的方法,類似於靜態方法(Static Function),常用於作為工廠模式中用來生成新的例項。定義的時候和例項的方法有區別的地方就是開頭的符號為加號[+]。呼叫的類的方法和呼叫(Static Function)基本相同,直接通過類名就可以。


2.Category的原理以及給Category新增屬性以及方法,但是不能新增成員變數的理解

新增一個屬性,呼叫的時候崩潰了,說是找不到getter、setter方法。查了下文件發現,OC的分類允許給分類新增屬性,但不會自動生成getter、setter方法。有沒有解決方案呢?有,通過執行時建立關聯引用

第一個引數id object, 當前物件
第二個引數const void *key, 關聯的key,可以是任意型別 
第三個引數id value, 被關聯的物件 
第四個引數objc_AssociationPolicy policy關聯引用的規則,取值有以下幾種:

關聯物件不是為類\物件新增屬性或者成員變數(因為在設定關聯後也無法通過ivarList或者propertyList取得) ,而是為類新增一個相關的物件,通常用於儲存類資訊,例如儲存類的屬性列表陣列,為將來字典轉模型的方便

- (void)setSubName:(NSString *)subName
{
    objc_setAssociatedObject(self, "subname", subName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


- (NSString *)subName
{
    return objc_getAssociatedObject(self, "subname");
}
也可以用這個方法獲取對應的屬性列表可以進行kvc字典轉模型

+ (NSArray *)properties {
    // 如果已經關聯了,就依據key取出被關聯的物件並返回
    NSArray *pList = objc_getAssociatedObject(self, propertiesKey);
    if (pList != nil) {
        return pList;
    }
    // 如果沒有關聯,則設定關聯物件,並將物件返回
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([self class], &count);
    
    NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];
    
    for (unsigned int i = 0; i < count; ++i) {
        Ivar pty = ivars[i];
        const char *cname = ivar_getName(ivars[i]);
        NSString *name = [NSString stringWithUTF8String:cname];
        NSString *key = [name substringFromIndex:1];
        [arrayM addObject:key];
    }
    free(ivars);
    objc_setAssociatedObject(self, propertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
    return arrayM.copy;
}


確實有一個class_addIvar()函式用於給類新增成員變數,但是文件中特別說明:

* @note This function may only be called after objc_allocateClassPair and before      objc_registerClassPair. 

*   Adding an instance variable to an existing class is not supported.


那麼為什麼不能新增成員變數能,但是能新增屬性和方法呢?

意思是說,這個函式只能在“構建一個類的過程中”呼叫。一旦完成類定義,就不能再新增成員變數了。經過編譯的類在程式啟動後就runtime載入,沒有機會呼叫addIvar。程式在執行時動態構建的類需要在呼叫objc_registerClassPair之後才可以被使用,同樣沒有機會再新增成員變數。那為什麼可以在類別中新增方法和屬性呢?
因為方法和屬性並不“屬於”類例項,而成員變數“屬於”類例項。我們所說的“類例項”概念,指的是一塊記憶體區域,包含了isa指標和所有的成員變數。所以假如允許動態修改類成員變數佈局,已經建立出的類例項就不符合類定義了,變成了無效物件。但方法定義是在objc_class中管理的,不管如何增刪類方法,都不影響類例項的記憶體佈局,已經建立出的類例項仍然可正常使用。




相關文章