iOS 分類 category

1. 分類是什么

分類是利用 OC 的動態(tài)運行時機制,實現(xiàn)對一個現(xiàn)有類的功能進行擴展而不必知道該類的源碼奖唯。所以分類不僅可以擴展自定義類,也可以擴展系統(tǒng)類如 NSString 等糜值;分類提供了一種比繼承更簡潔的方法對類進行功能擴展丰捷,無需創(chuàng)建子類就能為現(xiàn)有類添加新的方法。

底層結構
struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods; // 對象方法
    struct method_list_t *classMethods; // 類方法
    struct protocol_list_t *protocols; // 協(xié)議
    struct property_list_t *instanceProperties; // 屬性
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

從源碼基本可以看出平時使用categroy的方式寂汇,對象方法病往,類方法,協(xié)議骄瓣,和屬性都可以找到對應的存儲方式停巷。并且發(fā)現(xiàn)分類結構體中是不存在成員變量的,因此分類中是不允許添加成員變量的榕栏。
由源碼可知畔勤,分類的方法和屬性列表是存儲在分類結構體中的,在運行時扒磁,會檢查現(xiàn)有類是否存在分類庆揪,存在的話會遍歷分類列表,將各個分類下對應的方法及屬性列表插入到現(xiàn)有類的方法和屬性列表前面妨托,這樣做的目的是為了保證分類方法優(yōu)先調(diào)用缸榛,我們知道當分類重寫本類的方法時,會覆蓋本類的方法兰伤。其實本質(zhì)上并不是覆蓋内颗,而是優(yōu)先調(diào)用。本類的方法依然在內(nèi)存中的敦腔。

  • 以下示例均以該類為原有類:
Person.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
{
    NSString *_name;
    NSInteger _age;
}
- (void)speak;
@end

NS_ASSUME_NONNULL_END

Person.m
#import "Person.h"

@implementation Person

-(void)speak
{
    NSLog(@"Person---speak");
}

-(void)walk
{
    NSLog(@"Person---walk");
}
@end

2.category的作用

(1)將類的實現(xiàn)分散到多個不同的文件中均澳,方便代碼管理,也可以對框架提供的類進行擴展会烙。(可以減少單個類的體積负懦,把不同的功能組織到不同的 category 里筒捺,可以由多個開發(fā)者共同完成一個類柏腻,可以按需加載想要的 category);

Person + Test1.h
#import "Person.h"
NS_ASSUME_NONNULL_BEGIN

@interface Person (Test1)

- (void)eat;

@end

NS_ASSUME_NONNULL_END

Person+Test1.m
#import "Person+Test1.h"
@implementation Person (Test1)

- (void)eat{
    NSLog(@"Person+Test1---eat");
}
@end

(2)創(chuàng)建對私有方法的前向引用:如果原有類中有未在頭文件中聲明的私有方法,你在訪問該私有方法的時候編譯器就會報錯系吭,這時就可以使用分類五嫂,在分類中聲明該私有方法將其公開(不必提供方法實現(xiàn));

Person + Test2.h
#import "Person.h"
NS_ASSUME_NONNULL_BEGIN

@interface Person (Test2)
- (void)walk;
@end

NS_ASSUME_NONNULL_END

Person + Test2.h
#import "Person+Test2.h"
@implementation Person (Test2)

@end

在其他類中訪問原有類的私有方法:

#import "ViewController.h"
#import "Person.h"
#import "Person+Test1.h"
#import "Person+Test2.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *person = [[Person alloc]init];
    //原有類方法
    [person speak];
    //分類擴展方法
    [person eat];
    //利用方法創(chuàng)建對私有方法的前向引用
    [person walk];
    
}
@end

(3)向?qū)ο筇砑臃钦絽f(xié)議:創(chuàng)建一個 NSObject 的分類稱為“創(chuàng)建一個非正式協(xié)議”,因為可以作為任何類(NSObject 為任意類的父類)的委托對象使用(聲明私有方法)沃缘。

NSObject+Test3.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (Test3)
- (void)sleep;
@end
NS_ASSUME_NONNULL_END

NSObject+Test3.m
#import "NSObject+Test3.h"

@implementation NSObject (Test3)

-(void)sleep
{
    NSLog(@"NSObject + Test3 --- sleep");
}
@end
  Person *person = [[Person alloc]init];
    //原有類方法
    [person speak];
    //分類擴展方法
    [person eat];
    //利用方法創(chuàng)建對私有方法的前向引用
    [person walk];
    
    [person sleep];

3.category的局限性

  • category 只能給某個已有類擴充方法躯枢,不能擴充成員變量;
  • category中也可以添加屬性槐臀,只不過@property只會生成setter和getter的聲明锄蹂,不會生成setter和getter的實現(xiàn)(.調(diào)用會崩潰,找不到方法實現(xiàn)水慨〉妹樱可以使用runtime去實現(xiàn)Category為已有的類添加新的屬性并生成getter和setter方法)以及成員變量。
  • 果category中的方法和類中原有方法同名晰洒,運行時會優(yōu)先調(diào)用category中的方法朝抖。
  • 如果多個category中存在同名的方法,運行時到底調(diào)用哪個方法由編譯器決定谍珊,最后一個參與編譯的方法會被調(diào)用治宣。
    屏幕快照 2018-10-21 16.09.33.png

4.常見問題總結

  • Category的實現(xiàn)原理,以及Category為什么只能加方法不能加屬性?

    答:分類的實現(xiàn)原理是將category中的方法砌滞,屬性侮邀,協(xié)議數(shù)據(jù)放在category_t結構體中,然后將結構體內(nèi)的方法列表拷貝到類對象的方法列表中贝润。
    Category可以添加屬性豌拙,但是并不會自動生成成員變量及set/get方法。因為category_t結構體中并不存在成員變量题暖。通過之前對對象的分析我們知道成員變量是存放在實例對象中的按傅,并且編譯的那一刻就已經(jīng)決定好了。而分類是在運行時才去加載的胧卤。那么我們就無法再程序運行時將分類的成員變量中添加到實例對象的結構體中唯绍。因此分類中不可以添加成員變量。

  • Category中有l(wèi)oad方法嗎枝誊?load方法是什么時候調(diào)用的况芒?load 方法能繼承嗎?

    答:Category中有l(wèi)oad方法叶撒,load方法在程序啟動裝載類信息的時候就會調(diào)用绝骚。load方法可以繼承。調(diào)用子類的load方法之前祠够,會先調(diào)用父類的load方法压汪。

  • load、initialize的區(qū)別古瓤,以及它們在category重寫的時候的調(diào)用的次序止剖。

    答:區(qū)別在于調(diào)用方式和調(diào)用時刻
    調(diào)用方式:load是根據(jù)函數(shù)地址直接調(diào)用腺阳,initialize是通過objc_msgSend調(diào)用
    調(diào)用時刻:load是runtime加載類、分類的時候調(diào)用(只會調(diào)用1次)穿香,initialize是類第一次接收到消息的時候調(diào)用亭引,每一個類只會initialize一次(父類的initialize方法可能會被調(diào)用多次)

    調(diào)用順序:先調(diào)用類的load方法,在調(diào)用load之前會先調(diào)用父類的load方法皮获。分類中l(wèi)oad方法不會覆蓋本類的load方法焙蚓,先編譯的分類優(yōu)先調(diào)用load方法。initialize先初始化父類洒宝,之后再初始化子類主届。如果子類沒有實現(xiàn)+initialize,會調(diào)用父類的+initialize(所以父類的+initialize可能會被調(diào)用多次)待德,如果分類實現(xiàn)了+initialize君丁,就覆蓋類本身的+initialize調(diào)用。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末将宪,一起剝皮案震驚了整個濱河市绘闷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌较坛,老刑警劉巖印蔗,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異丑勤,居然都是意外死亡华嘹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門法竞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耙厚,“玉大人,你說我怎么就攤上這事岔霸⊙” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵呆细,是天一觀的道長型宝。 經(jīng)常有香客問我,道長絮爷,這世上最難降的妖魔是什么趴酣? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮坑夯,結果婚禮上岖寞,老公的妹妹穿的比我還像新娘。我一直安慰自己渊涝,他們只是感情好慎璧,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布床嫌。 她就那樣靜靜地躺著跨释,像睡著了一般胸私。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鳖谈,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天岁疼,我揣著相機與錄音,去河邊找鬼缆娃。 笑死捷绒,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的贯要。 我是一名探鬼主播暖侨,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼崇渗!你這毒婦竟也來了字逗?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤宅广,失蹤者是張志新(化名)和其女友劉穎葫掉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跟狱,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡俭厚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了驶臊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挪挤。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖关翎,靈堂內(nèi)的尸體忽然破棺而出电禀,到底是詐尸還是另有隱情,我是刑警寧澤笤休,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布尖飞,位于F島的核電站,受9級特大地震影響店雅,放射性物質(zhì)發(fā)生泄漏政基。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一闹啦、第九天 我趴在偏房一處隱蔽的房頂上張望沮明。 院中可真熱鬧,春花似錦窍奋、人聲如沸荐健。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽江场。三九已至纺酸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間址否,已是汗流浹背餐蔬。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留佑附,地道東北人樊诺。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像音同,于是被迫代替她去往敵國和親词爬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容