這世上,沒有誰活得比誰容易,隻是有人在呼天搶地,有人在默默努力。
分類(Category)
一. 概念
分類(Category)是OC中的特有文法,它是表示一個指向分類的結構體的指針。原則上它隻能增加方法,不能增加成員(執行個體)變量。具體原因看源碼組成:
Category
Category 是表示一個指向分類的結構體的指針,其定義如下:
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分類名
char *class_name OBJC2_UNAVAILABLE; // 分類所屬的類名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 執行個體方法清單
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 類方法清單
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分類所實作的協定清單
}
通過上面我們可以發現,這個結構體主要包含了分類定義的執行個體方法與類方法,其中instance_methods 清單是 objc_class 中方法清單的一個子集,而class_methods清單是元類方法清單的一個子集。但這個結構體裡面,根本沒有屬性清單。
分類的作用:
- Objective-C 中的 Category 就是對裝飾模式的一種具體實作。它的主要作用是在不改變原有類的前提下,動态地給這個類添加一些方法。
分類使用原則:
- 分類是用于給原有類添加方法的,因為分類的結構體指針中,沒有屬性清單,隻有方法清單。是以原則上講它隻能添加方法, 不能添加屬性(成員變量),實際上可以通過其它方式添加屬性 ;
- 分類中可以寫@property,但不會生成
方法,也不會生成私有的成員變量(編譯時會報警告);setter/getter
- 可以在分類中通路原有類中.h中的屬性;
- 如果分類中有和原有類同名的方法, 會優先調用分類中的方法,就是說會忽略原有類的方法。是以同名方法調用的優先級為
。是以在開發中盡量不要覆寫原有類;分類 > 本類 > 父類
- 如果多個分類中都有和原有類中同名的方法,那麼調用該方法的時候執行誰由編譯器決定;編譯器會執行最後一個參與編譯的分類中的方法。
二. 分類的定義
- 分類格式:
@interface 待擴充的類(分類的名稱) @end @implementation 待擴充的名稱(分類的名稱) @end
- 執行個體代碼:
// XBCar + MinCar.h檔案 @interface XBCar (MinCar) @property (nonatomic,copy)NSString *nameWithSetterGetter; //設定setter/getter方法的屬性 @property (nonatomic,copy)NSString *nameWithoutSetterGetter; //不設定setter/getter方法的屬性(注意是可以寫在這,而且編譯隻會報警告,運作不報錯) - (void)minCarCategoryMethod; //分類方法 @end
-
分類中@property相關研究
結合上面分析我們知道,分類中原則上是不能添加屬性的,那麼為什麼我們添加了運作的時候不會報錯呢?既然分類不讓添加屬性,為什麼仍然能編譯通過呢?
首頁我們應該了解分類不能添加屬性的實質原因是什麼?我們知道在一個類中用@property聲明屬性,編譯器會自動幫我們生成
和_成員變量
方法,但分類的指針結構體中,根本沒有屬性清單。是以在分類中用@property聲明屬性,既無法生成setter/getter
也無法生成_成員變量
。是以結論是:我們可以用@property聲明屬性,編譯和運作都會通過,隻要不使用程式也不會崩潰。但如果調用了setter/getter
_成員變量
方法,報錯就在所難免了。setter/getter
#import <objc/runtime.h> static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; //定義一個key值 @implementation XBCar (MinCar) // 運作時實作setter方法 - (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter { objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY); } // 運作時實作getter方法 - (NSString *)getNameWithSetterGetter { return objc_getAssociatedObject(self, &nameWithSetterGetterKey); } @end
擴充(Extension)
二. 擴充的定義
@interface XXX ()
//私有屬性
//私有方法(如果不實作,編譯時會報警,Method definition for 'XXX' not found)
@end
三. 作用
- 為一個類添加額外的、原來沒有的私有變量,私有方法和私有屬性;
- 隻以聲明存在,寄生于宿主類.m中(一般的類擴充寫到
檔案中,一般的私有屬性寫到.m
檔案中的類擴充中。);.m
類别和類擴充的差別
- 類别原則上隻能增加方法(能添加屬性的的原因隻是通過
解決無runtime
的問題而已);setter/getter
-
類擴充不僅可以增加方法,還可以增加執行個體變量(或者屬性),隻是該執行個體變量預設是@private類型的(
用範圍隻能在自身類,而不是子類或其他地方);
- 類擴充中聲明的方法沒被實作,編譯器會報警,但是類别中的方法沒被實作編譯器是不會有任何警告的。這是因為類擴充是在編譯階段被添加到類中,而類别是在運作時添加到類中;
- 類擴充不能像類别那樣擁有獨立的實作部分(@implementation部分),也就是說,類擴充所聲明的方法必須依托對應類的實作部分來實作;
- 定義在 .m 檔案中的類擴充方法為私有的,定義在 .h 檔案(頭檔案)中的類擴充方法為公有的。類擴充是在 .m 檔案中聲明私有方法的非常好的方式;