Runtime - 自己總結(jié)

Runtime概念

OC是基于C的,它為C添加了面向?qū)ο蟮奶匦裕鼘⒑芏囔o態(tài)語(yǔ)言在編譯和鏈接時(shí)期做的事情放到了runtime運(yùn)行時(shí)來(lái)處理,可以說(shuō)runtime是我們OC的幕后工作者
1.runtime簡(jiǎn)稱(chēng)運(yùn)行時(shí),是一套底層的c語(yǔ)言API,而OC就是運(yùn)行時(shí)機(jī)制,也就是運(yùn)行的一些機(jī)制,其中最主要的是消息機(jī)制。
objc在向一個(gè)對(duì)象發(fā)送消息時(shí),runtime庫(kù)會(huì)根據(jù)對(duì)象的isa指針找到該對(duì)象實(shí)際所屬的類(lèi),然后在該類(lèi)中的方法列表以及其父類(lèi)方法列表中尋找方法運(yùn)行,然后在發(fā)送消息的時(shí)候,objc_msfSend方法不會(huì)返回值,所謂的返回內(nèi)容都是具體調(diào)用時(shí)執(zhí)行的,那么如果想一個(gè)nil對(duì)象發(fā)送消息,首先在尋找對(duì)象的isa指針時(shí)就是0地址,返回了所以不會(huì)出現(xiàn)任何錯(cuò)誤。
對(duì)于c語(yǔ)言,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù)

而OC的函數(shù)調(diào)用成為消息發(fā)送,屬于動(dòng)態(tài)調(diào)用過(guò)程,在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù),只有在真正運(yùn)行的時(shí)候
才會(huì)根據(jù)函數(shù)的名稱(chēng)找到對(duì)應(yīng)的函數(shù)來(lái)調(diào)用。

2.實(shí)際上,在編譯階段,OC可以調(diào)用任何函數(shù),及時(shí)這個(gè)函數(shù)并未實(shí)現(xiàn),只有聲明過(guò)就不會(huì)報(bào)錯(cuò),只有當(dāng)運(yùn)行的時(shí)候才會(huì)報(bào)錯(cuò),這是因?yàn)镺C是運(yùn)行時(shí)動(dòng)態(tài)調(diào)用的,而C語(yǔ)言調(diào)用未實(shí)現(xiàn)的函數(shù)就會(huì)報(bào)錯(cuò)。

runtime 消息機(jī)制

我們寫(xiě)OC代碼,它在運(yùn)行的時(shí)候也是會(huì)轉(zhuǎn)換成runtime方式運(yùn)行。任何方法調(diào)用的本質(zhì):就是發(fā)送一個(gè)消息(用runtime發(fā)送消息,OC底層實(shí)現(xiàn)通過(guò)runtime實(shí)現(xiàn))。

消息機(jī)制原理:

比如我們創(chuàng)建一個(gè)對(duì)象[[NSObject alloc]init],最終被轉(zhuǎn)換為幾萬(wàn)行代碼,其實(shí)可以看到調(diào)用方法的本質(zhì)就是發(fā)送消息,[[NSObject alloc]init]語(yǔ)句發(fā)了兩個(gè)消息,第一次發(fā)了alloc消息,第二次發(fā)了init消息。利用這個(gè)功能我們可以探究底層,比如block的實(shí)現(xiàn)原理

利用runtime可以做一些oc不容易的實(shí)現(xiàn)功能;

1、獲得某個(gè)類(lèi)的所有成員變量、成員方法。


//獲取實(shí)例變量

unsignedintcount =0;

Ivar*ivars =class_copyIvarList([RuntimeToolsclass], &count);

for(inti =0; i

Ivarivar = ivars[i];

//獲取實(shí)例變量的名稱(chēng)

constchar*ivarName =ivar_getName(ivar);

//獲取成員變量的類(lèi)型

constchar*ivarKey =ivar_getTypeEncoding(ivar);

NSLog(@"名稱(chēng):%s,類(lèi)型:%s",ivarName,ivarKey);

}


//獲取類(lèi)的方法

unsignedintmethodCount =0;

//獲取RuntimeTools所有的類(lèi)方法

Method*allMethods =class_copyMethodList([RuntimeToolsclass], &methodCount);

for(inti =0; i < methodCount; i++) {

Methodmethod = allMethods[i];

SELmethodSel =method_getName(method);

constchar*name =sel_getName(methodSel);

NSLog(@"%s",name);

}

2.為某個(gè)類(lèi)的category添加一個(gè)新屬性

在category中不能添加成員變量,OC類(lèi)是由class類(lèi)型來(lái)表示的,它實(shí)際上是一個(gè)指向objc_class結(jié)構(gòu)體的指針定義如下:


struct objc_class {

Class isaOBJC_ISA_AVAILABILITY;

#if !__OBJC2__

Class super_class OBJC2_UNAVAILABLE; //父類(lèi)

cons tchar*name OBJC2_UNAVAILABLE; //類(lèi)名

long version OBJC2_UNAVAILABLE; 、//類(lèi)的版本信息

long info OBJC2_UNAVAILABLE;//類(lèi)信息

long instance_size OBJC2_UNAVAILABLE;//類(lèi)大小

struct objc_ivar_list *ivarsOBJC2_UNAVAILABLE; //該類(lèi)的成員變量鏈表

struct objc_method_list **methodLists OBJC2_UNAVAILABLE; //該類(lèi)的方法定義鏈表

struct objc_cache *cache OBJC2_UNAVAILABLE; //方法緩存

struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; //協(xié)議鏈表

#endif

} OBJC2_UNAVAILABLE;

在runtime中,objc_class結(jié)構(gòu)體大小是固定的,不可能往這個(gè)結(jié)構(gòu)體中添加數(shù)據(jù),只能修改。所以ivars指向的是一個(gè)固定區(qū)域,只能修改成員變量值,不能增加成員變量。method_list是一個(gè)二維數(shù)組,所以可以修改methodLists的值來(lái)增加成員方法。雖然沒(méi)辦法擴(kuò)展methodLists指向的內(nèi)存區(qū)域,卻可以改變這個(gè)內(nèi)存區(qū)域的值(存儲(chǔ)的指針)。因此可以動(dòng)態(tài)添加方法,不能添加成員變量


@interfaceRuntimeTools (Category)

@property(nonatomic,copy)NSString*lastName;

@end

生成set和get方法


- (void)setLastName:(NSString*)lastName

{

objc_setAssociatedObject(self,"lastName", lastName,OBJC_ASSOCIATION_COPY_NONATOMIC);

}

- (NSString*)lastName{

return objc_getAssociatedObject(self,"lastName");

}

使用runtime中objc_getAssociatedObject()和objc_setAssociatedObject()方法,本質(zhì)上只是為對(duì)象添加了對(duì)lastName的屬性關(guān)聯(lián),達(dá)到新屬性的作用

3.為某個(gè)類(lèi)添加一個(gè)新方法

class_addMethod([RuntimeToolsclass],@selector(newMethod), (IMP)myAddfunction,0);

[pperformSelector:@selector(newMethod)];

4.動(dòng)態(tài)交換2個(gè)方法的功能

Methodm1 =class_getInstanceMethod([selfclass],@selector(go));

Methodm2 =class_getClassMethod([selfclass],@selector(run));

method_exchangeImplementations(m1, m2);

交換方法使用場(chǎng)景:項(xiàng)目中的某個(gè)功能,在項(xiàng)目中需要多次被引用,當(dāng)項(xiàng)目的需求發(fā)生改變是,要使用另一個(gè)功能代替這個(gè)功能,且要求不改變舊的項(xiàng)目,那么,我們可以在分類(lèi)中,再寫(xiě)一個(gè)新的方法,然后交換2個(gè)方法的實(shí)現(xiàn)。

5.改變某個(gè)對(duì)象的私有變量的值

unsignedintcount =0;

Ivar *allList = class_copyIvarList([personclass], &count); 

Ivar ivv = allList[0];//從第一個(gè)例子getAllVariable中輸出的控制臺(tái)信息,我們可以看到name為第一個(gè)實(shí)例屬性。

object_setIvar(per, ivv,@"Mike");//name屬性Tom被強(qiáng)制改為Mike。

NSLog(@"改變之后的person:%@",per)

6.利用runtime獲取所有屬性來(lái)重寫(xiě)歸檔解檔方法
// 解檔方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self) {
        unsigned int count;
        Ivar *ivars = class_copyIvarList([self class], &count);
        
        for (int i = 0; i <count; i++) {
            Ivar ivar = ivars[i];
            const char *name = ivar_getName(ivar);
            //變量名轉(zhuǎn)換成NSSting類(lèi)型
            NSString *nameStr = [NSString stringWithFormat:@"%s",name];
            
            //解檔
            id value = [aDecoder decodeObjectForKey:nameStr];
            [self setValue:value forKey:nameStr];
            
        }
        free(ivars);
    }
    
        return self;
}

// 歸檔調(diào)用方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
    // 獲取所有成員變量
    unsigned int outCount = 0;
    Ivar *ivars = class_copyIvarList([self class], &outCount);
    for (int i = 0; i <outCount; i++) {
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        //變量名換成字符串
        NSString *key = [NSString stringWithFormat:@"%s",name];
        
        id value = [self valueForKey:key];
        [aCoder setValue:value forKey:key];
    }

}

如何應(yīng)用運(yùn)行時(shí)

1、可以將某些OC代碼轉(zhuǎn)換成運(yùn)行時(shí)的代碼,探究底層,比如block的實(shí)現(xiàn)原理
2、攔截系統(tǒng)自帶的方法調(diào)用。比如imageWithName:,alloc;viewDidLoad
3、實(shí)現(xiàn)分類(lèi)也可以增加屬性
4、實(shí)現(xiàn)NSCoding自動(dòng)歸檔和自動(dòng)解檔
5、實(shí)現(xiàn)字典和模型的自動(dòng)轉(zhuǎn)換。

動(dòng)態(tài)交換2個(gè)方法

、、、

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,592評(píng)論 33 466
  • 對(duì)于從事 iOS 開(kāi)發(fā)人員來(lái)說(shuō),所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢(mèng)夜繁星閱讀 3,732評(píng)論 7 64
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡(jiǎn)介 Runt...
    樂(lè)樂(lè)的簡(jiǎn)書(shū)閱讀 2,154評(píng)論 0 9
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,762評(píng)論 0 9
  • 這冷淡的光線照在她的手上,皺紋一下子暴露無(wú)余。那些糾纏交錯(cuò)的刻線將三維的空間添加上一個(gè)時(shí)間的長(zhǎng)軸,不可逆轉(zhuǎn)、無(wú)可奈...
    夢(mèng)蕪閣閱讀 409評(píng)論 0 0