關(guān)于分類(Category)和類擴(kuò)展(Extension)的解惑

代碼部分

為了理解和區(qū)分分類和拓展的區(qū)別治力,我們一共需要5個(gè)文件:
基類:Person.h、Person.m
擴(kuò)展:Person_Extension.h
分類:Person+Category.h勃黍、Person+Category.m

首先我們創(chuàng)建一個(gè)類(Person)

#import <Foundation/Foundation.h>

@interface Person : NSObject

//對(duì)外暴露的公共方法
-(void)eat;

@end
#import "Person.h"

//寄存于.m文件中的擴(kuò)展
@interface Person ()

//在.m文件中的擴(kuò)展會(huì)自動(dòng)為其中的屬性添加getter宵统、setter方法
@property (nonatomic,assign)int happyInt;

//聲明私有方法
-(void)sleep;

@end

@implementation Person

//在.h文件中對(duì)外暴露,公共方法
-(void)eat{
    self.happyInt += 10;
    NSLog(@"使用技能覆获,吃");
}

//私有方法實(shí)現(xiàn)
-(void)sleep{
    self.happyInt -= 10;
    NSLog(@"使用技能马澈,睡覺");
}

//方法實(shí)現(xiàn),默認(rèn)是一個(gè)私有方法
-(void)walk{
    NSLog(@"使用技能锻梳,行走");
}
@end

創(chuàng)建一個(gè)類擴(kuò)展 Person_Extension.h
代碼如下:

#import "Person.h"

@interface Person ()

//暴露Person中的happyInt箭券,并使之成為 readonly
@property (nonatomic,assign,readonly)int happyInt;

//可調(diào)用,但編譯無法通過疑枯,person中未實(shí)現(xiàn)
//單獨(dú)創(chuàng)建的擴(kuò)展辩块,基類不會(huì)對(duì)其中的屬性實(shí)現(xiàn)其getter或setter方法
//想讓該屬性能使用有兩種方法:
//1.在person中創(chuàng)建同名屬性sadInt;2.在person中手動(dòng)實(shí)現(xiàn)對(duì)應(yīng)getter荆永、setter方法
@property (nonatomic,assign)int sadInt;

//在Person中存在對(duì)應(yīng)私有方法的實(shí)現(xiàn)废亭,可正常調(diào)用
-(void)walk;

//在Person中存在對(duì)應(yīng)私有方法的實(shí)現(xiàn),可正常調(diào)用
-(void)sleep;

//在Person中不存在對(duì)應(yīng)私有方法的實(shí)現(xiàn)具钥,編譯通過豆村,但無法使用
-(void)read;

@end

創(chuàng)建一個(gè)分類Person+Category
Person+Category.h 代碼

#import "Person.h"

@interface Person (Category)

//Category中寫可以重寫同名屬性,但實(shí)際上這中方式并不規(guī)范骂删,不推薦
//Category原則上是不可以添加任何屬性的
//@property (nonatomic,assign,readwrite)int happyInt;

//非要在Category中添加屬性掌动,需要用runtime在.m中實(shí)現(xiàn)getter和setter方法
@property (nonatomic,copy)NSString *lucklyStr;

//實(shí)現(xiàn)額外的方法(基類本身未實(shí)現(xiàn)的方法)
-(void)fly;


@end

Person+Category.m代碼

#import "Person+Category.h"
#import "Person_Extension.h"
#import <objc/runtime.h>


static NSString *lucklyStrKey = @"lucklyStrKey";   //定義一個(gè)key值

@implementation Person (Category)

-(void)fly{
    //可以使用 readonly修飾 的 happyInt 的值
    if(self.happyInt > 50){
        NSLog(@"使用技能,飛行");
    }else{
        NSLog(@"使用技能宁玫,失敗");
    }
}

//runtime手動(dòng)實(shí)現(xiàn)lucklyStr屬性對(duì)應(yīng)的setter方法
- (void)setLucklyStr:(NSString *)lucklyStr{
    objc_setAssociatedObject(self, &lucklyStrKey, lucklyStr,OBJC_ASSOCIATION_COPY);
}

//runtime手動(dòng)實(shí)現(xiàn)lucklyStr屬性對(duì)應(yīng)的getter方法
- (NSString *)lucklyStr {
    return objc_getAssociatedObject(self, &lucklyStrKey);
}



/*
 系統(tǒng)警告:
 Category is implementing a method which will also be implemented by its primary class
 實(shí)際調(diào)用會(huì)以該方法內(nèi)部實(shí)現(xiàn)為準(zhǔn)
 */
-(void)walk{
    NSLog(@"Category粗恢,使用技能,行走");
}

@end

然后在ViewController中使用Person對(duì)象


#include <objc/runtime.h>
#import "ViewController.h"
#import "Person.h"
//不導(dǎo)入無法使用happyInt屬性欧瘪、walk方法眷射、sleep方法
#import "Person_Extension.h"
//不導(dǎo)入無法使用luckStr屬性、fly方法
#import "Person+Category.h"
/*
 注:
 通過打印Person的屬性和方法可知:
 即使不導(dǎo)入Person_Extension和Person+Category
 luckStr屬性佛掖、fly方法也會(huì)出現(xiàn)在Peron對(duì)象中妖碉,推測(cè)是編譯后即加入其中了
 */

@interface ViewController ()



@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *person = [[Person alloc] init];
    //公共方法
    [person eat];
    //由于readonly所以只能讀不能寫
    NSLog(@"當(dāng)前的開心程度 %d",person.happyInt);
    //由Person+Category添加的額外屬性
    person.lucklyStr = @"幸運(yùn)數(shù)字:3";
    //由Person_Extension暴露,Person負(fù)責(zé)實(shí)現(xiàn)
    [person sleep];
    //由Person_Extension暴露芥被,Person+Category覆寫
    //注:如果你打印Person的方法列表欧宜,會(huì)發(fā)現(xiàn)它有兩個(gè)walk方法
    [person walk];
    //由Person+Category暴露并實(shí)現(xiàn)
    [person fly];
    //會(huì)發(fā)生報(bào)錯(cuò)
    //person.sadInt = 10;
    //會(huì)發(fā)生報(bào)錯(cuò)
    //[person read];
    
    /*****     利用動(dòng)態(tài)特性訪問屬性或方法     *****/
    //用kvc的方式對(duì)happyInt屬性賦值,成功(KVC在iOS中就是個(gè)BUG)
    [person setValue:@100 forKey:@"happyInt"];
    //輸出結(jié)果:100
    NSLog(@"當(dāng)前的開心程度:%d",person.happyInt);
    //用performSelector方法調(diào)用 fly 拴魄,輸出飛行成功
    //這種方法甚至不用#import "Person+Category.h"
    [person performSelector:NSSelectorFromString(@"fly")];
}

控制臺(tái)輸出結(jié)果

2020-06-09 13:00:32.405 Test2[3886:109041] 使用技能鱼鸠,吃
2020-06-09 13:00:32.405 Test2[3886:109041] 當(dāng)前的開心程度 10
2020-06-09 13:00:32.406 Test2[3886:109041] 使用技能猛拴,睡覺
2020-06-09 13:00:32.406 Test2[3886:109041] Category,使用技能蚀狰,行走
2020-06-09 13:00:32.406 Test2[3886:109041] 當(dāng)前的開心程度:100
2020-06-09 13:00:32.406 Test2[3886:109041] 使用技能,飛行

上述的代碼內(nèi)容演示了iOS中擴(kuò)展和分類的基礎(chǔ)特性和用法职员。

分類和拓展的特性

擴(kuò)展:

1.可以在擴(kuò)展中聲明私有屬性麻蹋。僅可聲明,不可進(jìn)行實(shí)現(xiàn)焊切;(詳見誤區(qū)解析Q2)
2.擴(kuò)展可以把基類(Person)的私有屬性和方法進(jìn)行暴露扮授;
3.擴(kuò)展可以改變基類中私有屬性的修飾符。如能達(dá)到對(duì)外只讀专肪,而對(duì)內(nèi)可修改刹勃。但如果對(duì)內(nèi)是readonly,那么擴(kuò)展中的修飾符就不能是readwhite;(詳見誤區(qū)解析Q3)

分類:

1.分類的意義在于解決一些不能用繼承來處理的問題嚎尤;
2.分類主要作用是增加基類(Person)中沒有的方法荔仁;
3.分類的結(jié)構(gòu)體中沒有屬性列表,只有方法列表芽死。所以分類原則上無法添加屬性乏梁,但通過runtime可以實(shí)現(xiàn)屬性的添加,并會(huì)被編譯器承認(rèn)关贵;
4.分類中有和基類同名的方法, 會(huì)優(yōu)先調(diào)用分類中的方法遇骑。所以同名方法調(diào)用的優(yōu)先級(jí)為 分類 > 本類 > 父類;
5.分類在運(yùn)行時(shí)與基類想合并揖曾;
6.分類中的屬性和方法落萎,均可以被KVC這種動(dòng)態(tài)訪問的方式所訪問;
注:分類(Category)其實(shí)遵循了【裝飾者】設(shè)計(jì)模式炭剪。

分類和類擴(kuò)展的異同

  1. 一個(gè)類可以擁有多個(gè)不同的擴(kuò)展练链,也可以擁有多個(gè)不同的分類。
  2. 擴(kuò)展本質(zhì)上是讓私有方法暴露的一個(gè)特殊渠道念祭。
  3. 擴(kuò)展不能去實(shí)現(xiàn)基類沒有的方法兑宇。(屬性本身也是方法)
  4. 基類中有的,擴(kuò)展可以使之對(duì)外暴露粱坤;基類中沒有的隶糕,擴(kuò)展無能為力。
  5. 分類本質(zhì)上是為了解決不能用繼承所解決的問題站玄,可以看做是一中輕量級(jí)的繼承方案枚驻。(繼承還擁有父類所有的公共方法)
  6. 分類可以實(shí)現(xiàn)基類中沒有的額外方法。
  7. 基類中有的株旷,分類會(huì)覆寫(覆蓋)它再登;基類中沒有的尔邓,分了可以聲明并實(shí)現(xiàn)它。

誤區(qū)解析:

Q1.是否可認(rèn)為擴(kuò)展(Extension)常常是一個(gè)匿名的分類(Category)锉矢?
A:不可以梯嗽。
擴(kuò)展只有隱藏和顯示基類中私有屬性和方法的作用,擴(kuò)展沒有能力開辟新屬性和方法沽损。
而分類灯节,主要起到為基類增加方法這一作用,如有必要绵估,也可以配合runtime增加屬性炎疆。

Q2.擴(kuò)展是否有創(chuàng)建(聲明+實(shí)現(xiàn))私有屬性的功能?
A:沒有国裳。擴(kuò)展只有聲明私有屬性的功能形入,沒有實(shí)現(xiàn)私有屬性getter、setter方法的功能缝左。
在Person_Extension中的 sadInt 屬性就只有聲明亿遂,沒有實(shí)現(xiàn),所以不管是給其賦值還是想獲取其值都會(huì)發(fā)生編譯錯(cuò)誤盒使。
在Person.m中的擴(kuò)展中聲明 happyInt 會(huì)自動(dòng)進(jìn)行實(shí)現(xiàn)崩掘,是因?yàn)閮牲c(diǎn):一、OC是自上而下的進(jìn)行編譯的少办;二苞慢、.m文件中還有 @implementation關(guān)鍵字,它才是實(shí)現(xiàn)類對(duì)象所有屬性的關(guān)鍵英妓。

Q3.Person中的happyInt如果是readonly修飾符挽放,那么在擴(kuò)展中是否可以用readwrite?
A:不可以蔓纠。如果基類中使用readonly辑畦,那么對(duì)于happyInt屬性,編譯器只會(huì)為其生成getter方法腿倚,而沒有setter方法的實(shí)現(xiàn)纯出。所以即使你在Person_Extension中用 readwrite 修飾符,happyInt依然沒有setter方法敷燎,不能進(jìn)行賦值暂筝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市硬贯,隨后出現(xiàn)的幾起案子焕襟,更是在濱河造成了極大的恐慌,老刑警劉巖饭豹,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸵赖,死亡現(xiàn)場(chǎng)離奇詭異务漩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)它褪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門饵骨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人列赎,你說我怎么就攤上這事宏悦。” “怎么了包吝?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)源葫。 經(jīng)常有香客問我诗越,道長(zhǎng),這世上最難降的妖魔是什么息堂? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任嚷狞,我火速辦了婚禮,結(jié)果婚禮上荣堰,老公的妹妹穿的比我還像新娘床未。我一直安慰自己,他們只是感情好振坚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布薇搁。 她就那樣靜靜地躺著,像睡著了一般渡八。 火紅的嫁衣襯著肌膚如雪啃洋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天屎鳍,我揣著相機(jī)與錄音宏娄,去河邊找鬼。 笑死逮壁,一個(gè)胖子當(dāng)著我的面吹牛孵坚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窥淆,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卖宠,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了祖乳?” 一聲冷哼從身側(cè)響起逗堵,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎眷昆,沒想到半個(gè)月后蜒秤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汁咏,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年作媚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了攘滩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纸泡,死狀恐怖漂问,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情女揭,我是刑警寧澤蚤假,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站吧兔,受9級(jí)特大地震影響磷仰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜境蔼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一灶平、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧箍土,春花似錦逢享、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至调缨,卻和暖如春疮鲫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弦叶。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工俊犯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伤哺。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓燕侠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親立莉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绢彤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354