(轉(zhuǎn))Objective-C Runtime 運(yùn)行時(shí)之六:拾遺

前面幾篇基本介紹了runtime中的大部分功能惰匙,包括對(duì)類(lèi)與對(duì)象技掏、成員變量與屬性、方法與消息项鬼、分類(lèi)與協(xié)議的處理哑梳。runtime大部分的功能都是圍繞這幾點(diǎn)來(lái)實(shí)現(xiàn)的。

本章的內(nèi)容并不算重點(diǎn)秃臣,主要針對(duì)前文中對(duì)Objective-C Runtime Reference內(nèi)容遺漏的地方做些補(bǔ)充涧衙。當(dāng)然這并不能包含所有的內(nèi)容。runtime還有許多內(nèi)容奥此,需要讀者去研究發(fā)現(xiàn)弧哎。

super

在Objective-C中,如果我們需要在類(lèi)的方法中調(diào)用父類(lèi)的方法時(shí)稚虎,通常都會(huì)用到super撤嫩,如下所示:

@interface MyViewController: UIViewController
@end
@implementation MyViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // do something
    ...
}
@end

如何使用super我們都知道。現(xiàn)在的問(wèn)題是蠢终,它是如何工作的呢序攘?

首先我們需要知道的是super與self不同。self是類(lèi)的一個(gè)隱藏參數(shù)寻拂,每個(gè)方法的實(shí)現(xiàn)的第一個(gè)參數(shù)即為self程奠。而super并不是隱藏參數(shù),它實(shí)際上只是一個(gè)”編譯器標(biāo)示符”祭钉,它負(fù)責(zé)告訴編譯器瞄沙,當(dāng)調(diào)用viewDidLoad方法時(shí),去調(diào)用父類(lèi)的方法慌核,而不是本類(lèi)中的方法距境。而它實(shí)際上與self指向的是相同的消息接收者。為了理解這一點(diǎn)垮卓,我們先來(lái)看看super的定義:

struct objc_super { id receiver; Class superClass; };

這個(gè)結(jié)構(gòu)體有兩個(gè)成員:

  1. receiver:即消息的實(shí)際接收者
  2. superClass:指針當(dāng)前類(lèi)的父類(lèi)
    當(dāng)我們使用super來(lái)接收消息時(shí)垫桂,編譯器會(huì)生成一個(gè)objc_super結(jié)構(gòu)體。就上面的例子而言粟按,這個(gè)結(jié)構(gòu)體的receiver就是MyViewController對(duì)象诬滩,與self相同霹粥;superClass指向MyViewController的父類(lèi)UIViewController。

接下來(lái)碱呼,發(fā)送消息時(shí)蒙挑,不是調(diào)用objc_msgSend函數(shù),而是調(diào)用objc_msgSendSuper函數(shù)愚臀,其聲明如下:

id objc_msgSendSuper ( struct objc_super *super, SEL op, ... );

該函數(shù)第一個(gè)參數(shù)即為前面生成的objc_super結(jié)構(gòu)體忆蚀,第二個(gè)參數(shù)是方法的selector。該函數(shù)實(shí)際的操作是:從objc_super結(jié)構(gòu)體指向的superClass的方法列表開(kāi)始查找viewDidLoad的selector姑裂,找到后以objc->receiver去調(diào)用這個(gè)selector馋袜,而此時(shí)的操作流程就是如下方式了

objc_msgSend(objc_super->receiver, @selector(viewDidLoad))

由于objc_super->receiver就是self本身,所以該方法實(shí)際與下面這個(gè)調(diào)用是相同的:

objc_msgSend(self, @selector(viewDidLoad))

為了便于理解舶斧,我們看以下實(shí)例:

@interface MyClass : NSObject
@end
@implementation MyClass
- (void)test {
    NSLog(@"self class: %@", self.class);
    NSLog(@"super class: %@", super.class);
}
@end

調(diào)用MyClass的test方法后欣鳖,其輸出是:

2014-11-08 15:55:03.256 [824:209297] self class: MyClass
2014-11-08 15:55:03.256 [824:209297] super class: MyClass

從上例中可以看到,兩者的輸出都是MyClass茴厉。大家可以自行用上面介紹的內(nèi)容來(lái)梳理一下泽台。

庫(kù)相關(guān)操作

庫(kù)相關(guān)的操作主要是用于獲取由系統(tǒng)提供的庫(kù)相關(guān)的信息,主要包含以下函數(shù):

// 獲取所有加載的Objective-C框架和動(dòng)態(tài)庫(kù)的名稱(chēng)
const char ** objc_copyImageNames ( unsigned int *outCount );
// 獲取指定類(lèi)所在動(dòng)態(tài)庫(kù)
const char * class_getImageName ( Class cls );
// 獲取指定庫(kù)或框架中所有類(lèi)的類(lèi)名
const char ** objc_copyClassNamesForImage ( const char *image, unsigned int *outCount );

通過(guò)這幾個(gè)函數(shù)矾缓,我們可以了解到某個(gè)類(lèi)所有的庫(kù)怀酷,以及某個(gè)庫(kù)中包含哪些類(lèi)。如下代碼所示:

NSLog(@"獲取指定類(lèi)所在動(dòng)態(tài)庫(kù)");
NSLog(@"UIView's Framework: %s", class_getImageName(NSClassFromString(@"UIView")));
NSLog(@"獲取指定庫(kù)或框架中所有類(lèi)的類(lèi)名");
const char ** classes = objc_copyClassNamesForImage(class_getImageName(NSClassFromString(@"UIView")), &outCount);
for (int i = 0; i < outCount; i++) {
    NSLog(@"class name: %s", classes[i]);
}

其輸出結(jié)果如下:

2014-11-08 12:57:32.689 [747:184013] 獲取指定類(lèi)所在動(dòng)態(tài)庫(kù)
2014-11-08 12:57:32.690 [747:184013] UIView's Framework: /System/Library/Frameworks/UIKit.framework/UIKit
2014-11-08 12:57:32.690 [747:184013] 獲取指定庫(kù)或框架中所有類(lèi)的類(lèi)名
2014-11-08 12:57:32.691 [747:184013] class name: UIKeyboardPredictiveSettings
2014-11-08 12:57:32.691 [747:184013] class name: _UIPickerViewTopFrame
2014-11-08 12:57:32.691 [747:184013] class name: _UIOnePartImageView
2014-11-08 12:57:32.692 [747:184013] class name: _UIPickerViewSelectionBar
2014-11-08 12:57:32.692 [747:184013] class name: _UIPickerWheelView
2014-11-08 12:57:32.692 [747:184013] class name: _UIPickerViewTestParameters
......

塊操作

我們都知道block給我們帶到極大的方便嗜闻,蘋(píng)果也不斷提供一些使用block的新的API蜕依。同時(shí),蘋(píng)果在runtime中也提供了一些函數(shù)來(lái)支持針對(duì)block的操作琉雳,這些函數(shù)包括:

// 創(chuàng)建一個(gè)指針函數(shù)的指針样眠,該函數(shù)調(diào)用時(shí)會(huì)調(diào)用特定的block
IMP imp_implementationWithBlock ( id block );
// 返回與IMP(使用imp_implementationWithBlock創(chuàng)建的)相關(guān)的block
id imp_getBlock ( IMP anImp );
// 解除block與IMP(使用imp_implementationWithBlock創(chuàng)建的)的關(guān)聯(lián)關(guān)系,并釋放block的拷貝
BOOL imp_removeBlock ( IMP anImp );
  • imp_implementationWithBlock函數(shù):參數(shù)block的簽名必須是method_return_type ^(id self, method_args …)形式的翠肘。

該方法能讓我們使用block作為IMP檐束。如下代碼所示:

@interface MyRuntimeBlock : NSObject
@end    
@implementation MyRuntimeBlock
@end
// 測(cè)試代碼
IMP imp = imp_implementationWithBlock(^(id obj, NSString *str) {
    NSLog(@"%@", str);
});
class_addMethod(MyRuntimeBlock.class, @selector(testBlock:), imp, "v@:@");
MyRuntimeBlock *runtime = [[MyRuntimeBlock alloc] init];
[runtime performSelector:@selector(testBlock:) withObject:@"hello world!"];

輸出結(jié)果是

2014-11-09 14:03:19.779 [1172:395446] hello world!

弱引用操作

// 加載弱引用指針引用的對(duì)象并返回
id objc_loadWeak ( id *location );
// 存儲(chǔ)__weak變量的新值
id objc_storeWeak ( id *location, id obj );
  • objc_loadWeak函數(shù):該函數(shù)加載一個(gè)弱指針引用的對(duì)象,并在對(duì)其做retain和autoreleasing操作后返回它束倍。這樣被丧,對(duì)象就可以在調(diào)用者使用它時(shí)保持足夠長(zhǎng)的生命周期。該函數(shù)典型的用法是在任何有使用__weak變量的表達(dá)式中使用肌幽。

● objc_storeWeak函數(shù):該函數(shù)的典型用法是用于__weak變量做為賦值對(duì)象時(shí)晚碾。

這兩個(gè)函數(shù)的具體實(shí)施在此不舉例抓半,有興趣的小伙伴可以參考《Objective-C高級(jí)編程:iOS與OS X多線程和內(nèi)存管理》中對(duì)__weak實(shí)現(xiàn)的介紹喂急。

宏定義

在runtime中,還定義了一些宏定義供我們使用笛求,有些值我們會(huì)經(jīng)常用到廊移,如表示BOOL值的YES/NO糕簿;而有些值不常用,如OBJC_ROOT_CLASS狡孔。在此我們做一個(gè)簡(jiǎn)單的介紹懂诗。

布爾值
#define YES  (BOOL)1
#define NO   (BOOL)0

這兩個(gè)宏定義定義了表示布爾值的常量,需要注意的是YES的值是1苗膝,而不是非0值殃恒。

空值
#define nil  __DARWIN_NULL
#define Nil  __DARWIN_NULL

其中nil用于空的實(shí)例對(duì)象,而Nil用于空類(lèi)對(duì)象辱揭。

分發(fā)函數(shù)原型
#define OBJC_OLD_DISPATCH_PROTOTYPES  1

該宏指明分發(fā)函數(shù)是否必須轉(zhuǎn)換為合適的函數(shù)指針類(lèi)型离唐。當(dāng)值為0時(shí),必須進(jìn)行轉(zhuǎn)換

Objective-C根類(lèi)
#define OBJC_ROOT_CLASS

如果我們定義了一個(gè)Objective-C根類(lèi)问窃,則編譯器會(huì)報(bào)錯(cuò)亥鬓,指明我們定義的類(lèi)沒(méi)有指定一個(gè)基類(lèi)。這種情況下域庇,我們就可以使用這個(gè)宏定義來(lái)避過(guò)這個(gè)編譯錯(cuò)誤嵌戈。該宏在iOS 7.0后可用。

其實(shí)在NSObject的聲明中听皿,我們就可以看到這個(gè)宏的身影熟呛,如下所示:

__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

我們可以參考這種方式來(lái)定義我們自己的根類(lèi)。

局部變量存儲(chǔ)時(shí)長(zhǎng)
#define NS_VALID_UNTIL_END_OF_SCOPE

該宏表明存儲(chǔ)在某些局部變量中的值在優(yōu)化時(shí)不應(yīng)該被編譯器強(qiáng)制釋放写穴。

我們將局部變量標(biāo)記為id類(lèi)型或者是指向ObjC對(duì)象類(lèi)型的指針惰拱,以便存儲(chǔ)在這些局部變量中的值在優(yōu)化時(shí)不會(huì)被編譯器強(qiáng)制釋放。相反啊送,這些值會(huì)在變量再次被賦值之前或者局部變量的作用域結(jié)束之前都會(huì)被保存偿短。

關(guān)聯(lián)對(duì)象行為
enum {
    OBJC_ASSOCIATION_ASSIGN  = 0,
    OBJC_ASSOCIATION_RETAIN_NONATOMIC  = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC  = 3,
    OBJC_ASSOCIATION_RETAIN  = 01401,
    OBJC_ASSOCIATION_COPY  = 01403
};

這幾個(gè)值在前面已介紹過(guò),在此不再重復(fù)馋没。

總結(jié)

至此昔逗,本系列對(duì)runtime的整理已完結(jié)。當(dāng)然這只是對(duì)runtime的一些基礎(chǔ)知識(shí)的歸納篷朵,力圖起個(gè)拋磚引玉的作用勾怒。還有許多關(guān)于runtime有意思東西還需要讀者自己去探索發(fā)現(xiàn)。

參考

  1. Objective-C Runtime Reference
  2. iOS:Objective-C中Self和Super詳解
  3. Objective-C的動(dòng)態(tài)特性
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末声旺,一起剝皮案震驚了整個(gè)濱河市笔链,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腮猖,老刑警劉巖鉴扫,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異澈缺,居然都是意外死亡坪创,警方通過(guò)查閱死者的電腦和手機(jī)炕婶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)莱预,“玉大人柠掂,你說(shuō)我怎么就攤上這事∫谰冢” “怎么了涯贞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)危喉。 經(jīng)常有香客問(wèn)我肩狂,道長(zhǎng),這世上最難降的妖魔是什么姥饰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任傻谁,我火速辦了婚禮,結(jié)果婚禮上列粪,老公的妹妹穿的比我還像新娘审磁。我一直安慰自己,他們只是感情好岂座,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布态蒂。 她就那樣靜靜地躺著,像睡著了一般费什。 火紅的嫁衣襯著肌膚如雪钾恢。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天鸳址,我揣著相機(jī)與錄音瘩蚪,去河邊找鬼。 笑死稿黍,一個(gè)胖子當(dāng)著我的面吹牛疹瘦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播巡球,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼言沐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了酣栈?” 一聲冷哼從身側(cè)響起险胰,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎矿筝,沒(méi)想到半個(gè)月后起便,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年缨睡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陈辱。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奖年,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沛贪,到底是詐尸還是另有隱情陋守,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布利赋,位于F島的核電站水评,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏媚送。R本人自食惡果不足惜中燥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塘偎。 院中可真熱鬧疗涉,春花似錦、人聲如沸吟秩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涵防。三九已至闹伪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間壮池,已是汗流浹背偏瓤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椰憋,地道東北人硼补。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像熏矿,于是被迫代替她去往敵國(guó)和親已骇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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

  • objc_getAssociatedObject返回與給定鍵的特定對(duì)象關(guān)聯(lián)的值票编。ID objc_getAssoci...
    有一種再見(jiàn)叫青春閱讀 1,582評(píng)論 0 7
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉褪储,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,715評(píng)論 0 9
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí),它使得 Objective-C 如虎添翼慧域,具備了靈活的...
    lylaut閱讀 800評(píng)論 0 4
  • 原文出處:南峰子的技術(shù)博客 Objective-C語(yǔ)言是一門(mén)動(dòng)態(tài)語(yǔ)言鲤竹,它將很多靜態(tài)語(yǔ)言在編譯和鏈接時(shí)期做的事放到了...
    _燴面_閱讀 1,229評(píng)論 1 5
  • 文中的實(shí)驗(yàn)代碼我放在了這個(gè)項(xiàng)目中。 以下內(nèi)容是我通過(guò)整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 923評(píng)論 0 6