版本
Xcode 8.3.2
一、分類(lèi)
Category這里稱(chēng)分類(lèi)(另說(shuō)類(lèi)別纤壁、類(lèi)目)左刽。
分類(lèi)的作用:擴(kuò)展已有類(lèi)(包括系統(tǒng)類(lèi))的功能。比如給NSString增加一個(gè)字符串反轉(zhuǎn)的方法酌媒。你可能想到了欠痴,用繼承也可以增加這個(gè)功能。那為什么又搞出個(gè)分類(lèi)呢秒咨?不妨來(lái)對(duì)比一下喇辽。
1、繼承(Inherit) VS 分類(lèi)(Category)
-
繼承
繼承特點(diǎn):
1)子類(lèi)可以擴(kuò)展自己特有的屬性(包括新增成員變量)雨席,但是不能訪問(wèn)父類(lèi)的私有成員變量菩咨。
2)子類(lèi)可以擴(kuò)展自己特有的方法,且新方法可以與原方法同名陡厘,而不會(huì)對(duì)父類(lèi)原方法產(chǎn)生影響抽米。什么情況下使用繼承?
1)擴(kuò)展類(lèi)的屬性糙置。
2)新擴(kuò)展的方法與原方法同名云茸,但是還需要使用父類(lèi)的實(shí)現(xiàn)。 -
分類(lèi)
分類(lèi)特點(diǎn):
1)分類(lèi)可以在不改變類(lèi)名和原有類(lèi)的實(shí)現(xiàn)的前提下谤饭,進(jìn)行類(lèi)的擴(kuò)展标捺。
2)在方法內(nèi)部可以訪問(wèn)原有類(lèi)的成員變量(在頭文件中定義的成員變量),不能給原有類(lèi)增加成員變量(使用@property只能生成setter和getter方法的聲明网持,不能生成方法的實(shí)現(xiàn)宜岛,也不會(huì)生成帶“_”的成員變量)。 可以給原有類(lèi)添加屬性:使用Runtime里的運(yùn)行時(shí)關(guān)聯(lián)對(duì)象方法來(lái)實(shí)現(xiàn)屬性的setter和getter方法功舀。
3)分類(lèi)與原有類(lèi)不能有同名的方法。因?yàn)檫@樣會(huì)覆蓋原類(lèi)的實(shí)現(xiàn)身弊,而無(wú)法訪問(wèn)到原來(lái)的方法辟汰。
4)分類(lèi)之間也不能有同名的方法。因?yàn)樽詈缶幾g的那個(gè)方法會(huì)覆蓋其他方法阱佛。什么情況下使用分類(lèi)帖汞?
1)針對(duì)系統(tǒng)類(lèi)(例如:NSString、NSArray凑术、NSNumber等)翩蘸,系統(tǒng)本身不提倡使用繼承去擴(kuò)展方法,因?yàn)檫@些類(lèi)內(nèi)部實(shí)現(xiàn)對(duì)繼承有所限制淮逊,所以應(yīng)使用分類(lèi)的方式擴(kuò)展催首。
2)分類(lèi)支持開(kāi)發(fā)人員針對(duì)自己構(gòu)建的類(lèi)扶踊,把相關(guān)的方法分組到多個(gè)單獨(dú)的文件中,對(duì)于大型而復(fù)雜的類(lèi)郎任,這有助于提高可維護(hù)性秧耗,并簡(jiǎn)化單個(gè)源文件的管理。
2舶治、給分類(lèi)添加方法
步驟:
- 為原有類(lèi)創(chuàng)建一個(gè)分類(lèi)
- 在分類(lèi)中增加新方法的聲明和實(shí)現(xiàn)
按圖:
注意到上圖中的+Revers和(Revers)分井,這些是固定格式,表過(guò)不提霉猛。
接下來(lái)貼上開(kāi)頭提到的字符串反轉(zhuǎn)代碼:
.h文件
#import <Foundation/Foundation.h>
@interface NSString (Revers)
// 字符串反轉(zhuǎn)方法
-(NSString *)stringByReversString;
@end
.m文件
#import "NSString+Revers.h"
@implementation NSString (Revers)
// 字符串反轉(zhuǎn)方法
-(NSString *)stringByReversString {
NSUInteger length = [self length];
NSMutableArray *array = [NSMutableArray arrayWithCapacity:length];
for(long i=length-1; i>=0; i--){
unichar c = [self characterAtIndex:i];
[array addObject:[NSString stringWithFormat:@"%c",c]];
}
NSMutableString *str = [NSMutableString stringWithCapacity:length];
for(int i=0; i<=length-1; i++){
[str appendString:array[i]];
}
return str;
}
@end
main中調(diào)用:
#import <Foundation/Foundation.h>
#import "NSString+Revers.h"
int main(int argc, const char * argv[]) {
NSString *str = @"abcde";
str = [str stringByReversString];
NSLog(@"%@",str);
return 0;
}
輸出如下:
3尺锚、給分類(lèi)添加屬性
前文提到,分類(lèi)添加屬性惜浅,系統(tǒng)只能生成setter和getter方法的聲明瘫辩,不能生成方法的實(shí)現(xiàn),也不會(huì)生成帶“_”的成員變量赡矢。
如果我們?nèi)匀幌虢o分類(lèi)添加可用的屬性杭朱,那么可以使用Runtime里的運(yùn)行時(shí)關(guān)聯(lián)對(duì)象方法來(lái)實(shí)現(xiàn)屬性的setter和getter方法。
關(guān)于Runtime的技術(shù)原理吹散,請(qǐng)參考這里弧械。
實(shí)現(xiàn)步驟如下:
- 1、.h文件中添加屬性
#import <Foundation/Foundation.h>
@interface NSString (Revers)
@property (nonatomic, retain) NSNumber *tag;
@end
- 2空民、導(dǎo)入Runtime頭文件
#import <objc/runtime.h>
- 3刃唐、.m文件中實(shí)現(xiàn)屬性的setter和getter方法
#import "NSString+Revers.h"
#import <objc/runtime.h>
static char tagKey; // 用來(lái)標(biāo)記是哪一個(gè)屬性的key(靜態(tài)變量地址唯一且不變)
@implementation NSString (Revers)
// tag的setter方法
- (void)setTag:(NSNumber *)tag {
// 注意第四個(gè)參數(shù)參數(shù)OBJC_ASSOCIATION_RETAIN_NONATOMIC是和屬性修飾符對(duì)應(yīng)的
objc_setAssociatedObject(self, &tagKey, tag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// tag的getter方法
- (NSNumber *)tag {
return objc_getAssociatedObject(self, &tagKey);
}
@end
- 4、調(diào)用屬性示例
NSString *str = @"abcde";
str.tag = @101;
NSLog(@"tag:%@",str.tag);
注意
Xcode中對(duì)objc_setAssociatedObject的定義如下界轩。
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy);
第三個(gè)參數(shù)(即要關(guān)聯(lián)的數(shù)據(jù)類(lèi)型)是id數(shù)據(jù)類(lèi)型画饥,而id類(lèi)型是指向?qū)ο蟮闹羔槪哉f(shuō)浊猾,關(guān)聯(lián)對(duì)象不能是基本數(shù)據(jù)類(lèi)型(如BOOL抖甘、NSInteget、long等等)葫慎。當(dāng)我們需要為分類(lèi)添加基本數(shù)據(jù)類(lèi)型的時(shí)候衔彻,可以先將基本數(shù)據(jù)類(lèi)型轉(zhuǎn)化成OC對(duì)象,再添加屬性偷办。
二艰额、擴(kuò)展
先來(lái)看看:
注意到上圖中的_Exs和(),這些是擴(kuò)展固定格式椒涯,是不是感覺(jué)和分類(lèi)有點(diǎn)相似呢柄沮?
其實(shí),擴(kuò)展也叫匿名分類(lèi),區(qū)別是擴(kuò)展只有.h文件祖搓,而實(shí)現(xiàn)是寫(xiě)在被擴(kuò)展類(lèi)的.m文件中的狱意。因此,擴(kuò)展的功能可以認(rèn)為是被擴(kuò)展類(lèi)所私有的棕硫,其他類(lèi)無(wú)法使用髓涯。
擴(kuò)展的作用:為類(lèi)增加私有的屬性和方法。一般不用于給系統(tǒng)類(lèi)添加方法哈扮。
因?yàn)槭撬接袑傩院头椒ㄎ臣停覀円话悴涣硗馍蒃xtension(擴(kuò)展)的.h文件,而是將.h文件里的內(nèi)容(聲明)直接寫(xiě)在被擴(kuò)展類(lèi)的.m文件里滑肉。
例如包各,我們要擴(kuò)展一個(gè)自定義的Person類(lèi),不用另外創(chuàng)建Extension的.h文件靶庙,而是在Person的.m文件里加入:
@interface Person () {
// 添加成員變量
NSString * _sex;
}
// 添加方法问畅,實(shí)現(xiàn)要寫(xiě)在下方的@implementation里
- (void)sayHello;
@end