iOS runtime 應(yīng)用之給 NSString 添加對象屬性和非對象屬性

runtimeiOS 的作用和地位在此就無需多費口舌了.

接下來我以添加屬性為例, 用 runtimeFoundation 下的 NSString 類添加兩種屬性, 對象屬性和非對象屬性. 初步了解一下 runtime 的基本使用方法.

兩個重要的 API

首先來介紹 runtime 中的兩個 api :

1. objc_setAssociatedObject

蘋果官方給出的解釋就是:
Sets an associated value for a given object using a given key and association policy.
稍微翻譯一下(btw, 英文不好, 請勿見笑):
用一個關(guān)鍵字和一個關(guān)聯(lián)策略, 給一個已存在的對象設(shè)置一個關(guān)聯(lián)值.

它的聲明是:

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

結(jié)合上面的解釋, 對應(yīng)著聲明中的參數(shù), 很容易知道四個參數(shù)的含義了:
object: 給哪個對象關(guān)聯(lián)值;
key: 給 object 關(guān)聯(lián)值, 肯定就是要其他地方使用這個關(guān)聯(lián)值, 不然干嘛關(guān)聯(lián), 自然聯(lián)想到 key-value 的取值方法;
value: 不用說, 自然是關(guān)聯(lián)什么值;
policy: 這個需要深究一下

接下來深究一下 policy 的類型 objc_AssociationPolicy:

查看一下官方文檔就知道, objc_AssociationPolicy枚舉 類型:

/**
 * Policies related to associative references.
 * These are options to objc_setAssociatedObject()
 */
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. */
};

去掉前綴 OBJC_ASSOCIATION_ 剩下的 assign, retain, copy, nonatomic 是不是有種熟悉的味道, 不就是我們在定義屬性 @property 的時候使用的關(guān)鍵詞么~ 這下對 objc_setAssociatedObject 就完全了解了.

2. objc_getAssociatedObject

Returns the value associated with a given object for a given key.

很明顯, 這個上面的 objc_setAssociatedObject 是一對方法, 上面是 setter 方法, objc_getAssociatedObjectgetter方法.

再看看它的聲明:

id objc_getAssociatedObject(id object, void *key)

知道了 setter 里面的參數(shù), getter 里面參數(shù)就不用解釋了.

要注意一點, getter 返回的 id 類型, 要和 settervalueid 類型一致. 說的好拗口, 就是那個意思, 你懂的.

兩個關(guān)鍵的 api 介紹掌握清楚了, 接下來就回到之前的主題了, 給 NNString 添加對象屬性和非對象屬性, 畢竟上面的介紹屬于理論層次, 每一個 api 只有在實際中運用自如了, 才能算得上真正的掌握了.

對象屬性的添加

1.給 NSString 創(chuàng)建一個 Category :

這時你可能已經(jīng)有疑問了, Category 不是只能添加方法, 不能添加屬性嗎? 明明說的就是添加屬性, 這里怎么還是添加 Category 呢 ? 帶著你的疑問繼續(xù)往下看...

File >> New >> File.. >> [select iOS, others are OK too] >> [select Objective-C File] >> File:[enter CategoryName] >> File Type:[select "Category"] >> Class:[enter "NSString"] >> Next >> ...

2.在 .h 文件中聲明一對 settergetter 方法:

注意命名規(guī)則

- (void)setStrFlag:(NSString *)strFlag;
- (NSString *)strFlag;

3.在 .m 文件中引入 runtime 頭文件:

畢竟使用 runtime 機制, 頭文件自然不可缺少

#import <objc/runtime.h>

4.在 .m 文件中實現(xiàn) .h 中的一對方法:

- (void)setStrFlag:(NSString *)strFlag {
    // void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    objc_setAssociatedObject(self, const void *key, strFlag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)strFlag {
    // id objc_getAssociatedObject(id object, const void *key)
    return objc_getAssociatedObject(self, const void *key);
}

現(xiàn)在除了參數(shù) key 沒填入, 其他參數(shù)都準(zhǔn)備好了.
熟悉 C 語言的同學(xué)一定對 const void * 類型一定不陌生吧!
沒錯, void * 就是 C 語言中令人頭疼的指針類型, 指向的是某個變量在內(nèi)存中的首地址, const 是修飾這個指針的內(nèi)容是常量, 不能修改.
當(dāng)然, 這里我們沒必要思考指針那些頭疼的問題了, 只需要知道 key 其實是個指針類型就 OK 了.

那我們就隨便定義一個變量, 然后把它的指針獲取到, 作為 key 值傳進去就 OK 了:

@implementation NSString (Ass)

NSString *strKey;

- (void)setStrFlag:(NSString *)strFlag {
    // void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
//    SEL key = @selector(strFlag);
    objc_setAssociatedObject(self, &strKey, strFlag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)strFlag {
    // id objc_getAssociatedObject(id object, const void *key)
    return objc_getAssociatedObject(self, &strKey);
}

@end

發(fā)現(xiàn)編譯器并沒有任何 errorwarning, 這也表明指針那些頭疼的問題, 在這里我們的確不需要去考慮的.

不知道你剛才的疑云還在不在? 不管在不在, 現(xiàn)在回到 .h 文件:

@interface NSString (Ass)

- (void)setStrFlag:(NSString *)strFlag;

- (NSString *)strFlag;

@end

有沒有發(fā)現(xiàn), 這對 存取器 (accessor) 就是我們在定義屬性的時候, 編譯器自動給我們創(chuàng)建的兩個方法, 既然我們在 .m 文件中都已經(jīng)實現(xiàn)了這兩個方法, 那么在 .h 中是否可以用一個屬性來代替這兩個方法呢?

@interface NSString (Ass)

/**
 額外增加的屬性
 */
@property (nonatomic, copy) NSString *strFlag;

// 對象屬性的set和get
//- (void)setStrFlag:(NSString *)strFlag;
//- (NSString *)strFlag;

@end

替換之后, 編譯器并沒有任何 errorwarning , 這說明剛才的猜想是正確的.

至此, 一個 NSString * 類型的屬性 strFlag 是不是就冠冕堂皇地加到 NSString 類里面去了.

這時, 你一定會迫不及待的去驗證這個方法是不是可行:

引入上面創(chuàng)建類別的頭文件:

#import "NSString+Ass.h"

創(chuàng)建一個 NSString 對象:

NSString *str = [NSString new];

嘗試 . 出剛才添加的屬性 strFlag :

str.str

突然發(fā)現(xiàn):


Amazing...

這說明 strFlag 屬性的確添加進去了!
如果你還不信, 你可以繼續(xù)復(fù)制, 然后打印看看...

至此, 對象屬性已經(jīng)添加成功了, 接下來添加非對象屬性了

非對象屬性的添加

基本和對象屬性的添加差不多

.h

/**
 額外增加的屬性2
 */
@property (assign) int intFlag;

//// 非對象屬性的set和get
//- (void)setIntFlag:(int)intFlag;
//- (int)intFlag;

.m

int intKey;

- (void)setIntFlag:(int)flag {
    // void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    objc_setAssociatedObject(self, &intKey, @(flag), OBJC_ASSOCIATION_ASSIGN);
}

- (int)intFlag {
    // id objc_getAssociatedObject(id object, const void *key)
    NSNumber *t = objc_getAssociatedObject(self,&intKey);
    return (int)[t integerValue];
}

注意: objc_setAssociatedObject 函數(shù)的第三個參數(shù)接受的是 id 類型, 而 int 不是 id 類型, 所有將它轉(zhuǎn)為 NSNumber 類型后再傳入.
同理, objc_getAssociatedObject 返回的是 NSNumber 類型, 轉(zhuǎn)為 int 后再返回.

至此, 兩種類型的屬性已經(jīng)添加完成了. 其實在 .m 文件中添加的那兩個 key 還是可以優(yōu)化的:

那兩個 api 只需要接受 void * 類型就行了, 而剛才在 .m 文件中 那兩個 key 值我分別傳入的是 NSString **int * 類型.
學(xué)習(xí)過 C 語言的同學(xué)都知道在 C 語言中, void * 可以指向任何指針.
iOS 中, 給一個按鈕添加點擊事件的時候, action 接受的就是一個 SEL 類型, 查看文檔不難發(fā)現(xiàn), 系統(tǒng)對 SEL 的定義是:

typedef struct objc_selector *SEL;

不難發(fā)現(xiàn), SEL 其實也是一種指針類型, 那么是不是可以用 SEL 類型的值作為 key 呢?

馬上修改 .m 文件:

- (void)setStrFlag:(NSString *)flag {
    // void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    SEL key = @selector(strFlag);
    
    NSLog(@"%p", key);
    
    objc_setAssociatedObject(self, key, flag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)strFlag {
    // id objc_getAssociatedObject(id object, const void *key)
    return objc_getAssociatedObject(self, _cmd);
}

打兩個斷點查看一下:

key值的詳情
Paste_Image.png

strFlag 方法中就沒必要在獲取自身的函數(shù)指針了, SEL key = @selector(strFlag); , _cmd 是就是一個指向方法自身的宏 .

在非屬性的方法里如法炮制的替換一下就可以了, 注意要用SEL key = @selector(intFlag);, 不能再用 SEL key = @selector(strFlag);了.


至此, 給已有類添加屬性的方法就完美實現(xiàn)了.

如果代碼中有什么 bug 或者需要改進的地方, 還望海涵, 同時歡迎在下方留言~

不要吝嗇您那寶貴的??&★就好, 您的支持是我分享的動力~??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巫橄,一起剝皮案震驚了整個濱河市妙痹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狭瞎,死亡現(xiàn)場離奇詭異今瀑,居然都是意外死亡,警方通過查閱死者的電腦和手機拗引,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門借宵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人矾削,你說我怎么就攤上這事壤玫。” “怎么了哼凯?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵欲间,是天一觀的道長。 經(jīng)常有香客問我断部,道長猎贴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任蝴光,我火速辦了婚禮她渴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔑祟。我一直安慰自己趁耗,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布疆虚。 她就那樣靜靜地躺著苛败,像睡著了一般。 火紅的嫁衣襯著肌膚如雪径簿。 梳的紋絲不亂的頭發(fā)上著拭,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音牍帚,去河邊找鬼儡遮。 笑死,一個胖子當(dāng)著我的面吹牛暗赶,可吹牛的內(nèi)容都是我干的鄙币。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蹂随,長吁一口氣:“原來是場噩夢啊……” “哼十嘿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岳锁,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绩衷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咳燕,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡勿决,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了招盲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片低缩。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖曹货,靈堂內(nèi)的尸體忽然破棺而出咆繁,到底是詐尸還是另有隱情,我是刑警寧澤顶籽,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布玩般,位于F島的核電站,受9級特大地震影響礼饱,放射性物質(zhì)發(fā)生泄漏坏为。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一慨仿、第九天 我趴在偏房一處隱蔽的房頂上張望久脯。 院中可真熱鬧纳胧,春花似錦镰吆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至核行,卻和暖如春牢硅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芝雪。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工减余, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惩系。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓位岔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親堡牡。 傳聞我的和親對象是個殘疾皇子抒抬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,726評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,569評論 33 466
  • 對于從事 iOS 開發(fā)人員來說晤柄,所有的人都會答出【runtime 是運行時】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,726評論 7 64
  • 住的地方是一片老的居民區(qū)赚抡,沒錯,就是這種一抬頭就能看到錯綜復(fù)雜交匯著的電線的地方捉撮。原來是閘北區(qū)怕品,現(xiàn)在洋氣地變成了靜...
    Luna_Lu閱讀 361評論 0 0
  • 一天早上肉康,和幾個朋友圍一起聊天, 突然一個小男孩來到我面前灼舍, 說:“阿姨吼和,送給你∑锼兀” 那是兩朵小小的花炫乓,真真小,估...
    兜兜轉(zhuǎn)轉(zhuǎn)123閱讀 198評論 0 0