Category
- 概述:Category是OC2.0之后添加的語言特性研叫,Category又叫類別,分類等璧针,能夠在不改變原來類的內(nèi)容基礎(chǔ)上嚷炉,為類增加一些方法(當(dāng)然還有大家都知道的,雖然不能以正常方式為類添加屬性探橱,但是我們可以通過runtime來動態(tài)綁定添加申屹,后面會講到),除此之外Category還有以下功能
- 將類的實(shí)現(xiàn)分開寫在幾個分類里面隧膏,這樣做的好處是
- 可以減少單個文件的體積
- 可以把不用的功能組織在不同的Category里
- 可以由多個開發(fā)者共同完成一個類
- 可以按需加載想要的Category
- 聲明私有的方法
- 模擬多繼承
- 使用
既然我們說Category只能為類增加方法哗讥,而不能添加屬性,那么我們就去創(chuàng)建一個分類文件去驗(yàn)證以下胞枕,看看具體是個什么樣的情況杆煞。我們創(chuàng)建了一個Student和他的分類Student+Category
@interface Student : NSObject
@property (nonatomic,copy) NSString *age;
@property (nonatomic,copy) NSString *name;
@end
//Student+Category.h
@interface Student (Category)
@property (nonatomic,copy) NSString *schoolName;
@end
我們運(yùn)行一下,可以發(fā)現(xiàn)他編譯成功了并且運(yùn)行成功了腐泻,
image
image
放開斷點(diǎn)發(fā)現(xiàn)崩潰了决乎,看錯誤日志我們知道了沒有setSchoolName方法。那么問題來了派桩,既然我們可以在分類中添加屬性瑞驱,但在調(diào)用的時(shí)候卻會奔潰,這是為什么窄坦,我們先來看看Category的源碼
Category
Category 是表示一個指向分類的結(jié)構(gòu)體的指針唤反,其定義如下:
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; // 實(shí)例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 類方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分類所實(shí)現(xiàn)的協(xié)議列表
}
我們看到了什么凳寺,我們有發(fā)現(xiàn)實(shí)例方法列表,類方法類表彤侍,協(xié)議列表肠缨,但是沒有屬性列表。我們知道在一個類中用 @property 聲明屬性盏阶,編譯器會自動幫我們生成 _成員變量名 和setter,getter方法晒奕,但是在分類的指針結(jié)構(gòu)體中,根本就要沒有屬性類表名斟,所以也就不能生成 _成員變量名 和setter,getter方法脑慧,因此我們的程序可以編譯和運(yùn)行都成功了,但是一旦有調(diào)用這個屬性就會奔潰砰盐。
解決方案
- 由于OC是動態(tài)語言闷袒,方法真正的實(shí)現(xiàn)是通過runtime完成的,雖然系統(tǒng)不給我們生成setter/getter岩梳,但我們可以通過runtime手動添加setter/getter方法
#import "Student+Category.h"
#import <objc/runtime.h>
static const char *schoolNameKey = "schoolNameKey";
@implementation Student (Category)
- (void)setSchoolName:(NSString *)schoolName{
objc_setAssociatedObject(self, &schoolNameKey, schoolName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)schoolName{
return objc_getAssociatedObject(self, &schoolNameKey);
}
@end
- 總結(jié)
- 分類是用于給原有類添加方法的,因?yàn)榉诸惖慕Y(jié)構(gòu)體指針中囊骤,沒有屬性列表,只有方法列表冀值。所以< 原則上講它只能添加方法, 不能添加屬性(成員變量),實(shí)際上可以通過其它方式添加屬性> ;
- 分類中的可以寫@property, 但不會生成setter/getter方法, 也不會生成實(shí)現(xiàn)以及私有的成員變量(編譯時(shí)會報(bào)警告);
- 可以在分類中訪問原有類中.h中的屬性;
- 如果分類中有和原有類同名的方法, 會優(yōu)先調(diào)用分類中的方法, 就是說會忽略原有類的方法也物。所以同名方法調(diào)用的優(yōu)先級為 分類 > 本類 > 父類。因此在開發(fā)中盡量不要覆蓋原有類;
- 如果多個分類中都有和原有類中同名的方法, 那么調(diào)用該方法的時(shí)候執(zhí)行誰由編譯器決定列疗;編譯器會執(zhí)行最后一個參與編譯的分類中的方法滑蚯。
Extension
- Extension 是Category的一個特例,類擴(kuò)展與分類相比只少了分類的名稱以及.m文件抵栈,他常用的形式不是創(chuàng)建一個單獨(dú)的文件膘魄,而是在實(shí)現(xiàn)文件中添加私有的成員變量、屬性和方法竭讳。比如我們平時(shí)在創(chuàng)建一個類的時(shí)候我們會在.m文件中添加私有的成員變量、屬性和方法浙踢,當(dāng)然我們也可以在.h文件中添加绢慢,所以說Extension在我們平時(shí)的開發(fā)中是最常用的
@interface Student ()
{
NSString *_core;
}
@property (nonatomic,copy) NSString *teachName;
- (void)study;
@end
- 總結(jié)
- Extension的作用是為一個類添加額外的私有成員變量,屬性以及方法
- 一般的類擴(kuò)展寫到.m文件里
Category和Extension的區(qū)別
- Category原則中只能增加方法(能添加屬性的原因只是通過runtime解決沒有setter和getter方法)
- Extension不僅可以增加方法洛波,還可以增加屬性胰舆,只是屬性默認(rèn)的訪問權(quán)限是 private
- Extension中聲明的方法沒有在.m文件中實(shí)現(xiàn),編譯器會報(bào)警蹬挤,但是在Category中的方法沒有實(shí)現(xiàn)的話編譯器是不會有任何警告的缚窿。這是因?yàn)镋xtension是在編譯階段添加到類中,而Categor是在運(yùn)行時(shí)添加到類中的
- Extension不能像類別那樣擁有獨(dú)立的實(shí)現(xiàn)部分焰扳,他所聲明的方法只能在其對應(yīng)的類中實(shí)現(xiàn)
- Extension定義在.m文件的方法是私有的倦零,定義在.h文件的方法是公有的
- Extension一般用來隱藏類的私有消息误续,你必須有一個類的源碼才能添加一個類的Extension,所以對于系統(tǒng)一些類扫茅,如NSString蹋嵌,就無法添加類擴(kuò)展,如果只是簡單的創(chuàng)建Extension文件是能夠創(chuàng)建成功的,但是如果你在文件中添加屬性或者方法葫隙,在程序中一旦使用了該屬性或者方法程序就會崩潰栽烂,會報(bào)找不到相對應(yīng)的方法錯誤信息。