首先說明觉壶,這篇文章幾乎都是抄錄的別人的博客,簡書文章缝裤,在此總結屏轰,只是為了方便記憶和以后閱讀,如果有什么失禮的地方憋飞,請大家及時指正霎苗。
大神們:(推薦閱讀原文)
好了,抄錄正式開始
每個Objective-C對象都有一個隱藏的數(shù)據(jù)結構榛做,這個數(shù)據(jù)結構是Objective-C對象的第一個成員變量唁盏,它就是isa指針内狸。
這個isa到底是什么呢?官方介紹是這樣的:
Every object is connected to the run-time system through itsisa instance variable, inherited from the NSObject class.isa identifies the object's class; it points to a structurethat's compiled from the class definition. Through isa, anobject can find whatever information it needs at run timesuch asits place in the inheritance hierarchy, the size and structure ofits instance variables, and the location of the methodimplementations it can perform in response to messages.
可見厘擂,一個對象(Object)的isa指向了這個對象的類(Class)昆淡,而這個對象的類(Class)的isa指向了metaclass。這樣我們就可以找到靜態(tài)方法和變量了刽严。
Objective-C的運行時是動態(tài)的昂灵,它能讓你在運行時為類添加方法或者去除方法以及使用反射。這在其它語言是不多見的舞萄。
類的實例對象的 isa 指向它的類眨补;類的 isa 指向該類的 metaclass;
類的 super_class 指向其父類倒脓,如果該類為根類則值為 NULL撑螺;
metaclass 的 isa 指向根 metaclass,如果該 metaclass 是根 metaclass則指向自身崎弃;
metaclass 的 super_class 指向父 metaclass甘晤,如果該 metaclass 是根 metaclass則指向該 metaclass 對應的類;
Object-C 為每個類的定義生成兩個 objc_class 饲做,一個普通的 class安皱,另一個即metaclass。我們可以在運行期創(chuàng)建這兩個 objc_class 數(shù)據(jù)結構艇炎,然后使用 objc_addClass將 class注冊到運行時系統(tǒng)中酌伊,以此實現(xiàn)動態(tài)地創(chuàng)建一個新的類。
在NSObject.h里面:
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
再點開 Class 的定義:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
}
這一些定義對于懂的人自然懂缀踪,不會的人根本看不懂居砖。建議看完下面的例子,再點開參考里面的鏈接仔細看一遍驴娃。
現(xiàn)在我們知道的是奏候,對于我們新建的一個類,都會有一個隱藏的屬性isa唇敞,可以通過它進行一些訪問蔗草。
我們現(xiàn)在新建一個類Parent,繼承于NSObject, 里面有成員方法-(void)selectorP疆柔,類方法+(void)ClassSelectorP咒精。
再新建一個類Child,繼承于Parent旷档,里面有成員方法-(void)selectorC, 類方法+(void)ClassSelectorC模叙。
現(xiàn)在我們新建一個實例Child* child = [Chlid new];
- 當我們調(diào)用[child class] 的時候,child就會通過isa指針去找到Child的class鞋屈。
- 當我們調(diào)用[child superclass]的時候范咨,child 通過isa找到Child的class故觅,再通過super_class,找到Parent的class渠啊。
在這里输吏,再普及objc_class 的兩種類型:
class 實例對象(child、Child)的isa指向的結構體替蛉;
metaclass class的isa指向的一個結構體贯溅; - 接著,調(diào)用[child SelectorC]灭返,child通過isa找到Child的class盗迟,在class(注意看上面 struct objc_class 的定義)的方法列表里面找到SelectorC坤邪;
- 再試著調(diào)用[child SelectorP]熙含,child通過isa找到Child的class,發(fā)現(xiàn)class里面并沒有這個方法艇纺,通過class里面的super_class找到Parent的class怎静,在里面的方法列表找到了SelectorP;
- 再是類方法[Child ClassSelectorC]黔衡,Child(請注意蚓聘,大寫)通過isa找到Child的class,通過class的isa找到Child的metaclass盟劫,在metaclass的方法列表里面找到了ClassSelectorC夜牡;
- 再試著調(diào)用[Child ClassSelectorP],Child通過isa找到Child的class侣签,通過class的isa找到Child的metaclass塘装,發(fā)現(xiàn)metaclass里面并沒有這個方法,通過metaclass里面的super_class找到Parent的metaclass影所,在里面的方法列表找到了ClassSelectorP蹦肴;
- (void)viewDidLoad {
[super viewDidLoad];
Class clazz = [self class];
Class clarr = [AroundMapController class];
Class metalclazz = objc_getMetaClass("AroundMapController");
if (class_respondsToSelector(metalclazz, @selector(viewWillAppear:))) {
NSLog(@"ok");
}
if (clarr == clazz) {
NSLog(@"2 ok");
}
}
這個是驗證runtime的機制,可以把這段代碼復制到自己的controller.m里面猴娩,運行一下阴幌。記得把AroundMapController改成自己的controller的名字,還有引入頭文件<objc/runtime.h>
這是幾個例子基本上已經(jīng)涵蓋了大多數(shù)調(diào)用的情況卷中。
細心的朋友可能已經(jīng)發(fā)現(xiàn)矛双,上面的例子中,child通過isa找到的類對象蟆豫,其實就是Child 通過isa找到的class背零。(如果能理解這一點,基本上也算對objectC的isa機制也算入門)
最后為了理解class和metaclass的作用无埃,大家可以換位思考一下徙瓶,如果我們作為runtime的設計者毛雇,當開發(fā)者新建出來一個實例對象child的時候,我們應該存儲child的屬性和方法侦镇,同時又該如何響應其方法調(diào)用灵疮,最后還要記錄與Parent之間的繼承關系。
這時候壳繁,再來看看震捣,這種圖∧致可以加深對isa機制的理解:
參考
http://www.cocoachina.com/iOS/20141018/9960.html
http://blog.csdn.NET/jasonblog/article/details/7246822
http://blog.csdn.Net/totogo2010/article/details/8081253
一.isa指針
要認識什么是isa指針蒿赢,我們得先明確一點:
在Objective-C中,任何類的定義都是對象渣触。類和類的實例(對象)沒有任何本質(zhì)上的區(qū)別羡棵。任何對象都有isa指針。
那么什么是類呢嗅钻?在xcode中用快捷鍵Shift+Cmd+O 打開文件objc.h 能看到類的定義:
可以看出:
Class 是一個 objc_class 結構類型的指針, id是一個 objc_object 結構類型的指針.
我們再來看看 objc_class 的定義:
稍微解釋一下各個參數(shù)的意思:
- isa:是一個Class 類型的指針. 每個實例對象有個isa的指針,他指向?qū)ο蟮念愒肀鳦lass里也有個isa的指針, 指向meteClass(元類)。元類保存了類方法的列表养篓。當類方法被調(diào)用時秃流,先會從本身查找類方法的實現(xiàn),如果沒有柳弄,元類會向他父類查找該方法舶胀。同時注意的是:元類(meteClass)也是類,它也是對象碧注。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass).根元類的isa指針指向本身嚣伐,這樣形成了一個封閉的內(nèi)循環(huán)。
- super_class:父類应闯,如果該類已經(jīng)是最頂層的根類,那么它為NULL纤控。
- version:類的版本信息,默認為0
- info:供運行期使用的一些位標識。
- instance_size:該類的實例變量大小
- ivars:成員變量的數(shù)組
再來看看各個類實例變量的繼承關系:
- 每一個對象本質(zhì)上都是一個類的實例碉纺。其中類定義了成員變量和成員方法的列表船万。對象通過對象的isa指針指向類。
- 每一個類本質(zhì)上都是一個對象骨田,類其實是元類(meteClass)的實例耿导。元類定義了類方法的列表。類通過類的isa指針指向元類态贤。
- 所有的元類最終繼承一個根元類舱呻,根元類isa指針指向本身,形成一個封閉的內(nèi)循環(huán)。
二.runtime 機制
- runtime:指一個程序在運行(或者在被執(zhí)行)的狀態(tài)箱吕。也就是說芥驳,當你打開一個程序使它在電腦上運行的時候,那個程序就是處于運行時刻茬高。在一些編程語言中兆旬,把某些可以重用的程序或者實例打包或者重建成為“運行庫"。這些實例可以在它們運行的時候被連接或者被任何程序調(diào)用怎栽。
- objective-c中runtime:是一套比較底層的純C語言API, 屬于1個C語言庫, 包含了很多底層的C語言API丽猬。 在我們平時編寫的OC代碼中, 程序運行過程時, 其實最終都是轉(zhuǎn)成了runtime的C語言代碼。
runtime的應用:
- 動態(tài)創(chuàng)建一個類(比如KVO的底層實現(xiàn))
- 動態(tài)地為某個類添加屬性\方法, 修改屬性值\方法
- 遍歷一個類的所有成員變量(屬性)\所有方法
- 實質(zhì)上熏瞄,以上的是通過相關方法來獲取對象或者類的isa指針來實現(xiàn)的脚祟。
相關函數(shù)
- 增加
- 增加函數(shù):class_addMethod
- 增加實例變量:class_addIvar
- 增加屬性:@dynamic標簽,或者class_addMethod强饮,因為屬性其實就是由getter和setter函數(shù)組成
- 增加Protocol:class_addProtocol
- 獲取
- 獲取函數(shù)列表及每個函數(shù)的信息(函數(shù)指針由桌、函數(shù)名等等):class_getClassMethod method_getName ...
- 獲取屬性列表及每個屬性的信息:class_copyPropertyList property_getName
- 獲取類本身的信息,如類名等:class_getName class_getInstanceSize
- 獲取變量列表及變量信息:class_copyIvarList
- 替換
- 將實例替換成另一個類:object_setClass
- 替換類方法的定義:class_replaceMethod
- 其他常用方法:
- 交換兩個方法的實現(xiàn): method_exchangeImplementations.
- 設置一個方法的實現(xiàn):method_setImplementation.
三、對meta-class的補充說明(抄錄)
比較簡單的一篇英文胡陪,重點是講解meta-class沥寥。翻譯下碍舍,加深理解柠座。
原文標題:What is a meta-class in Objective-C?
原文地址:http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html
接下來將會探討一個在Objective-C中相對陌生的概念 -- meta-class。OC中的每一個類都會有一個與之相關聯(lián)的meta class片橡,但是你卻幾乎永遠也不會直接使用到妈经,它們始終籠罩著一層神秘的面紗。筆者將以運行時動態(tài)創(chuàng)建一個class為引捧书,通過剖析創(chuàng)建的class pair來弄明白到底meta-class是什么以及更深入的了解它對于OC中對象吹泡、類的意義。
- 在運行時創(chuàng)建類
以下代碼演示運行時創(chuàng)建一個NSError的子類经瓷,同時添加一個實例方法給它:
Class newClass =
objc_allocateClassPair([NSError class], "RuntimeErrorSubclass", 0);
class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
objc_registerClassPair(newClass);
- 函數(shù)ReportFunction就是添加的實例方法的具體實現(xiàn)
void ReportFunction(id self, SEL _cmd)
{
NSLog(@"This object is %p.",self);
NSLog(@"Class is %@, and super is %@.",[self class],[self superclass]);
Class currentClass = [self class];
for( int i = 1; i < 5; ++i )
{
NSLog(@"Following the isa pointer %d times gives %p",i,currentClass);
currentClass = object_getClass(currentClass);
}
NSLog(@"NSObject's class is %p", [NSObject class]);
NSLog(@"NSObject's meta class is %p",object_getClass([NSObject class]));
}
看起來一切都很簡單爆哑,運行時創(chuàng)建類只需要三步:
- 為"class pair"分配空間(使用objc_allocateClassPair).
- 為創(chuàng)建的類添加方法和成員(上例使用class_addMethod添加了一個方法)。
- 注冊你創(chuàng)建的這個類舆吮,使其可用(使用objc_registerClassPair)揭朝。
估計讀者馬上就要問:什么是“class pair"? objc_allocateClassPair只返回一個值:Class。那么pair的另一半在哪里呢色冀?
是的潭袱,估計你已經(jīng)猜到了這個另一半就是meta-class,也就是這篇短文的標題锋恬,但是要解釋清楚它是什么屯换,為什么需要它,還需要交代下OC的對象與類的相關背景与学。
一個數(shù)據(jù)結構何以成為一個對象彤悔?
每個對象都會有一個它所屬的類嘉抓。這是面向?qū)ο蟮幕靖拍睿窃贠C中晕窑,這對所有數(shù)據(jù)結構有效掌眠。任何數(shù)據(jù)結構,只要在恰當?shù)奈恢镁哂幸粋€指針指向一個class幕屹,那么蓝丙,它都可以被認為是一個對象。
在OC中望拖,一個對象所屬于哪個類渺尘,是由它的isa指針指向的。這個isa指針指向這個對象所屬的class说敏。
實際上鸥跟,OC中對象的定義是如下的樣子:
typedef struct objc_object {
Class isa;
}*id;
這個定義表明:任何以一個指向Class的指針作為首個成員的數(shù)據(jù)結構都可以被認為是一個objc_object.
最重要的特性就是,你可以向OC中的任何對象發(fā)送消息盔沫,如下這樣:
【@”stringValue" writeToFile:@"/file.txt atomically:YES encoding: NSUTF8StringEncoding error:NULL];
運行原理就是医咨,當你向一個OC對象發(fā)送消息時(上文的@“stringValue”),運行時庫會根據(jù)對象的isa指針找到這個對象所屬的類(上文為例架诞,會找到NSCFString類).這個類會包含一個所有實例方法的列表及一個指向superclass的指針以便可以找到父類的實例方法拟淮。運行時庫會在類的方法列表以及父類(們)的方法列表中尋找符合這個selector(上文為例,這個selector是"writeToFile:atomically:encoding:error")的方法谴忧。找到后即運行這個方法很泊。關鍵點就是類要定義這個你發(fā)送給對象的消息。
什么是meta-class沾谓?
typedef struct objc_class *Class;
struct objc_class{
Class isa;
Class super_class;
/*followed by runtime specific details...*/
};
- 為了可以調(diào)用類方法委造,這個類的isa指針必須指向一個包含這些類方法的類結構體。
這樣就引出了meta-class的概念:meta-class是一個類對象的類均驶。
簡單解釋下:
- 當你向一個對象發(fā)送消息時昏兆,runtime會在這個對象所屬的那個類的方法列表中查找。
- 當你向一個類發(fā)送消息時妇穴,runtime會在這個類的meta-class的方法列表中查找爬虱。
- meta-class之所以重要,是因為它存儲著一個類的所有類方法伟骨。每個類都會有一個單獨的meta-class饮潦,因為每個類的類方法基本不可能完全相同。
meta-class的類又是什么呢携狭?
完美的閉環(huán)
- meta-class继蜡,就像Class一樣,也是一個對象。你依舊可以向它發(fā)送消息調(diào)用函數(shù)稀并,自然的仅颇,meta-class也會有一個isa指針指向其所屬類。所有的meta-class使用基類的meta-class作為他們的所屬類碘举。具體而言忘瓦,任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己所屬的類。
- 根據(jù)這個規(guī)則引颈,所有的meta-class使用基類的meta-class作為它們的類耕皮,而基類的meta-class也是屬于它自己,也就是說基類的meta-class的isa指針指向它自己蝙场。(譯:完美的閉環(huán))
類和meta-class的繼承
- 就像一個類使用super_class指針指向自己的父類一樣凌停,meta-class的super_class會指向類的super_class的meta-class。一直追溯到基類的meta-class售滤,它的super_class會指向基類自身罚拟。(譯:萬物歸根)
- 這樣一來,整個繼承體系中的實例完箩、類和meta-class都派生自繼承體系中的基類赐俗。對于NSObject繼承體系來說,NSObject的實例方法對體系中所有的實例弊知、類和meta-class都是有效的阻逮;NSObject的類方法對于體系中所有的類和meta-class都是有效的。
實驗證明:
為了證實以上的論述吉捶,讓我們查看下開篇代碼中ReportFunction的輸出夺鲜。這個函數(shù)的目的就是沿著isa指針進行打印皆尔。
為了運行ErportFunction呐舔,我們需要創(chuàng)建一個實例,并調(diào)用report方法慷蠕。
id instanceOfNewClass = [[newClass alloc]initWithDomain:@"some Domain" code:0 userInfo:nil];
[instanceOfNewClass performSelector:@"report)];
[instanceOfNewClass release];
因為我們并沒有對report方法進行聲明珊拼,所以我們使用performSelector進行調(diào)用,這樣避免編譯器警告流炕。
然后ReportFunction函數(shù)會沿著isa進行檢索澎现,來告訴我們class,meta-class以及meta-class的class是什么樣的情況:
【注:ReportFunction使用object_getClass來獲取isa指針指向的類每辟,因為isa指針是一個受保護成員剑辫,你不能直接訪問其他對象的isa指針。ReportFunction沒有使用class方法是因為在一個類對象上調(diào)用這個方法是無法獲得meta-class的渠欺,它只是返回這個類而已妹蔽。(所以[NSString class]只是返回NSString類,而不是NSString的meta-class]
以下是程序的輸出:
This object is 0x10010c810.
Class is RuntimeErrorSubclass, and super is NSError.
Followingthe isa pointer 1times gives 0x10010c600
Followingthe isa pointer 2times gives 0x10010c630
Followingthe isa pointer 3times gives 0x7fff71038480
Followingthe isa pointer 4times gives 0x7fff71038480
NSObject's class is 0x7fff710384a8
NSObject's meta class is 0x7fff71038480
觀察通過isa獲得的地址:
對象的地址是 0x10010c810.
類的地址是 0x10010c600.
類的meta-class地址是 0x10010c630.
類的meta-class的類地址是 0x7fff71038480.(即NSOjbect的meta-class)
NSObject的meta-class的類地址是它自身。
這些地址的值并不重要胳岂,重要的是它們說明了文中討論的從類到meta-class到NSObject的meta-class的整個流程编整。
結論:
meta-class是類對象的類,每個類都有自己單獨的meta-class乳丰。所有的類對象并不會屬于同一個meta-class掌测。
meta-class要保證類對象具有繼承體系中基類的所有實例和類方法,以及繼承體系中的所有中間類方法产园。對于所有NSObject繼承體系下的類汞斧,NSObject的實例方法和協(xié)議方法對他們和他們meta-class的對象都要有效。
所有的meta-class使用基類的meta-class作為自己的基類什燕,對于頂層基類的meta-class也是一樣断箫,只是它指向自己而已。
再次說明:
這篇文章純屬抄錄秋冰,感謝各位大神的總結仲义,如果本文有什么不對的地方,請及時指正剑勾。