如何給Category增長屬性

前言

本文已經添加到專輯:《完全弄懂OC》。 歡迎加入個人QQ羣:661461410,一塊兒探討iOS底層原理。objective-c

相關問題

  • 分類能夠添加屬性嗎? 若是能夠,應該如何實現。

背景知識

咱們知道在一個類中增長一個屬性,編譯器會幫咱們作3件事,好比,咱們給Person這個類增長一個屬性age,編譯以後,類中會增長一個成員變量_age, 增長get方法和set方法的生命與實現 -(int)age-(void)setAge:(int)age多線程

可是咱們在分類裏面聲明一個屬性,會幫咱們聲明兩個方法set, get。 但不會生成實現,也不會產生成員變量。因此咱們不能直接給Category添加成員變量,可是能夠間接實現Category有成員變量的效果。函數

實現分類添加屬性

使用全局變量

下面代碼中,咱們爲person的分類增長了一個age屬性,咱們經過聲明一個全局變量_age,將age的值存儲到_age中。ui

@interface Person (Test)
@property (nonatomic, assign) int age;
@end
  
@implementation Person (Test)
int _age;
- (void)setAge:(int)age {
    _age = age;
}
-(int)age {
    return _age;
}
@end
複製代碼

但,這種方式有致命的肯定,就是多個實例對象共用_age,致使數據錯誤。atom

使用字典

#import "Person+Test.h"

@implementation Person (Test)
NSMutableDictionary *_ageDic;
+ (void)load {
    _ageDic = [[NSMutableDictionary alloc] init];
}

- (int)age {
    NSString *selfKey = [NSString stringWithFormat:@"%p", self];
    return [[_ageDic valueForKey: selfKey] intValue];
}

- (void)setAge:(int)age {
    NSString *selfKey = [NSString stringWithFormat:@"%p", self];
    [_ageDic setValue:@(age) forKey:selfKey];
}
@end
複製代碼

使用字典來存儲值解決上一種方法中多個對象致使數據錯亂的問題,但它也有一些問題,以下:spa

  1. 多線程問題。
  2. 每增長一個屬性都要新增一個字段,拓展性太差,假如再加一個屬性name,就須要增長一個字段用來存儲對象對應的name值。
  3. 屬性是存儲在字段內部的,並非存儲在類的對象內部。

關聯對象

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

@implementation Person (Test)

- (int)age {
    return [objc_getAssociatedObject(self, @selector(age)) intValue];
}

- (void)setAge:(int)age {
    objc_setAssociatedObject(self, @selector(age), @(age), OBJC_ASSOCIATION_ASSIGN);
}
@end
複製代碼

這種方式,咱們使用runtime提供的兩個方法objc_setAssociatedObjectobjc_setAssociatedObject 來分別設置和獲取屬性值。就objc_setAssociatedObject而言其接受三個參數:線程

  1. id,表示爲當前對象關聯屬性。code

  2. key,關聯屬性對應的惟一key,這裏咱們使用get方法的函數地址值。orm

  3. value,key對應的值。cdn

  4. objc_AssociationPolicy, 存儲策略。有5個取值,根據屬性的類型選擇對應的內存存儲策略。

    OBJC_ASSOCIATION_ASSIGN = 0,

    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,

    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,

    OBJC_ASSOCIATION_RETAIN = 01401,

    OBJC_ASSOCIATION_COPY = 01403

關聯對象實現原理

關聯對象的底層實現涉及到了4個類,分別是:

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

其實現結構以下:

關聯對象實現原理

其中 AssociationsHashMapdisguised_ptr_t 表示對象,AssociationMap 中的 void * 表示 keyObjectAssociation 中包含value和存儲策略。

咱們以上面的Person對象爲例,圖示一下總體的結構。

實現原理

總結

經過上面的分析,咱們回答一下開頭的問題:

分類不能夠直接添加屬性,但能夠間接添加,最優雅的方式是經過關聯對象進行屬性與分類的綁定。

那麼,留一個問題,你認爲關聯對象須要手動釋放嗎? 在類銷燬的時候,須要釋放其經過關聯對象綁定的屬性嗎?

互動交流

關注公衆號
相關文章
相關標籤/搜索