Objective-C Runtime之關(guān)聯(lián)對象

文章也同時在個人博客 http://kimihe.com/更新

引言

最近在補(bǔ)習(xí)《Effective Objective-C 2.0》。其中涉及到部分OC runtime的知識,runtime是OC的一個重要特性谈宛,掌握它對于理解OC有著巨大的幫助秽誊,也能夠提升我們的開發(fā)能力每聪。

OC的runtime可以將我們的程序在運(yùn)行期進(jìn)行一系列控制忍法,表象看起來如同我們可以動態(tài)修改正在運(yùn)行的程序面粮。我將其視為OC語法的封裝 -> C高級API的轉(zhuǎn)化魔法灾挨,因?yàn)樵谧罱K邑退,我們的程序還是會被翻譯成是C(以及C++)的樣式。在C的層面上劳澄,我們或許能夠忽略runtime這種說法地技,畢竟C幾乎可以做任何事情。

可以說秒拔,如果你了解runtime的設(shè)計(jì)理念莫矗,你將可以親手用C實(shí)現(xiàn)一個類,包含一些最基本的面相對象的特性砂缩。runtime也是開源的作谚,有興趣不妨學(xué)習(xí)一下。

我們平時在學(xué)習(xí)OC時庵芭,可能都聽說過Category的作用妹懒,可以拆分代碼,增加方法等等双吆。不過在一般意義下無法增加實(shí)例變量眨唬,這可能會阻礙我們實(shí)現(xiàn)一些有趣的idea会前,但是runtime可以非常簡單地跨過這個限制,將我們帶入OC特性的更深層次匾竿。

本文將通過一個非常簡單的Demo瓦宜,來演示runtime中關(guān)聯(lián)對象的作用。我們修改UIAlertView搂橙,給它增加一個傳入ActionBlock的方法歉提,我們可以直接在ActionBlock中編寫邏輯代碼,避免了在相關(guān)protocol的方法中進(jìn)行操作的繁雜区转。雖然UIAlertView在iOS9.0后被UIAlertController代替,但卻非常適合用于教學(xué)演示版扩。

好書推薦

《Effective Objective-C 2.0》是一本非常適合深入學(xué)習(xí)OC內(nèi)涵的書废离,很多開發(fā)中的疑惑都可以在書中找到較為科學(xué)的解答。不過礁芦,這本書需要一定的iOS開發(fā)積累蜻韭,可能不適合初學(xué)者。

代碼地址

https://github.com/kimihe/KMiOSLib/tree/master/ObjcRuntimeResearch/UIAlertViewMagic

原理詳解

思路

我們的目的很明確柿扣,給UIAlertView增加可以傳入邏輯代碼塊的ActionBlock接口肖方,這樣我們就不需要把這些代碼寫在相關(guān)protocol的方法中了,使代碼更加集中未状,便于理解和管理俯画。我們的思路如下:

  • 通過Category來擴(kuò)充這個UIAlertView類。
  • 遵循“勿在分類中聲明屬性”這一原則司草,不聲明ActionBlock的@property艰垂,而是直接提供一個setup方法。
  • 通過runtime的objc_associatedObject來“變相”給分類增加實(shí)例變量埋虹,即把我們要增加的實(shí)例變量(ActionBlock)關(guān)聯(lián)到UIAlerView分類

實(shí)現(xiàn)

我們新增一個分類UIAlertView+ActionBlock猜憎,在頭文件中聲明我們的ActionBlock類型,并增加一個傳block的方法搔课。

#import <UIKit/UIKit.h>

typedef void(^ActionBlock)(UIAlertView*, NSInteger);

@interface UIAlertView (ActionBlock) <UIAlertViewDelegate>

/**
 設(shè)置一個ActionBlock

 @param action 需要傳入的block
 */
- (void)setupActionBlock:(ActionBlock)action;

@end

在實(shí)現(xiàn)文件中我們只需要實(shí)現(xiàn)兩個模塊胰柑,一是完成關(guān)聯(lián)對象,二是在何時的時機(jī)調(diào)用外部傳入的ActionBlock爬泥。

完成關(guān)聯(lián)對象

在我們添加的方法中柬讨,利用runtime完成關(guān)聯(lián)對象,變相存儲外部傳入的block急灭,如下:

static const void *KMAlertViewKey = "KMAlertViewKey";
...
- (void)setupActionBlock:(ActionBlock)action
{
    objc_setAssociatedObject(self, KMAlertViewKey, action, OBJC_ASSOCIATION_COPY_NONATOMIC);
    self.delegate = self;
}
...

代碼就只有兩行姐浮,第一行是設(shè)置關(guān)聯(lián)對象,把傳入的block(action)關(guān)聯(lián)到self(UIAlerView分類)葬馋,其中的KMAlertViewKey是一個唯一區(qū)分標(biāo)識符卖鲤,類似于字典的鍵肾扰,以此來尋找我們關(guān)聯(lián)的對象。OBJC_ASSOCIATION_COPY_NONATOMIC是內(nèi)存管理語義蛋逾,我們強(qiáng)調(diào):block通過copy存放在堆上集晚。

第二行重新覆蓋delegate,將其引導(dǎo)至本實(shí)現(xiàn)文件中区匣,從而實(shí)現(xiàn)protocol的方法偷拔,并適時進(jìn)行ActionBlock的調(diào)用。

調(diào)用ActionBlock

經(jīng)過上述的delegate重定向亏钩,我們可以攔截相關(guān)方法莲绰,并在其中調(diào)用ActionBlock,完成外部傳入的邏輯代碼姑丑,如下:

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    ActionBlock action = objc_getAssociatedObject(self, KMAlertViewKey);
    action(alertView, buttonIndex);
}

和設(shè)置關(guān)聯(lián)對象一樣蛤签,代碼的第一行,我們?nèi)〕龇诸愱P(guān)聯(lián)的ActionBlock栅哀。然后在代碼的第二行進(jìn)行調(diào)用震肮。

使用演示

ViewController.m中進(jìn)行使用,代碼如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Title" message:@"Message" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
    [alert setupActionBlock:^(UIAlertView *alertView, NSInteger buttonIndex) {
        NSLog(@"You did click button index %ld", (long)buttonIndex);
    }];
    [alert show];
}

注意留拾,我們在生成一個alert時會聲明其delegate戳晌,但是當(dāng)執(zhí)行到后續(xù)的setupActionBlock:方法時,我們其實(shí)會覆蓋這個delegate痴柔,將其引導(dǎo)至新增的分類中沦偎,所以在使用時,我們可以完全不編寫相關(guān)的protocol方法竞帽,而是直接在block中傳入邏輯代碼扛施。

運(yùn)行一下,點(diǎn)擊alert的OK按鈕屹篓,控制臺輸出符合預(yù)期:

2016-12-26 16:05:53.776 UIAlertViewMagic[2082:70283] You did click button index 1

總結(jié)

本文通過給UIAlertView增加一個actionBlock回調(diào)疙渣,演示了OC runtime的“關(guān)聯(lián)對象”(objc_associatedObject)的功能。算是對OC runtime的涉獵堆巧。

要點(diǎn)

  • iOS9.0后妄荔,你應(yīng)該使用UIAlertController,棄用UIAlerView谍肤!
  • 代碼主要講解了如何使用runtime的objc_associatedObject啦租,模擬部分UIAlertController的功能,使用了category來增加一個方法荒揣,但在實(shí)際開發(fā)中篷角,還是建議通過繼承使用子類來實(shí)現(xiàn)。畢竟category中增加實(shí)例變量屬于一種trick技巧系任。
  • 我們需要一個內(nèi)部block來存儲外部傳進(jìn)來的action恳蹲,但是category無法在一般意義上增加實(shí)例變量虐块, 因此可以使用runtime魔法實(shí)現(xiàn)。
  • 我們依舊遵照“勿在分類中聲明屬性”這一原則嘉蕾,不聲明void (^block)(UIAlertView*, NSInteger)的@property贺奠,直接提供一個setup方法。

參考文獻(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末错忱,一起剝皮案震驚了整個濱河市儡率,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌以清,老刑警劉巖儿普,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異玖媚,居然都是意外死亡箕肃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門今魔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人障贸,你說我怎么就攤上這事错森。” “怎么了篮洁?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵涩维,是天一觀的道長。 經(jīng)常有香客問我袁波,道長瓦阐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任篷牌,我火速辦了婚禮睡蟋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枷颊。我一直安慰自己戳杀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布夭苗。 她就那樣靜靜地躺著信卡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪题造。 梳的紋絲不亂的頭發(fā)上傍菇,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音界赔,去河邊找鬼丢习。 笑死牵触,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泛领。 我是一名探鬼主播荒吏,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渊鞋!你這毒婦竟也來了绰更?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤锡宋,失蹤者是張志新(化名)和其女友劉穎儡湾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體执俩,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡徐钠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了役首。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尝丐。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖衡奥,靈堂內(nèi)的尸體忽然破棺而出爹袁,到底是詐尸還是另有隱情,我是刑警寧澤矮固,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布失息,位于F島的核電站,受9級特大地震影響档址,放射性物質(zhì)發(fā)生泄漏盹兢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一守伸、第九天 我趴在偏房一處隱蔽的房頂上張望绎秒。 院中可真熱鬧,春花似錦含友、人聲如沸替裆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辆童。三九已至,卻和暖如春惠赫,著一層夾襖步出監(jiān)牢的瞬間把鉴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庭砍,地道東北人场晶。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像怠缸,于是被迫代替她去往敵國和親诗轻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

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