Category深度解析

因?yàn)镃ategory在iOS平時(shí)的開發(fā)中用的比較多,所以本文從各個(gè)方面介紹一下Category,很多地方都是自己的理解,歡迎各位看到的同學(xué)批評指正,多多交流。

一、Category作用

  1. 為已存在的類添加方法;對類進(jìn)行了很好的擴(kuò)展,應(yīng)對變化的需求。
  2. 可以把一個(gè)類的實(shí)現(xiàn)分散到多個(gè)文件中,使得每個(gè)文件不至于龐大,而且可以聚集同一邏輯的代碼在一個(gè)文件中。同時(shí),也可以按需加載方法。
  3. 同一個(gè)類可以由多個(gè)人共同完成。

二、為什么category不能添加屬性或者說是成員變量

這個(gè)問題從runtime的角度進(jìn)行分析。
下圖是objc_class結(jié)構(gòu)體:


classOfRuntime.png

不詳細(xì)介紹每一個(gè)字段的含義了,主要介紹與category相關(guān)的部分:
在上面的objc_class結(jié)構(gòu)體中,ivars是objc_ivar_list(成員變量列表)指針;methodLists是指向objc_method_list指針的指針。在Runtime中,objc_class結(jié)構(gòu)體大小是固定的,不可能往這個(gè)結(jié)構(gòu)體中添加數(shù)據(jù),只能修改。所以ivars指向的是一個(gè)固定區(qū)域,只能修改成員變量值,不能增加成員變量個(gè)數(shù)。methodList是是一個(gè)二維數(shù)組,所以可以修改*methodLists的值來增加成員方法,雖沒辦法擴(kuò)展methodLists指向的內(nèi)存區(qū)域,卻可以改變這個(gè)內(nèi)存區(qū)域的值(存儲(chǔ)的是指針)。因此,可以動(dòng)態(tài)添加方法,不能添加成員變量。類似的是protocol,因?yàn)樗蔷幾g器處理的,所以可以添加變量。category是在運(yùn)行時(shí)處理的。

三、怎么樣可以實(shí)現(xiàn)添加屬性或者說添加成員變量

上面說了category中不可能添加成員變量或?qū)傩?。因?yàn)橄到y(tǒng)無法合成實(shí)現(xiàn)屬性所需的實(shí)例變量,所以category中也無法添加@property。但有時(shí)候確實(shí)需要添加屬性,那有沒有其他辦法,可以不改變對象的內(nèi)存結(jié)構(gòu),變相的給對象增加成員變量。
我們可以Runtime的objc_getAssociatedObject和objc_setAssociatedObject方法來模擬屬性的get和set方法,用關(guān)聯(lián)對象來模擬實(shí)例變量,這樣就有了@property的三要素,只是跟@property的實(shí)現(xiàn)機(jī)制是完全不一樣的。
有兩種方式,第一種代碼更簡潔:

.h文件:

@property (nonatomic) NSString Id;

.m文件,要#import <objc/runtime.h>

- (NSString *)Id
{
   return objc_getAssociatedObject(self, @selector(Id));
}

- (void)setId:(NSString *)Id
{
   objc_setAssociatedObject(self, @selector(Id), Id, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

第二種:

static void *UIViewPoint =@"UIViewPoint";
@implementation UIView (Point)

@dynamic pointView;

- (void)setPointView:(UIView *)pointView {
    objc_setAssociatedObject(self, UIViewPoint, pointView,OBJC_ASSOCIATION_RETAIN);
}

- (UIView *)pointView {
   return objc_getAssociatedObject(self, UIViewPoint);
}

這兩個(gè)方法的原型如下:

id objc_getAssociatedObject(id object, const void *key);
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

@selector(Id) 是參數(shù)中的 key,方法二中使用了靜態(tài)指針 static void * 類型的參數(shù)來代替,第一種方法因?yàn)槭÷粤寺暶鲄?shù)的代碼,并且能很好地保證 key 的唯一性,所以更簡潔。

OBJC_ASSOCIATION_RETAIN_NONATOMIC 從字面意思就能猜到它是和屬性的修飾符是對應(yīng)的。對應(yīng)關(guān)系見如下的定義:

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                        *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                        *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                        *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                        *   The association is made atomically. */
};

當(dāng)屬性是基本數(shù)據(jù)類型的時(shí)候,可能只是需要添加get和set方法,不需要實(shí)例變量的時(shí)候,可以在category中這樣添加屬性,這種方式,同樣沒有改變對象的內(nèi)存結(jié)構(gòu)。

@property (nonatomic) CGFloat left;
   @property (nonatomic) CGFloat right;

- (CGFloat)left {
    return self.frame.origin.x;
}
- (void)setLeft:(CGFloat)x {
    CGRect frame = self.frame;
    frame.origin.x = x;
    self.frame = frame;
}
- (CGFloat)right {
    return self.left + self.width;
}

- (void)setRight:(CGFloat)right {
    if(right == self.right){
        return;
    }
    CGRect frame = self.frame;
    frame.origin.x = right - frame.size.width;
    self.frame = frame;
}

我查了很多資料,很多人認(rèn)為category可以添加屬性,但是不可以添加成員變量,我覺得這種說法是不嚴(yán)謹(jǐn)?shù)?,所以寫了這篇博客,來總結(jié)一下。如果有理解的不對的地方,希望看到的朋友指正,大家多多交流。

參考:

  1. http://yulingtianxia.com/blog/2014/11/05/objective-c-runtime/
  2. http://zhangbuhuai.com/2015/04/26/unstanding-the-Objective-C-Runtime-part1/
  3. http://draveness.me/ao/
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,872評論 33 466
  • 對于從事 iOS 開發(fā)人員來說,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,799評論 7 64
  • 文|張小魚 如果不能住在你心里, 都是客死他鄉(xiāng)。 ...
    張小魚716閱讀 294評論 1 2
  • 相信用過小米手機(jī)的同學(xué)都遇到一個(gè)問題,不管你是MIUI7還是內(nèi)測中的MIUI8,即使你是很早的MIUI5、MIUI...
    科技達(dá)人15閱讀 7,038評論 0 0

友情鏈接更多精彩內(nèi)容