分類(Category):
概念
分類(Category)是OC中的特有語法,它是表示一個指向分類的結構體的指標。原則上它只能增加方法,不能增加成員(例項)變數。
- 分類中的可以寫@property, 但不會生成
setter/getter
方法, 也不會生成實現以及私有的成員變數(編譯時會報警告); - 可以在分類中訪問原有類中.h中的屬性;
- 如果分類中有和原有類同名的方法, 會優先呼叫分類中的方法, 就是說會忽略原有類的方法。所以同名方法呼叫的優先順序為
分類 > 本類 > 父類
。因此在開發中儘量不要覆蓋原有類; - 如果多個分類中都有和原有類中同名的方法, 那麼呼叫該方法的時候執行誰由編譯器決定;編譯器會執行最後一個參與編譯的分類中的方法。
我們知道在一個類中用@property宣告屬性,編譯器會自動幫我們生成
因此結論是:我們可以用@property宣告屬性,編譯和執行都會通過,只要不使用程式也不會崩潰。但如果呼叫了
_成員變數
和setter/getter
,但分類的指標結構體中,根本沒有屬性列表。所以在分類中用@property宣告屬性,既無法生成_成員變數
也無法生成setter/getter
。因此結論是:我們可以用@property宣告屬性,編譯和執行都會通過,只要不使用程式也不會崩潰。但如果呼叫了
_成員變數
和setter/getter
方法,報錯就在所難免了。既然報錯的根本原因是使用了系統沒有生成的
其實是可以的。由於OC是動態語言,方法真正的實現是通過
setter/getter
方法,可不可以在手動新增setter/getter
來避免崩潰,完成呼叫呢?其實是可以的。由於OC是動態語言,方法真正的實現是通過
runtime
完成的,雖然系統不給我們生成setter/getter
,但我們可以通過runtime
手動新增setter/getter
方法。程式碼實現如下:
#import <objc/runtime.h>
static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; //定義一個key值
@implementation Programmer (Category)
//執行時實現setter方法
- (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter {
objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);
}
//執行時實現getter方法
- (NSString *)nameWithSetterGetter {
return objc_getAssociatedObject(self, &nameWithSetterGetterKey);
}
@end複製程式碼
但是注意,以上程式碼僅僅是手動實現了setter/getter
方法,但呼叫_成員變數
依然報錯。
擴充套件(Class Extension)
Extension是Category的一個特例。類擴充套件與分類相比只少了分類的名稱,所以稱之為“匿名分類”。
其實開發當中,我們幾乎天天在使用。對於有些人來說像是最熟悉的陌生人。
@interface XXX ()
//私有屬性
//私有方法(如果不實現,編譯時會報警,Method definition for 'XXX' not found)
@end複製程式碼
作用:
為一個類新增額外的原來沒有變數,方法和屬性
一般的類擴充套件寫到.m
檔案中
一般的私有屬性寫到.m
檔案中的類擴充套件中
類別與類擴充套件的區別:
- 類別中原則上只能增加方法(能新增屬性的的原因只是通過
runtime
解決無setter/getter
的問題而已); - 類擴充套件不僅可以增加方法,還可以增加例項變數(或者屬性),只是該例項變數預設是@private型別的(
- 用範圍只能在自身類,而不是子類或其他地方);
- 類擴充套件中宣告的方法沒被實現,編譯器會報警,但是類別中的方法沒被實現編譯器是不會有任何警告的。這是因為類擴充套件是在編譯階段被新增到類中,而類別是在執行時新增到類中。
- 類擴充套件不能像類別那樣擁有獨立的實現部分(@implementation部分),也就是說,類擴充套件所宣告的方法必須依託對應類的實現部分來實現。
- 定義在 .m 檔案中的類擴充套件方法為私有的,定義在 .h 檔案(標頭檔案)中的類擴充套件方法為公有的。類擴充套件是在 .m 檔案中宣告私有方法的非常好的方式。