麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Objective-C——Runtime理解

2019-11-14 18:32:29
字體:
供稿:網(wǎng)友

動(dòng)態(tài)語(yǔ)言

OC是一門不折不扣的動(dòng)態(tài)語(yǔ)言,所以它的很多機(jī)制都是動(dòng)態(tài)運(yùn)行時(shí)決定的。這點(diǎn)和C語(yǔ)言不一樣,C語(yǔ)言是靜態(tài)綁定,也就是編譯后所有的一切都已經(jīng)決定了。這一點(diǎn)和C語(yǔ)言的函數(shù)指針有些類似,很多時(shí)候函數(shù)指針在編譯的時(shí)候并不知道會(huì)指向哪個(gè)函數(shù),所以此時(shí)就是動(dòng)態(tài)綁定。

舉幾個(gè)OC動(dòng)態(tài)類型的例子,最為直接的就是id類型了、還有關(guān)聯(lián)對(duì)象、動(dòng)態(tài)綁定、消息轉(zhuǎn)發(fā)、方法調(diào)配、這些技術(shù)都是動(dòng)態(tài)類型很好的證明

 

OC對(duì)象結(jié)構(gòu)

在介紹動(dòng)動(dòng)態(tài)性之前,我們先來看看OC對(duì)象的一些結(jié)構(gòu)。

#import<objc/runtime>這是OC運(yùn)行時(shí)函數(shù)庫(kù),里面定義了很多結(jié)構(gòu)體。

首先看對(duì)象的結(jié)構(gòu):

typdef struct objc_object {      Class isa;     }  *id;

對(duì)象結(jié)構(gòu)中非常簡(jiǎn)單,只有一個(gè)isa指針,isa指針后面我們會(huì)介紹。

接下來我們看類的結(jié)構(gòu)體

struct objc_class {    Class isa#if !__OBJC2__    Class super_class                                            const char *name                                           long version                                               long info                                                   long instance_size                                           struct objc_ivar_list *ivars                               struct objc_method_list **methodLists              struct objc_cache *cache                                 struct objc_PRotocol_list *protocols               #endif}typedef struct objc_class *Class;

可以看到很多信息都在Class中定義著,里面信息如下

字段含義
isaisa指針
super_class父類指針
name類名
version類的版本信息,默認(rèn)為0
info供運(yùn)行期使用的一些位標(biāo)識(shí)
instance_size實(shí)例的大小
ivars實(shí)例變量列表
methodLists方法列表
cache指向最近調(diào)用的方法,用于優(yōu)化調(diào)用方法的速度
protocols協(xié)議列表

 

 

 

 

 

 

 

 

 

 

接下來我們逐個(gè)介紹一下:

 

isa指針和super_class

在OC中,嚴(yán)格意義上講是沒有類這種概念的,每一個(gè)類都是一個(gè)對(duì)象,只不過類對(duì)象是一個(gè)單例。

isa指針存在于每一個(gè)對(duì)象中,類普通實(shí)例的isa指針指向類,類的isa指針指向它的元類(類方法全部都在元類中存放)。元類的isa指針指向根元類,也就是NSObject的isa所指向的元類。

super_class只有類和元類才有,它們分別指向自己的父類和父元類,而為了讓NSObject成為所有類的根類,讓NSObject的元類的父類指針也指向了NSObject。

這樣說可能也不是很好理解,看下面這張圖應(yīng)該就很快理解了。

等下我們說到消息傳遞的時(shí)候還會(huì)在說到isa和super_class。

ivars屬性列表

struct objc_ivar_list {    int ivar_count                       #ifdef __LP64__    int space                                       #endif    /* variable length structure */    struct objc_ivar ivar_list[1]               }    

space作用還不太清楚...求指教啊。

下面是實(shí)例變量結(jié)構(gòu)

struct objc_ivar {    char *ivar_name                                          OBJC2_UNAVAILABLE;    char *ivar_type                                          OBJC2_UNAVAILABLE;    int ivar_offset                                          OBJC2_UNAVAILABLE;#ifdef __LP64__    int space                                                OBJC2_UNAVAILABLE;#endif}  

可以看到有一個(gè)ivar_offset,這個(gè)是實(shí)例變量在編譯時(shí)的偏移量,是由編譯時(shí)決定的。

 methodLists方法列表

struct objc_method_list {    struct objc_method_list *obsolete                           int method_count                                       #ifdef __LP64__    int space                                            #endif    /* variable length structure */    struct objc_method method_list[1]                  }  

該結(jié)構(gòu)有方法鏈表和方法總數(shù)。

struct objc_method {    SEL method_name                                          OBJC2_UNAVAILABLE;    char *method_types                                       OBJC2_UNAVAILABLE;    IMP method_imp                                           OBJC2_UNAVAILABLE;}       

里面有函數(shù)名,返回值類型和函數(shù)實(shí)現(xiàn),接下來看看SEL和IMP定義

typedef struct objc_selector *SEL;typedef id (*IMP)(id, SEL, ...); 

可以看到SEL是objc_selector,(*IMP)(id,SEL,...)是id,我沒有找到objc_selector的結(jié)構(gòu)所以這里也沒法說什么...

cache緩存列表

typedef struct objc_cache *Cache                            #define CACHE_BUCKET_NAME(B)  ((B)->method_name)#define CACHE_BUCKET_IMP(B)   ((B)->method_imp)#define CACHE_BUCKET_VALID(B) (B)#ifndef __LP64__#define CACHE_HASH(sel, mask) (((uintptr_t)(sel)>>2) & (mask))#else#define CACHE_HASH(sel, mask) (((unsigned int)((uintptr_t)(sel)>>3)) & (mask))#endifstruct objc_cache {    unsigned int mask /* total = mask + 1 */                 unsigned int occupied                                     Method buckets[1]                                   };

 

 緩存列表里面包含了已緩存的方法,用于快速的調(diào)用,不需要在去方法列表里面查詢了。

protocol協(xié)議列表

struct objc_protocol_list {    struct objc_protocol_list *next;    long count;    Protocol *list[1];};

 

協(xié)議列表包含了協(xié)議數(shù)量和協(xié)議的指針。

 

id

id類型實(shí)際上就是一個(gè)objc_object的typedef,是一個(gè)對(duì)象實(shí)例,而且還是一個(gè)指針,所以用id來定義對(duì)象的時(shí)候就不需要加*號(hào)了。

id類型往往需要我們使用”自省“機(jī)制來保證使用安全,所謂自省其實(shí)就是看看這個(gè)對(duì)象是不是某個(gè)類的實(shí)例,或者是不是其子類。

自省用一下兩個(gè)方法:

isKindOfClass:(Class)class          判斷是不是其類族對(duì)象

isMemberOfClass:(Class)class     判斷是不是類本身對(duì)象

 

關(guān)聯(lián)對(duì)象

關(guān)聯(lián)對(duì)象在我之前的博客中已經(jīng)有介紹了,這里就不再說了。

 

OC消息機(jī)制和消息轉(zhuǎn)發(fā)

別的語(yǔ)言調(diào)用函數(shù),OC則叫做發(fā)送消息,這是因?yàn)樗蠴C的方法調(diào)用實(shí)際上底層都是通過

objc_msgSend(id self , SEL cmd,...)來發(fā)送的。

該函數(shù)的作用就是傳遞給一個(gè)對(duì)象某個(gè)方法,后面的不定參數(shù)列表是方法所需要的參數(shù)。

這里說一下OC的消息傳遞機(jī)制,首先對(duì)一個(gè)對(duì)象發(fā)送消息,它會(huì)先檢查自己的類中有沒有該方法,如果沒有就找他的父類中有沒有,如果還沒有則會(huì)進(jìn)行消息轉(zhuǎn)發(fā)。

在看例子之前先說一下,Xcode6貌似默認(rèn)行為不讓我們使用objc_msgSend了,所以需要先設(shè)置一下

 

把這一項(xiàng)設(shè)置為No就可以了。

這里看個(gè)例子,

        EqualObject *object1 = [EqualObject new];        EqualObject *object2 = [EqualObject new];        object1.name = @"xiaoming";        object2.name = @"xiaoming";                BOOL isEqual = objc_msgSend(object1,@selector(isEqualToEqualObject:),object2);                if(isEqual)        {            NSLog(@"equal");        }

 

EqualObject是我們自己實(shí)現(xiàn)的類,它有一個(gè)判斷是否相等的方法isEqualToEqualObject:,如果name相等就人為兩個(gè)對(duì)象相等。

這里我們直接傳遞消息,不通過OC語(yǔ)法,運(yùn)行程序可以看到equal被打印了出來。

然后我們?cè)倏纯聪⑥D(zhuǎn)發(fā)機(jī)制。

當(dāng)該對(duì)象包括其父類都沒有這個(gè)方法的時(shí)候會(huì)啟動(dòng),消息轉(zhuǎn)發(fā)機(jī)制分為兩大階段。

第一階段先看對(duì)象所屬類是否有能力動(dòng)態(tài)添加方法,已處理這個(gè)位置的選擇子,這叫做動(dòng)態(tài)解析(dynamic method resolution)。

第二階段設(shè)計(jì)“完整的消息轉(zhuǎn)發(fā)機(jī)制”。如果運(yùn)行期系統(tǒng)已經(jīng)把第一階段執(zhí)行完了,那么接受者自己就沒法再以動(dòng)態(tài)新增方法的手段來處理與消息相關(guān)的方法調(diào)用。這又分為兩個(gè)小步。

首先,請(qǐng)接受者看看有沒有其他對(duì)象能處理這條消息。若有,則在運(yùn)行時(shí)轉(zhuǎn)給那個(gè)對(duì)象,于是消息轉(zhuǎn)發(fā)過程結(jié)束。若沒有“備用的接受者”,則啟動(dòng)完成的消息轉(zhuǎn)發(fā)機(jī)制,運(yùn)行起系統(tǒng)會(huì)把與消息有關(guān)的全部細(xì)節(jié)都封裝到NSInvocation對(duì)象中,再給接受者最后一次機(jī)會(huì),令其設(shè)法解決當(dāng)前還未處理的這條消息。

 

動(dòng)態(tài)方法解析

遇到無(wú)法解析的信息后,首先將調(diào)用其所屬類的下列類方法:
+(BOOL)resolveInstanceMethod:(SEL)selector

該方法參數(shù)就是未知的選擇子,返回BOOL類型那個(gè),表示這個(gè)類是否能新增一個(gè)實(shí)力方法已處理這個(gè)選擇子。假如是類方法,那么會(huì)調(diào)用

+(BOOL)resolveClassMethod:(SEL)selector

使用這種方法的前提是相關(guān)的實(shí)現(xiàn)已經(jīng)寫好了,只等運(yùn)行時(shí)動(dòng)態(tài)的插入就行,比如CoreData中NSManagedObjects對(duì)象的屬性時(shí)就可以這么做,因?yàn)閷?shí)現(xiàn)這些屬性所需的存取方法在編譯期就能確定。

下面我們看個(gè)例子

void showMessage(id self, SEL _cmd, id value){    if([value isKindOfClass:[NSString class]])    {        NSLog(@"%@",(NSString *)value);    }}+(BOOL)resolveInstanceMethod:(SEL)sel{    NSString *selString = NSStringFromSelector(sel);    if([selString isEqualToString:@"showMessage:"])    {        class_addMethod(self, sel, (IMP)showMessage, "v@:@");        return YES;    }    else    {        return [super resolveInstanceMethod:sel];    }}

 

向剛才EqualObject添加以上實(shí)現(xiàn)帶代碼,然后在客戶端調(diào)用:

        objc_msgSend(object1,@selector(showMessage:),@"Hello");

 

能夠看到程序并沒有報(bào)錯(cuò),而且還打印出了Hello!

 

備援接受者

當(dāng)沒有使用動(dòng)態(tài)方法解析后,還是出發(fā)備用接受者,該步驟會(huì)觸發(fā)該方法

-(id)forwardingTargetForSelector:(SEL)selector

方法參數(shù)是未知的選擇子,如果找到備用對(duì)象返回對(duì)象,否則返回nil。

可以利用該方法來模擬多重繼承機(jī)制(實(shí)際為組合),因?yàn)橥獠靠床坏剑愿杏X上就像是本身處理該消息。

下面看一個(gè)例子:

把剛才添加的代碼注釋掉,添加一個(gè)新類OtherObject,添加showMessage方法,然后在EqualObject中添加以下代碼

-(id)forwardingTargetForSelector:(SEL)aSelector{    NSString *selectString = NSStringFromSelector(aSelector);    if([selectString isEqualToString:@"showMessage:"])    {        return other;    }    return nil;}

 

會(huì)發(fā)現(xiàn)Hello依舊出現(xiàn)了!,而且如果你在OtherObject中的showMessage方法中打上斷點(diǎn),會(huì)發(fā)現(xiàn)方法執(zhí)行到了OtherObject中...

 

完整的消息轉(zhuǎn)發(fā)

如果消息沒有轉(zhuǎn)發(fā),那么回來到這一步,首先創(chuàng)建NSInvocation對(duì)象,然后把未處理的信息細(xì)節(jié)全部都封裝于其中。

此對(duì)象包含選擇子、目標(biāo)(target)和參數(shù)。在觸發(fā)NSInvocation對(duì)象時(shí),“消息派發(fā)系統(tǒng)”會(huì)把消息派給目標(biāo)。

此步驟會(huì)調(diào)用以下方法:

-(void)forwardInvocation:(NSInvocation *)invocation

這個(gè)方法實(shí)現(xiàn)簡(jiǎn)單,只要改變調(diào)用目標(biāo),使消息在新目標(biāo)上得以調(diào)用即可。但是這樣和備用接受者實(shí)現(xiàn)就一樣了,所以一般都不會(huì)這樣寫。

比較有用的實(shí)現(xiàn)是再出發(fā)消息前,先以某種方式改變消息內(nèi)容,比如追加另一個(gè)參數(shù),或是改裝選擇子。

如果發(fā)現(xiàn)不該由該類執(zhí)行,那么需要調(diào)用超類的該方法,繼承體系中每個(gè)類都有機(jī)會(huì)處理此調(diào)用請(qǐng)求,如果到NSObject還不能處理會(huì)調(diào)用doesNotRecognizerSelector拋出異常。

實(shí)現(xiàn)上面那個(gè)方法的時(shí)候需要同時(shí)實(shí)現(xiàn)。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

 

需要先對(duì)NSInvocation簽名然后才能使用NSInvocation

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {    NSMethodSignature *sig;    sig = [other methodSignatureForSelector:aSelector];    return sig;}-(void)forwardInvocation:(NSInvocation *)anInvocation{    [anInvocation invokeWithTarget:other];}

 

方法調(diào)配

在運(yùn)行時(shí),我們還可以使用方法調(diào)配技術(shù)來改變SEL指向的IMP,比如說目前方法名和對(duì)應(yīng)IMP如下

方法IMP
methodAIMPA
methodBIMPB

 

 

 

但是當(dāng)我們使用方法調(diào)配后,就可以出現(xiàn)一下情況

方法IMP
methodAIMPB
methodBIMPA

 

 

 

其實(shí)我們修改的本質(zhì)是方發(fā)表的映射,修改了選擇子的指向

該技術(shù)主要用到的方法如下

void method_exchangeImplementations(Method m1, Method m2)

該函數(shù)的兩個(gè)參數(shù)表示待交換的兩個(gè)方法實(shí)現(xiàn),而方法實(shí)現(xiàn)則可通過下列函數(shù)獲得:

Method class_getInstanceMethod(Class aClass , SEL aSelector)

現(xiàn)在我們向EqualObject中添加methodA和methodB

-(void)methodA{    NSLog(@"methodA");}-(void)methodB{    NSLog(@"methodB");}

 

然后客戶端這樣寫

        EqualObject *object1 = [EqualObject new];        Method methodA = class_getInstanceMethod([EqualObject class], @selector(methodA));        Method methodB = class_getInstanceMethod([EqualObject class], @selector(methodB));        method_exchangeImplementations(methodA, methodB);                [object1 methodA];

 

運(yùn)行后就會(huì)打印出methodB。

利用該技術(shù)進(jìn)行黑盒調(diào)試

要進(jìn)行黑盒調(diào)試,主要用到的就是該技術(shù)和category

下面來看看具體該如何編寫:

首先添加一個(gè)EqualObject的category,然后添加一個(gè)新方法plusMethodA

-(void)plusMethodA{    [self plusMethodA];    NSLog(@"this is plus version");}

 

這里看著像是會(huì)無(wú)限遞歸,但是實(shí)際上plusMethodA選擇子已經(jīng)指向了methodA的IMP,所以并不會(huì)出現(xiàn)無(wú)限調(diào)用的情況。

客戶端代碼

        EqualObject *object1 = [EqualObject new];        Method methodA = class_getInstanceMethod([EqualObject class], @selector(methodA));        Method methodB = class_getInstanceMethod([EqualObject class], @selector(plusMethodA));        method_exchangeImplementations(methodA, methodB);                [object1 methodA]

 

輸出結(jié)果為

2015-08-13 09:06:01.672 Equal[8282:5413229] methodA2015-08-13 09:06:01.673 Equal[8282:5413229] this is plus version

 

可以看到我們沒有繼承一個(gè)類就做到了擴(kuò)展某個(gè)方法,用于調(diào)試打印一些輸出信息會(huì)很有用!

 

常用Runtime總結(jié)

關(guān)聯(lián)對(duì)象:

設(shè)置一個(gè)關(guān)聯(lián)對(duì)象

void objc_setAssociatedObject(id object, void *key ,id value, objc_AssociationPolicy policy)

獲取關(guān)聯(lián)對(duì)象

void objc_getAssociatedObject(id object, void *key)

刪除該對(duì)象所有的關(guān)聯(lián)對(duì)象

void objc_removeAssociatedObjects(id object)

 

消息傳遞

向某個(gè)對(duì)象/父類 發(fā)送消息

objc_msgSend(Super)

 

方法調(diào)配

交換兩個(gè)方法的實(shí)現(xiàn)

void method_exchangeImplementation(Method m1, Method m2)

得到該法的指針

Method class_getInstanceMethod(Class aClass, SEL aSelector)

 

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

創(chuàng)建新的類

Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)

給類增加新的方法

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

注冊(cè)新的類

void objc_registerClassPair(Class cls)

獲得對(duì)象的isa指針?biāo)赶虻膶?duì)象

Class object_getClass(id obj)


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 91成人在线网站 | 久久综合久久精品 | 久久精品操 | 九草在线 | 成人性视频免费网站下载软件 | 久久久线视频 | 久久久久久久久久久久久久国产 | 香蕉成人在线视频 | 久久成人精品视频 | 99re66热这里只有精品8 | 国产五区 | 91看片免费版 | 视频一区二区三区视频 | 黄色片网站免费 | 欧美日韩精品不卡一区二区三区 | 国内精品视频饥渴少妇在线播放 | 久久成人免费观看 | 婷婷中文字幕一区二区三区 | 国产成人免费精品 | 免费黄色大片在线观看 | 欧美成人三级视频 | 黄色片视频免费观看 | 羞羞视频一区二区 | 久久色播 | av在线高清观看 | 黄色高清免费网站 | 国产精品视频一区二区三区四 | 在线视频观看一区二区 | 人成久久 | 日韩精品久久久 | 久久精品久久精品国产大片 | 香蕉久草视频 | 午夜视频在线免费观看 | 久久一区国产 | 欧美日本在线视频 | chinese乱子伦xxxx国语对白 | 91成人午夜性a一级毛片 | 日日鲁夜夜视频热线播放 | 日本精品视频一区二区三区四区 | 在线影院av| 欧美亚洲一级 |