參考文章:http://www.reibang.com/p/935e966c0c08
一乌奇、Category(分類)
分類就是對(duì)一個(gè)類的功能進(jìn)行擴(kuò)展理朋,讓這個(gè)類能夠適應(yīng)不同情況的需求,在一般的實(shí)際開發(fā)中铣墨,我們都會(huì)對(duì)系統(tǒng)的一些常用類進(jìn)行擴(kuò)展室埋,比如:NSString,Button踏兜,Label等等词顾。
簡(jiǎn)單來(lái)說(shuō)類別是一種為現(xiàn)有的類添加新方法的方式
利用Objective-C的動(dòng)態(tài)運(yùn)行時(shí)分配機(jī)制,Category提供了一種比繼承(inheritance)更為簡(jiǎn)潔的方法來(lái)對(duì)類進(jìn)行擴(kuò)展碱妆,無(wú)需創(chuàng)建對(duì)象類的子類就能為現(xiàn)有的類添加新方法
可以為任何已經(jīng)存在的類添加方法肉盹,包括那些沒有源代碼的類,如系統(tǒng)框架類Foundation疹尾,UIKit等
1.類別的作用
(1)可以將類的實(shí)現(xiàn)分散到多個(gè)不同的文件或者不同的框架中上忍,方便代碼的管理。也可以對(duì)框架提供類的擴(kuò)展(沒有源碼纳本,不能修改)窍蓝。
(2)創(chuàng)建對(duì)私有方法的前向引用:在訪問(wèn)其他類的私有方法時(shí)(私有方法:在.h中沒有聲明,在.m中有方法實(shí)現(xiàn))繁成,編譯器報(bào)錯(cuò)吓笙,這時(shí)使用類別,在添加的類別中聲明這些方法(不必提供方法實(shí)現(xiàn))巾腕,編譯器就不會(huì)再產(chǎn)生警告面睛。
(3)向?qū)ο筇砑臃钦絽f(xié)議:創(chuàng)建一個(gè)NSObject的類別稱為“創(chuàng)建一個(gè)非正式協(xié)議”。因?yàn)樽鸢幔琋SObject是頂級(jí)父類叁鉴,在NSObject中添加了該方法,也就是說(shuō)通過(guò)繼承關(guān)系佛寿,所有的類中都有該方法幌墓。
2.局限性
(1)分類只能增加方法,不能增加成員變量,但是可以通過(guò)運(yùn)行時(shí)來(lái)給分類添加屬性
成員變量和屬性的區(qū)別
聲明一個(gè)屬性:
@property (nonatomic, strong) NSString *myString;
聲明一個(gè)成員變量(實(shí)例變量):
@interface MyViewController:UIViewController
{
NSString *_myString;
}
@end
因?yàn)楝F(xiàn)在我們用的編譯器已經(jīng)時(shí)LLVM了常侣,當(dāng)我們聲明一個(gè)屬性時(shí)蜡饵,如果LLVM發(fā)現(xiàn)一個(gè)沒有匹配實(shí)例變量的屬性, 它將為你生成以下劃線開頭的實(shí)例變量 _myString, 所以不再需要為屬性手動(dòng)聲明實(shí)例變量了袭祟;
而且不需要在.m文件中寫@synthesize myString验残;也會(huì)自動(dòng)為你生成setter,getter方法巾乳。
@synthesize的作用就是讓編譯器為你自動(dòng)生成setter與getter方法
在.m文件中可以直接使用_myString實(shí)例變量您没,也可以通過(guò)屬性self.myString兩者都是一樣的,只不過(guò)后者時(shí)通過(guò)調(diào)用_myString的setter/getter方法胆绊。
@synthesize 還有一個(gè)作用氨鹏,可以指定與屬性對(duì)應(yīng)的實(shí)例變量,例如@synthesize myString = xxxx压状;那么self.myString 其實(shí)是操作的實(shí)例變量xxxx仆抵,而不是_myString了。
分類中用@property定義變量种冬,只會(huì)生成變量的getter镣丑,setter方法的聲明,不能生成方法實(shí)現(xiàn)和帶下劃線的成員變量娱两。有沒有解決方案呢莺匠?有,通過(guò)運(yùn)行時(shí)建立關(guān)聯(lián)引用
(2)如果分類和原來(lái)類出現(xiàn)同名的方法十兢,優(yōu)先調(diào)用分類中的方法趣竣,原來(lái)類中的方法會(huì)被忽略,方法調(diào)用的優(yōu)先級(jí)分類較高(最后參與編譯的分類優(yōu)先)旱物,只要有分類就優(yōu)先調(diào)用分類遥缕,不考慮與主類的編譯順序
3.利用運(yùn)行時(shí)來(lái)為分類添加屬性
(1)引入運(yùn)行時(shí)頭文件。
\#import <objc/runtime.h>
(2)在匿名分類或者頭文件中添加屬性宵呛。
(區(qū)別是:匿名分類中添加的是私有屬性单匣,只在本類中可以使用,類的實(shí)例中不可以使用宝穗;在分類頭文件中添加的屬性在類的實(shí)例中也可以使用)
//分類的頭文件
@interface ClassName (CategoryName)
//我要添加一個(gè)實(shí)例也可以訪問(wèn)的變量所以就寫在這里了
@property (nonatomic, strong) NSString *str;
@end
//匿名分類
@interface ClassName ()
@end
(3)在實(shí)現(xiàn)里面寫要添加屬性的getter户秤、setter方法
static void *strKey = &strKey;
@implementation ClassName (CategoryName)
-(void)setStr:(NSString *)str
{
objc_setAssociatedObject(self, &strKey, str, OBJC_ASSOCIATION_COPY);
}
-(NSString *)str
{
return objc_getAssociatedObject(self, &strKey);
}
@end
在setStr:方法中使用了一個(gè)objc_setAssociatedObject的方法,這個(gè)方法有四個(gè)參數(shù)讽营,分別是:
1.源對(duì)象,
2.關(guān)聯(lián)時(shí)用來(lái)標(biāo)記是哪一個(gè)屬性的key(因?yàn)槟憧赡芤砑雍芏鄬傩裕?3.關(guān)聯(lián)的對(duì)象
4.一個(gè)關(guān)聯(lián)策略
用來(lái)標(biāo)記是哪一個(gè)屬性的key常見有三個(gè)寫法泡徙,但代碼效果是一樣的橱鹏,如下:
//利用靜態(tài)變量地址唯一不變的特性
1、static void *strKey = &strKey;
2、static NSString *strKey = @"strKey";
3莉兰、static char strKey;
關(guān)聯(lián)策略是個(gè)枚舉值挑围,解釋如下:
enum {
OBJC_ASSOCIATION_ASSIGN = 0, //關(guān)聯(lián)對(duì)象的屬性是弱引用
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //關(guān)聯(lián)對(duì)象的屬性是強(qiáng)引用并且關(guān)聯(lián)對(duì)象不使用原子性
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //關(guān)聯(lián)對(duì)象的屬性是copy并且關(guān)聯(lián)對(duì)象不使用原子性
OBJC_ASSOCIATION_RETAIN = 01401, //關(guān)聯(lián)對(duì)象的屬性是copy并且關(guān)聯(lián)對(duì)象使用原子性
OBJC_ASSOCIATION_COPY = 01403 //關(guān)聯(lián)對(duì)象的屬性是copy并且關(guān)聯(lián)對(duì)象使用原子性
};
二、繼承
多個(gè)類具有相同的實(shí)例變量和方法時(shí)糖荒,考慮用繼承杉辙。即子類可以繼承父類的相同特性。
和Category的區(qū)別:
1.類別是對(duì)方法的擴(kuò)展捶朵,不能添加成員變量蜘矢。繼承可以在原來(lái)父類的成員變量的基礎(chǔ)上,添加新的成員變量
2.類別只能添加新的方法综看,不能修改和刪除原來(lái)的方法品腹。繼承可以增加、修改和刪除方法红碑。
3.類別不提倡對(duì)原有的方法進(jìn)行重載舞吭;繼承可以通過(guò)使用super對(duì)原來(lái)方法進(jìn)行重載
4.類別可以被繼承,如果一個(gè)父類中定義了類別析珊,那么其子類中也會(huì)繼承此類別羡鸥。
共同點(diǎn):都是給一個(gè)類進(jìn)行擴(kuò)展
三、Extension:類擴(kuò)展
Extension是Category的一個(gè)特例
作用:為一個(gè)類增加私有方法忠寻,屬性或成員變量惧浴,也就是說(shuō)只能在本文件中被使用其名字為匿名(為空),并且新添加的方法一定要予以實(shí)現(xiàn)锡溯。(Category沒有這個(gè)限制)
Extension(延展)的實(shí)現(xiàn):
第一種方法:
通過(guò)延展來(lái)實(shí)現(xiàn)方法的私有赶舆,延展的.h頭文件獨(dú)立。這種方法不能實(shí)現(xiàn)真正的方法私有祭饭,當(dāng)在別的文件中引入延展的.h頭文件芜茵,那么在這個(gè)文件中定義的類的對(duì)象就可以直接調(diào)用在延展中定義所謂 私有方法
第二種實(shí)現(xiàn)延展的方式
延展沒有獨(dú)立的頭文件,在類的實(shí)現(xiàn)文件.m中聲明和實(shí)現(xiàn)延展倡蝙,這種方法可以很好的實(shí)現(xiàn)方法的私有九串,因?yàn)樵贠C中是不能引入.m的文件的
第三種實(shí)現(xiàn)方法
私有方式是在.m文件中的@implementation中直接實(shí)現(xiàn)在@interface中沒有聲明的方法,這樣也可以很好的實(shí)現(xiàn)方法的私有寺鸥,開發(fā)中經(jīng)常用的方式猪钮。
Category和Extension區(qū)別:
1.形式上看:extension是匿名的category
2.extension 中聲明的方法需要在implementation中實(shí)現(xiàn),而catefory不做強(qiáng)制要求
3.extension 可以添加屬性胆建、成員變量烤低,而category 一般不可以。
有人說(shuō)extension 是一個(gè)特殊的category笆载,也有人將extension叫做匿名分類扑馁,但是其實(shí)兩者差別很大涯呻。
extension:
1.在編譯器決議,是類的一部分腻要,在編譯器和頭文件的@interface和實(shí)現(xiàn)文件里的@implement一起形成了一個(gè)完整的類复罐。
2.伴隨著類的產(chǎn)生而產(chǎn)生,也隨著類的消失而消失
3.extension一般用來(lái)隱藏類的私有消失雄家,你必須有一個(gè)類的源碼才能添加一個(gè)類的extension效诅,所以對(duì)于系統(tǒng)一些類,如nsstring趟济,就無(wú)法添加類擴(kuò)展
category:
1.是運(yùn)行期決議的
2.類擴(kuò)展可以添加實(shí)例變量乱投,分類不能添加實(shí)例變量
原因:因?yàn)樵谶\(yùn)行期,對(duì)象的內(nèi)存布局已經(jīng)確定咙好,如果添加實(shí)例變量會(huì)破環(huán)類的內(nèi)部布局篡腌,這對(duì)編譯性語(yǔ)言是災(zāi)難性的。