本文是參考南峰子Objective-C Runtime系列文章
做一個自己的總結(jié)褒翰,強烈推薦查看原文
1、runtime介紹
Objective-C
是一門動態(tài)語言楚里,它將很多靜態(tài)語言在編譯和連接時期做的事情放到了運行時來處理断部,這種動態(tài)語言的優(yōu)勢在于:我們寫代碼時更具有靈活性,如我們可以把消息轉(zhuǎn)發(fā)給我們想要轉(zhuǎn)發(fā)的對象班缎,或者隨意交換一個方法等蝴光。
這種特性意味著Objective-C
不僅需要一個編譯器,還需要一個運行時動態(tài)庫來執(zhí)行編譯的代碼达址。對于Objective-C
來說蔑祟,這個運行時系統(tǒng)就像一個操作系統(tǒng)一樣:讓所有工作正常運行。這個運行時系統(tǒng)就是Objc Runtime
沉唠,Objc Runtime
其實是一個Runtime
庫疆虚,基本上是用C語言
和匯編語言
寫的,使得C語言有了面向?qū)ο蟮哪芰Α?/p>
Runtime
庫主要做了以下兩件事:
- 封裝:在這個庫中,對象可以使用
C語言
中的結(jié)構(gòu)體
表示径簿,方法可以使用C函數(shù)
來實現(xiàn)罢屈,另外再加上一些額外的特性,這些結(jié)構(gòu)體和函數(shù)被runtime
函數(shù)封裝后篇亭,我們就可以在程序運行時創(chuàng)建缠捌、檢查、修改類译蒂、對象鄙币、方法了 - 找出方法的最終執(zhí)行代碼:當程序執(zhí)行
[object doSomething]
時,會向接受者(object
)發(fā)送一條消息(doSomething
)蹂随,runtime
會根據(jù)消息接受者能否響應該消息而做出不同反應
Objective-C runtime
目前有兩個版本:Modern runtime
和Legacy runtime
。Modern Runtime
覆蓋了64位的Mac OS X Apps
因惭,還有iOS Apps
岳锁,Legacy Runtime
是早期用來給32位 Mac OS X Apps
用的,已經(jīng)過時
蘋果和GNU各自維護一個開源的 runtime 版本,這兩個版本之間都在努力的保持一致蹦魔。
高級編程語言想要成為可執(zhí)行文件激率,需要先編譯成匯編語言,再匯編為機器語言勿决,機器語言是計算機唯一能識別的語言(轉(zhuǎn)換過程待研究)
2乒躺、類
Objective-C
類是有Class
類型表示,實際上是指向objc_class
結(jié)構(gòu)體的指針低缩,定義如下
typedef struct objc_class *Class;
objc_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; // 類的版本信息嘉冒,默認為0
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; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
1.isa
: 需要注意的是在Objective-C中咆繁,所有的類自身也是一個對象讳推,這個對象的Class
里面也有一個isa
指針,它指向metaClass
(元類)
super_class
:指向該類的父類玩般,如果該類已經(jīng)是最頂層的根類(如NSObject
或NSProxy
)银觅,則super_class
為NULL
name
: 類名version
:我們可以使用這個字段來提供類的版本信息。這對于對象的序列化非常有用坏为,它可是讓我們識別出不同類定義版本中實例變量布局的改變究驴。info
: 類信息instance_size
: 該類的對象大小ivars
: 成員變量的列表methodLists
: 方法列表cathe
: 用于緩存最近使用的方法。
一個接收者對象接收到一個消息時匀伏,它會根據(jù)isa
指針去查找能夠響應這個消息的對象洒忧。在實際使用中,這個對象只有一部分方法是常用的帘撰,很多方法其實很少用或者根本用不上跑慕。這種情況下,如果每次消息來時,我們都是methodLists
中遍歷一遍核行,性能勢必很差牢硅。這時,cache
就派上用場了芝雪。在我們每次調(diào)用過一個方法后减余,這個方法就會被緩存到cache
列表中,下次調(diào)用的時候runtime
就會優(yōu)先去cache
中查找惩系,如果cache
沒有位岔,才去methodLists
中查找方法。這樣堡牡,對于那些經(jīng)常用到的方法的調(diào)用抒抬,提高了調(diào)用的效率。protocols
: 協(xié)議列表
3晤柄、對象
objc_object
是表示一個類的實例的結(jié)構(gòu)體
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
這個結(jié)構(gòu)體只有一個屬性擦剑,即指向其類的isa
指針。這樣芥颈,當我們向一個Objective-C
對象發(fā)送消息時惠勒,運行時庫會根據(jù)實例對象的isa
指針找到這個實例對象所屬的類。Runtime
庫會在類的方法列表及父類的方法列表中去尋找與消息對應的selector
指向的方法,找到后即運行這個方法爬坑。
4纠屋、元類(Meta Class)
meta-class是一個類對象的類
當我們向一個對象發(fā)送消息時,runtime
會在這個對象所屬的這個類的方法列表中查找方法盾计;
而向一個類發(fā)送消息時售担,會在這個類的meta-class
的方法列表中查找方法
meta-class
之所以重要,是因為它存儲著一個類的所有類方法署辉。每個類都會有一個單獨的meta-class
灼舍,因為每個類的類方法基本不可能完全相同
meta-class
也是一個類,也可以向它發(fā)送一個消息涨薪,那么它的isa
又是指向什么呢骑素?為了不讓這種結(jié)構(gòu)無限延伸下去,Objective-C
的設(shè)計者讓所有的meta-class
的isa
指向基類的meta-class
刚夺,以此作為它們的所屬類献丑。
即,任何NSObject
繼承體系下的meta-class
都使用NSObject
的meta-class
作為自己的所屬類侠姑,而基類的meta-class
的isa
指針是指向它自己创橄。這樣就形成了一個完美的閉環(huán)。
對于NSObject
繼承體系來說莽红,其實例方法
對體系中的所有實例妥畏、類和meta-class
都是有效的邦邦;而類方法
對于體系內(nèi)的所有類和meta-class
都是有效的。
5醉蚁、objc_cache
cathe
用于緩存調(diào)用過的方法燃辖。這個字段是一個指向objc_cache
結(jié)構(gòu)體的指針,其定義如下:
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method _Nullable buckets[1] OBJC2_UNAVAILABLE;
};
-
mask
:一個整數(shù)网棍,指定分配的緩存bucket的總數(shù)黔龟。在方法查找過程中,runtime
使用這個字段來確定開始線性查找數(shù)組的索引位置滥玷。指向方法selecto
r的指針與該字段做一個AND位操作(index = (mask & selector))氏身。這可以作為一個簡單的hash散列算法。 -
occupied
:一個整數(shù)惑畴,指定實際占用的緩存bucket
的總數(shù)蛋欣。 -
buckets
:指向Method
數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組。這個數(shù)組可能包含不超過mask+1
個元素如贷。需要注意的是豁状,指針可能是NULL
,表示這個緩存bucket
沒有被占用倒得,另外被占用的bucket
可能是不連續(xù)的。這個數(shù)組可能會隨著時間而增長夭禽。
6霞掺、類與對象的操作函數(shù)
類的操作方法大部分是以class_
為前綴的
對象的操作方法大部分是以objc_
或object_
為前綴
在runtime.h
里面搜索Working with
,一共有8個搜索結(jié)果讹躯,搜索順序分別是:
Working with Instances
Working with Classes
Working with Methods
Working with Instance Variables
Working with Properties
Working with Protocols
Working with Libraries
Working with Selectors
這里我們主要看Working with Classes
菩彬、Working with Instances
、 Working with Instance Variables
里面的方法
6.1潮梯、動態(tài)創(chuàng)建類
//創(chuàng)建一個新類和元類
Class objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name,
size_t extraBytes) ;
// 在應用中注冊由objc_allocateClassPair創(chuàng)建的類
void objc_registerClassPair(Class _Nonnull cls) ;
//銷毀一個類極其相關(guān)類
Class objc_duplicateClass(Class _Nonnull original, const char * _Nonnull name,
size_t extraBytes);
objc_allocateClassPair
函數(shù):如果我們要創(chuàng)建一個根類骗灶,則superclass
指定為Nil
。extraBytes
通常指定為0秉馏,該參數(shù)是分配給類和元類對象尾部的索引ivars的字節(jié)數(shù)耙旦。objc_disposeClassPair
函數(shù)用于銷毀一個類,不過需要注意的是萝究,如果程序運行中還存在類或其子類的實例免都,則不能調(diào)用針對類調(diào)用該方法。
創(chuàng)建一個新類,先用objc_allocateClassPair
,調(diào)用class_addMethod
帆竹,class_addIvar
等函數(shù)來為新創(chuàng)建的類添加方法绕娘、實例變量和屬性等。再調(diào)用objc_registerClassPair
函數(shù)來注冊類栽连,之后這個新類就可以在程序中使用了险领。
實例方法和實例變量應該添加到類自身上侨舆,而類方法應該添加到類的元類上
6.2、動態(tài)創(chuàng)建對象
// 創(chuàng)建類實例
id class_createInstance ( Class cls, size_t extraBytes );
// 在指定位置創(chuàng)建類實例
id objc_constructInstance ( Class cls, void *bytes );
// 銷毀類實例
void * objc_destructInstance ( id obj );
class_createInstance
:創(chuàng)建實例時绢陌,會在默認的內(nèi)存區(qū)域為類分配內(nèi)存挨下。extraBytes
額外的字節(jié)可以用來存儲類定義中定義之外的其他實例變量.objc_constructInstance
:在指定的位置(bytes
)創(chuàng)建類實例。objc_destructInstance
:銷毀一個類的實例下面,但不會釋放并移除任何與其相關(guān)的引用复颈。
類和對象的相關(guān)操作函數(shù)
// 返回指定對象的一份拷貝
id object_copy ( id obj, size_t size );
// 釋放指定對象占用的內(nèi)存
id object_dispose ( id obj );
// 修改類實例的實例變量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
// 獲取對象實例變量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
// 返回指向給定對象分配的任何額外字節(jié)的指針
void * object_getIndexedIvars ( id obj );
// 返回對象中實例變量的值
id object_getIvar ( id obj, Ivar ivar );
// 設(shè)置對象中實例變量的值
void object_setIvar ( id obj, Ivar ivar, id value );
// 返回給定對象的類名
const char * object_getClassName ( id obj );
// 返回對象的類
Class object_getClass ( id obj );
// 設(shè)置對象的類
Class object_setClass ( id obj, Class cls );
// 獲取已注冊的類定義的列表
int objc_getClassList ( Class *buffer, int bufferCount );
// 創(chuàng)建并返回一個指向所有已注冊類的指針列表
Class * objc_copyClassList ( unsigned int *outCount );
// 返回指定類的類定義
Class objc_lookUpClass ( const char *name );
Class objc_getClass ( const char *name );
Class objc_getRequiredClass ( const char *name );
// 返回指定類的元類
Class objc_getMetaClass ( const char *name );
6.3、父類(super_class
)和元類(meta-class
)
// 獲取類的父類
Class class_getSuperclass (Class _Nullable cls);
// 判斷給定的Class是否是一個元類
BOOL class_isMetaClass (Class _Nullable cls);
6.4沥割、類名
// 獲取類的類名
const char * class_getName (Class _Nullable cls);
6.5耗啦、版本號
// 獲取版本號
int class_getVersion (Class _Nullable cls) ;
// 設(shè)置版本號
void class_setVersion (Class _Nullable cls, int version);
6.6、實例大小
// 獲取實例大小
size_t class_getInstanceSize (Class _Nullable cls);
6.7 成員變量
// 獲取類中指定名稱實例成員變量的信息
Ivar class_getInstanceVariable (Class _Nullable cls, const char * _Nonnull name)机杜;
// 獲取類成員變量的信息
Ivar class_getClassVariable(Class _Nullable cls, const char * _Nonnull name);
// 添加成員變量
BOOL class_addIvar (Class _Nullable cls, const char * _Nonnull name, size_t size,
uint8_t alignment, const char * _Nullable types) ;
// 獲取整個成員變量列表
Ivar * class_copyIvarList (Class _Nullable cls, unsigned int * _Nullable outCount) ;
-
class_getInstanceVariable
: 根據(jù)name
返回指定的對象成員變量信息(objc_ivar
結(jié)構(gòu)體)
class_getClassVariable
: 根據(jù)name
返回指定的類成員變量信息(objc_ivar
結(jié)構(gòu)體)帜讲,一般認為Objective-C
不支持類變量class_addIvar
: 參數(shù)分別是,成員變量所屬類椒拗、成員變量名似将、對齊方式、成員變量類型
Objective-C
不支持往已存在的類中添加實例成員變量蚀苛,因此不管是系統(tǒng)庫提供的提供的類在验,還是我們自定義的類,都無法動態(tài)添加成員變量,但是動態(tài)創(chuàng)建類的可以添加成員變量堵未,只能在objc_allocateClassPair
與objc_registerClassPair
之間
如添加一個NSString
類型的ivar1
變量
class_addIvar(cls, "ivar1", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
-
class_copyIvarList
: 它返回一個指向成員變量信息的數(shù)組腋舌,數(shù)組中每個元素是指向該成員變量信息的objc_ivar
結(jié)構(gòu)體的指針,outCount
指針返回數(shù)組的大小
注意 : 返回的列表不包含父類的成員變量和屬性渗蟹,必須使用free()來釋放這個數(shù)組
6.8 屬性
// 獲取指定的屬性
objc_property_t class_getProperty(Class _Nullable cls, const char * _Nonnull name);
// 獲取屬性列表
objc_property_t class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount);
// 為類添加屬性
BOOL class_addProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount);
// 替換類的屬性
void class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount)
-
class_getProperty
: 根據(jù)name
返回指定的類屬性(objc_property
結(jié)構(gòu)體) -
class_copyPropertyList
: 它返回一個指向?qū)傩缘臄?shù)組块饺,數(shù)組中每個元素是指向該屬性的objc_property
結(jié)構(gòu)體的指針,outCount指針返回數(shù)組的大小
注意 : 返回的列表不包含父類的屬性雌芽,必須使用free()來釋放這個數(shù)組
-
class_addProperty
: 參數(shù)含義分別是:類名授艰、屬性名、屬性特性世落、屬性特性個數(shù)
如添加屬性名為@property (nonatomic , copy) NSString *property1;
的屬性
objc_property_attribute_t type = { "T", "@\"NSString\"" };
objc_property_attribute_t ownership = { "C", "" }; // C = copy
objc_property_attribute_t nonatomic = { "N", "" }; //nonatomic
objc_property_attribute_t backingivar = { "V", "_property1" };//V 實例變量
objc_property_attribute_t attrs[] = { type, ownership,nonatomic, backingivar };
class_addProperty(cls, "property1", attrs, 4);
-
class_replaceProperty
: 替換類的屬性
6.9淮腾、方法
//添加方法
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types);
//獲取實例方法
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name);
//獲取類方法
Method class_getClassMethod(Class _Nullable cls, SEL _Nonnull name);
//獲取所有方法數(shù)組
Method class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount);
//替代方法的實現(xiàn)
IMP class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types) ;
//返回方法的具體實現(xiàn)
IMP class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) ;
IMP class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name) ;
//對象是否響應指定的selector
BOOL class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel) ;
1.class_addMethod
: 參數(shù)含義分別是:添加方法的類、添加方法的編號屉佳、方法實現(xiàn)来破、方法類型,使用這個方法添加方法會覆蓋父類的方法實現(xiàn)忘古,但不會取代本類中已存在的實現(xiàn)徘禁,如果本類中包含一個同名的實現(xiàn),則函數(shù)會返回NO髓堪。如果要修改已存在實現(xiàn)送朱,可以使用method_setImplementation
如添加一個eat
方法
class_addMethod([self class],@selector(eat:),(IMP) eatIMP, "v@:@");
-
class_getInstanceMethod
: 根據(jù)name
返回指定的對象方法(objc_method
結(jié)構(gòu)體) -
class_getClassMethod
: 根據(jù)name
返回指定的類方法(objc_method
結(jié)構(gòu)體)
注意 : 這兩個函數(shù)都會去搜索父類的實現(xiàn)
-
class_copyMethodList
: 返回一個指向?qū)嵗椒ǖ臄?shù)組娘荡,數(shù)組中每個元素都是該方法信息的objc_method
結(jié)構(gòu)體指針,outCount
返回數(shù)組的大小驶沼。
如果需要獲取類方法炮沐,則可以使用class_copyMethodList(object_getClass(cls), &count)
,因為一個類的實例方法是定義在元類里面
注意 : 返回的列表不包含父類的方法回怜,必須使用free()來釋放這個數(shù)組
class_replaceMethod
: 如果類中不存在name
指定的方法大年,則類似于class_addMethod
函數(shù)一樣會添加方法;如果類中已存在name
指定的方法玉雾,則類似于method_setImplementation
一樣替代原方法的實現(xiàn)翔试。class_getMethodImplementation
: 返回一個指向方法實現(xiàn)函數(shù)的指針,在向類實例發(fā)送消息時會被調(diào)用,這個函數(shù)會比method_getImplementation(class_getInstanceMethod(cls, name))
更快复旬。返回的函數(shù)指針可能是一個指向runtime
內(nèi)部的函數(shù)垦缅,而不一定是方法的實際實現(xiàn)。例如驹碍,如果類實例無法響應selector
壁涎,則返回的函數(shù)指針將是運行時消息轉(zhuǎn)發(fā)機制的一部分。class_respondsToSelector
: 我們通常使用NSObject
類的respondsToSelector:
或instancesRespondToSelector:
方法來達到相同目的志秃。
6.10怔球、協(xié)議
// 添加協(xié)議
BOOL class_addProtocol ( Class cls, Protocol *protocol );
// 返回類是否實現(xiàn)指定的協(xié)議
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
// 返回類實現(xiàn)的協(xié)議列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
class_conformsToProtocol
: 可以使用NSObject
類的conformsToProtocol:
方法來替代。class_copyProtocolList
: 返回一個指向協(xié)議的數(shù)組浮还,數(shù)組中每個元素都是該協(xié)議信息的objc_object
結(jié)構(gòu)體指針竟坛,outCount返回數(shù)組的大小。
注意 : 返回的列表不包含父類的協(xié)議碑定,必須使用free()來釋放這個數(shù)組