ios rumtime 1- 類與對(duì)象

本文是參考南峰子Objective-C Runtime系列文章
做一個(gè)自己的總結(jié),強(qiáng)烈推薦查看原文

1、runtime介紹

Objective-C 是一門動(dòng)態(tài)語言,它將很多靜態(tài)語言在編譯和連接時(shí)期做的事情放到了運(yùn)行時(shí)來處理,這種動(dòng)態(tài)語言的優(yōu)勢(shì)在于:我們寫代碼時(shí)更具有靈活性,如我們可以把消息轉(zhuǎn)發(fā)給我們想要轉(zhuǎn)發(fā)的對(duì)象,或者隨意交換一個(gè)方法等。

這種特性意味著Objective-C不僅需要一個(gè)編譯器,還需要一個(gè)運(yùn)行時(shí)動(dòng)態(tài)庫(kù)來執(zhí)行編譯的代碼。對(duì)于Objective-C來說,這個(gè)運(yùn)行時(shí)系統(tǒng)就像一個(gè)操作系統(tǒng)一樣:讓所有工作正常運(yùn)行。這個(gè)運(yùn)行時(shí)系統(tǒng)就是Objc RuntimeObjc Runtime其實(shí)是一個(gè)Runtime庫(kù),基本上是用C語言匯編語言寫的,使得C語言有了面向?qū)ο蟮哪芰Α?/p>

Runtime庫(kù)主要做了以下兩件事:

  1. 封裝:在這個(gè)庫(kù)中,對(duì)象可以使用C語言中的結(jié)構(gòu)體表示,方法可以使用C函數(shù)來實(shí)現(xiàn),另外再加上一些額外的特性,這些結(jié)構(gòu)體和函數(shù)被runtime函數(shù)封裝后,我們就可以在程序運(yùn)行時(shí)創(chuàng)建、檢查、修改類、對(duì)象、方法了
  2. 找出方法的最終執(zhí)行代碼:當(dāng)程序執(zhí)行[object doSomething]時(shí),會(huì)向接受者(object)發(fā)送一條消息(doSomething),runtime會(huì)根據(jù)消息接受者能否響應(yīng)該消息而做出不同反應(yīng)

Objective-C runtime目前有兩個(gè)版本:Modern runtimeLegacy runtime。Modern Runtime覆蓋了64位的Mac OS X Apps,還有iOS AppsLegacy Runtime是早期用來給32位 Mac OS X Apps 用的,已經(jīng)過時(shí)

蘋果和GNU各自維護(hù)一個(gè)開源的 runtime 版本,這兩個(gè)版本之間都在努力的保持一致。

高級(jí)編程語言想要成為可執(zhí)行文件,需要先編譯成匯編語言,再匯編為機(jī)器語言,機(jī)器語言是計(jì)算機(jī)唯一能識(shí)別的語言(轉(zhuǎn)換過程待研究)

2、類

Objective-C類是有Class類型表示,實(shí)際上是指向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;  // 類的版本信息,默認(rèn)為0
    long info                               OBJC2_UNAVAILABLE;  // 類信息,供運(yùn)行期使用的一些位標(biāo)識(shí)
    long instance_size                      OBJC2_UNAVAILABLE;  // 該類的實(shí)例變量大小
    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中,所有的類自身也是一個(gè)對(duì)象,這個(gè)對(duì)象的Class里面也有一個(gè)isa指針,它指向metaClass(元類)

  1. super_class :指向該類的父類,如果該類已經(jīng)是最頂層的根類(如NSObjectNSProxy),則super_classNULL

  2. name : 類名

  3. version :我們可以使用這個(gè)字段來提供類的版本信息。這對(duì)于對(duì)象的序列化非常有用,它可是讓我們識(shí)別出不同類定義版本中實(shí)例變量布局的改變。

  4. info : 類信息

  5. instance_size : 該類的對(duì)象大小

  6. ivars : 成員變量的列表

  7. methodLists : 方法列表

  8. cathe : 用于緩存最近使用的方法。
    一個(gè)接收者對(duì)象接收到一個(gè)消息時(shí),它會(huì)根據(jù)isa指針去查找能夠響應(yīng)這個(gè)消息的對(duì)象。在實(shí)際使用中,這個(gè)對(duì)象只有一部分方法是常用的,很多方法其實(shí)很少用或者根本用不上。這種情況下,如果每次消息來時(shí),我們都是methodLists中遍歷一遍,性能勢(shì)必很差。這時(shí),cache就派上用場(chǎng)了。在我們每次調(diào)用過一個(gè)方法后,這個(gè)方法就會(huì)被緩存到cache列表中,下次調(diào)用的時(shí)候runtime就會(huì)優(yōu)先去cache中查找,如果cache沒有,才去methodLists中查找方法。這樣,對(duì)于那些經(jīng)常用到的方法的調(diào)用,提高了調(diào)用的效率。

  9. protocols : 協(xié)議列表

3、對(duì)象

objc_object是表示一個(gè)類的實(shí)例的結(jié)構(gòu)體

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

這個(gè)結(jié)構(gòu)體只有一個(gè)屬性,即指向其類的isa指針。這樣,當(dāng)我們向一個(gè)Objective-C對(duì)象發(fā)送消息時(shí),運(yùn)行時(shí)庫(kù)會(huì)根據(jù)實(shí)例對(duì)象的isa指針找到這個(gè)實(shí)例對(duì)象所屬的類。Runtime庫(kù)會(huì)在類的方法列表及父類的方法列表中去尋找與消息對(duì)應(yīng)的selector指向的方法,找到后即運(yùn)行這個(gè)方法。

4、元類(Meta Class)

meta-class是一個(gè)類對(duì)象的類

當(dāng)我們向一個(gè)對(duì)象發(fā)送消息時(shí),runtime會(huì)在這個(gè)對(duì)象所屬的這個(gè)類的方法列表中查找方法;
而向一個(gè)類發(fā)送消息時(shí),會(huì)在這個(gè)類的meta-class的方法列表中查找方法

meta-class之所以重要,是因?yàn)樗鎯?chǔ)著一個(gè)類的所有類方法。每個(gè)類都會(huì)有一個(gè)單獨(dú)的meta-class,因?yàn)槊總€(gè)類的類方法基本不可能完全相同

meta-class也是一個(gè)類,也可以向它發(fā)送一個(gè)消息,那么它的isa又是指向什么呢?為了不讓這種結(jié)構(gòu)無限延伸下去,Objective-C的設(shè)計(jì)者讓所有的meta-classisa指向基類的meta-class,以此作為它們的所屬類。
即,任何NSObject繼承體系下的meta-class都使用NSObjectmeta-class作為自己的所屬類,而基類的meta-classisa指針是指向它自己。這樣就形成了一個(gè)完美的閉環(huán)。

Class和meta-Class.png

對(duì)于NSObject繼承體系來說,其實(shí)例方法對(duì)體系中的所有實(shí)例、類和meta-class都是有效的;而類方法對(duì)于體系內(nèi)的所有類和meta-class都是有效的。

5、objc_cache

cathe用于緩存調(diào)用過的方法。這個(gè)字段是一個(gè)指向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;
};
  1. mask:一個(gè)整數(shù),指定分配的緩存bucket的總數(shù)。在方法查找過程中,runtime使用這個(gè)字段來確定開始線性查找數(shù)組的索引位置。指向方法selector的指針與該字段做一個(gè)AND位操作(index = (mask & selector))。這可以作為一個(gè)簡(jiǎn)單的hash散列算法。
  2. occupied:一個(gè)整數(shù),指定實(shí)際占用的緩存bucket的總數(shù)。
  3. buckets:指向Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組。這個(gè)數(shù)組可能包含不超過mask+1個(gè)元素。需要注意的是,指針可能是NULL,表示這個(gè)緩存bucket沒有被占用,另外被占用的bucket可能是不連續(xù)的。這個(gè)數(shù)組可能會(huì)隨著時(shí)間而增長(zhǎng)。

6、類與對(duì)象的操作函數(shù)

類的操作方法大部分是以class_為前綴的
對(duì)象的操作方法大部分是以objc_object_為前綴

runtime.h里面搜索Working with ,一共有8個(gè)搜索結(jié)果,搜索順序分別是:

  1. Working with Instances
  2. Working with Classes
  3. Working with Methods
  4. Working with Instance Variables
  5. Working with Properties
  6. Working with Protocols
  7. Working with Libraries
  8. Working with Selectors

這里我們主要看Working with Classes、Working with InstancesWorking with Instance Variables里面的方法

6.1、動(dòng)態(tài)創(chuàng)建類

//創(chuàng)建一個(gè)新類和元類
Class objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name, 
                       size_t extraBytes) ;

// 在應(yīng)用中注冊(cè)由objc_allocateClassPair創(chuàng)建的類
void objc_registerClassPair(Class _Nonnull cls) ;

//銷毀一個(gè)類極其相關(guān)類
Class objc_duplicateClass(Class _Nonnull original, const char * _Nonnull name,
                    size_t extraBytes);

  1. objc_allocateClassPair函數(shù):如果我們要?jiǎng)?chuàng)建一個(gè)根類,則superclass指定為Nil。extraBytes通常指定為0,該參數(shù)是分配給類和元類對(duì)象尾部的索引ivars的字節(jié)數(shù)。

  2. objc_disposeClassPair函數(shù)用于銷毀一個(gè)類,不過需要注意的是,如果程序運(yùn)行中還存在類或其子類的實(shí)例,則不能調(diào)用針對(duì)類調(diào)用該方法。

創(chuàng)建一個(gè)新類,先用objc_allocateClassPair,調(diào)用class_addMethod,class_addIvar等函數(shù)來為新創(chuàng)建的類添加方法、實(shí)例變量和屬性等。再調(diào)用objc_registerClassPair函數(shù)來注冊(cè)類,之后這個(gè)新類就可以在程序中使用了。

實(shí)例方法和實(shí)例變量應(yīng)該添加到類自身上,而類方法應(yīng)該添加到類的元類上

6.2、動(dòng)態(tài)創(chuàng)建對(duì)象

// 創(chuàng)建類實(shí)例
id class_createInstance ( Class cls, size_t extraBytes );
// 在指定位置創(chuàng)建類實(shí)例
id objc_constructInstance ( Class cls, void *bytes );
// 銷毀類實(shí)例
void * objc_destructInstance ( id obj );
  1. class_createInstance :創(chuàng)建實(shí)例時(shí),會(huì)在默認(rèn)的內(nèi)存區(qū)域?yàn)轭惙峙鋬?nèi)存。extraBytes額外的字節(jié)可以用來存儲(chǔ)類定義中定義之外的其他實(shí)例變量.

  2. objc_constructInstance :在指定的位置(bytes)創(chuàng)建類實(shí)例。

  3. objc_destructInstance:銷毀一個(gè)類的實(shí)例,但不會(huì)釋放并移除任何與其相關(guān)的引用。

類和對(duì)象的相關(guān)操作函數(shù)

// 返回指定對(duì)象的一份拷貝
id object_copy ( id obj, size_t size );

// 釋放指定對(duì)象占用的內(nèi)存
id object_dispose ( id obj );

// 修改類實(shí)例的實(shí)例變量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );

// 獲取對(duì)象實(shí)例變量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );

// 返回指向給定對(duì)象分配的任何額外字節(jié)的指針
void * object_getIndexedIvars ( id obj );

// 返回對(duì)象中實(shí)例變量的值
id object_getIvar ( id obj, Ivar ivar );

// 設(shè)置對(duì)象中實(shí)例變量的值
void object_setIvar ( id obj, Ivar ivar, id value );

// 返回給定對(duì)象的類名
const char * object_getClassName ( id obj );

// 返回對(duì)象的類
Class object_getClass ( id obj );

// 設(shè)置對(duì)象的類
Class object_setClass ( id obj, Class cls );

// 獲取已注冊(cè)的類定義的列表
int objc_getClassList ( Class *buffer, int bufferCount );

// 創(chuàng)建并返回一個(gè)指向所有已注冊(cè)類的指針列表
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是否是一個(gè)元類
BOOL class_isMetaClass (Class _Nullable cls);

6.4、類名

// 獲取類的類名
const char * class_getName (Class _Nullable cls);

6.5、版本號(hào)

// 獲取版本號(hào)
int class_getVersion (Class _Nullable cls) ;

// 設(shè)置版本號(hào)
void class_setVersion (Class _Nullable cls, int version);

6.6、實(shí)例大小

// 獲取實(shí)例大小
size_t class_getInstanceSize (Class _Nullable cls);

6.7 成員變量

// 獲取類中指定名稱實(shí)例成員變量的信息
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) ;

// 獲取整個(gè)成員變量列表
Ivar * class_copyIvarList (Class _Nullable cls, unsigned int * _Nullable outCount) ;
  1. class_getInstanceVariable: 根據(jù)name返回指定的對(duì)象成員變量信息(objc_ivar結(jié)構(gòu)體)
  1. class_getClassVariable: 根據(jù)name返回指定的類成員變量信息(objc_ivar結(jié)構(gòu)體),一般認(rèn)為Objective-C不支持類變量

  2. class_addIvar : 參數(shù)分別是,成員變量所屬類、成員變量名、對(duì)齊方式、成員變量類型

Objective-C不支持往已存在的類中添加實(shí)例成員變量,因此不管是系統(tǒng)庫(kù)提供的提供的類,還是我們自定義的類,都無法動(dòng)態(tài)添加成員變量,但是動(dòng)態(tài)創(chuàng)建類的可以添加成員變量,只能在objc_allocateClassPairobjc_registerClassPair之間

如添加一個(gè)NSString類型的ivar1變量

class_addIvar(cls, "ivar1", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
  1. class_copyIvarList : 它返回一個(gè)指向成員變量信息的數(shù)組,數(shù)組中每個(gè)元素是指向該成員變量信息的objc_ivar結(jié)構(gòu)體的指針,outCount指針返回?cái)?shù)組的大小

注意 : 返回的列表不包含父類的成員變量和屬性,必須使用free()來釋放這個(gè)數(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)

  1. class_getProperty : 根據(jù)name返回指定的類屬性(objc_property結(jié)構(gòu)體)
  2. class_copyPropertyList : 它返回一個(gè)指向?qū)傩缘臄?shù)組,數(shù)組中每個(gè)元素是指向該屬性的objc_property結(jié)構(gòu)體的指針,outCount指針返回?cái)?shù)組的大小

注意 : 返回的列表不包含父類的屬性,必須使用free()來釋放這個(gè)數(shù)組

  1. class_addProperty : 參數(shù)含義分別是:類名、屬性名、屬性特性、屬性特性個(gè)數(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 實(shí)例變量
    objc_property_attribute_t attrs[] = { type, ownership,nonatomic, backingivar };
    class_addProperty(cls, "property1", attrs, 4);
  1. class_replaceProperty : 替換類的屬性

6.9、方法

//添加方法
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                const char * _Nullable types);

//獲取實(shí)例方法
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);

//替代方法的實(shí)現(xiàn)
IMP class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                    const char * _Nullable types) ;

//返回方法的具體實(shí)現(xiàn)
IMP class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) ;
IMP class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name) ;

//對(duì)象是否響應(yīng)指定的selector
BOOL class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel) ;

1.class_addMethod : 參數(shù)含義分別是:添加方法的類、添加方法的編號(hào)、方法實(shí)現(xiàn)、方法類型,使用這個(gè)方法添加方法會(huì)覆蓋父類的方法實(shí)現(xiàn),但不會(huì)取代本類中已存在的實(shí)現(xiàn),如果本類中包含一個(gè)同名的實(shí)現(xiàn),則函數(shù)會(huì)返回NO。如果要修改已存在實(shí)現(xiàn),可以使用method_setImplementation

如添加一個(gè)eat方法

class_addMethod([self class],@selector(eat:),(IMP) eatIMP, "v@:@");
  1. class_getInstanceMethod : 根據(jù)name返回指定的對(duì)象方法(objc_method結(jié)構(gòu)體)
  2. class_getClassMethod : 根據(jù)name返回指定的類方法(objc_method結(jié)構(gòu)體)

注意 : 這兩個(gè)函數(shù)都會(huì)去搜索父類的實(shí)現(xiàn)

  1. class_copyMethodList: 返回一個(gè)指向?qū)嵗椒ǖ臄?shù)組,數(shù)組中每個(gè)元素都是該方法信息的objc_method結(jié)構(gòu)體指針,outCount返回?cái)?shù)組的大小。
    如果需要獲取類方法,則可以使用class_copyMethodList(object_getClass(cls), &count),因?yàn)橐粋€(gè)類的實(shí)例方法是定義在元類里面

注意 : 返回的列表不包含父類的方法,必須使用free()來釋放這個(gè)數(shù)組

  1. class_replaceMethod : 如果類中不存在name指定的方法,則類似于class_addMethod函數(shù)一樣會(huì)添加方法;如果類中已存在name指定的方法,則類似于method_setImplementation一樣替代原方法的實(shí)現(xiàn)。

  2. class_getMethodImplementation : 返回一個(gè)指向方法實(shí)現(xiàn)函數(shù)的指針,在向類實(shí)例發(fā)送消息時(shí)會(huì)被調(diào)用,這個(gè)函數(shù)會(huì)比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函數(shù)指針可能是一個(gè)指向runtime內(nèi)部的函數(shù),而不一定是方法的實(shí)際實(shí)現(xiàn)。例如,如果類實(shí)例無法響應(yīng)selector,則返回的函數(shù)指針將是運(yùn)行時(shí)消息轉(zhuǎn)發(fā)機(jī)制的一部分。

  3. class_respondsToSelector : 我們通常使用NSObject類的respondsToSelector:instancesRespondToSelector:方法來達(dá)到相同目的。

6.10、協(xié)議

// 添加協(xié)議
BOOL class_addProtocol ( Class cls, Protocol *protocol );
// 返回類是否實(shí)現(xiàn)指定的協(xié)議
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
// 返回類實(shí)現(xiàn)的協(xié)議列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );

  1. class_conformsToProtocol : 可以使用NSObject類的conformsToProtocol:方法來替代。

  2. class_copyProtocolList : 返回一個(gè)指向協(xié)議的數(shù)組,數(shù)組中每個(gè)元素都是該協(xié)議信息的objc_object結(jié)構(gòu)體指針,outCount返回?cái)?shù)組的大小。

注意 : 返回的列表不包含父類的協(xié)議,必須使用free()來釋放這個(gè)數(shù)組

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容