iOS中runtime機(jī)制

?什么是runtime?

runtime就是運(yùn)行時(shí),因?yàn)镺bjective-C是一門動(dòng)態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時(shí)期做的事放到了運(yùn)行時(shí)來處理。也就是說只有編譯器是不夠的,還需要一個(gè)運(yùn)行時(shí)系統(tǒng) (runtime system) 來執(zhí)行編譯后的代碼。對(duì)于Objective-C來說,這個(gè)運(yùn)行時(shí)系統(tǒng)就像一個(gè)操作系統(tǒng)一樣:它讓所有的工作可以正常的運(yùn)行。這個(gè)運(yùn)行時(shí)系統(tǒng)即Objc Runtime。Objc Runtime其實(shí)是一個(gè)Runtime庫(kù),它基本上是用C和匯編寫的,這個(gè)庫(kù)使得C語言有了面向?qū)ο蟮哪芰Α?/p>

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

封裝:在這個(gè)庫(kù)中,對(duì)象可以用C語言中的結(jié)構(gòu)體表示,而方法可以用C函數(shù)來實(shí)現(xiàn),另外再加上了一些額外的特性。這些結(jié)構(gòu)體和函數(shù)被runtime函數(shù)封裝后,我們就可以在程序運(yùn)行時(shí)創(chuàng)建,檢查,修改類、對(duì)象和它們的方法了。

找出方法的最終執(zhí)行代碼:當(dāng)程序執(zhí)行[object doSomething]時(shí),會(huì)向消息接收者(object)發(fā)送一條消息(doSomething),runtime會(huì)根據(jù)消息接收者是否能響應(yīng)該消息而做出不同的反應(yīng)。OC中的方法調(diào)用:[receiver doSomething]; 在運(yùn)行時(shí)都會(huì)轉(zhuǎn)換成消息發(fā)送代碼:objc_msgSend(receiver, @selector(doSomething));,其中,receiver是消息的接收者,@selector(doSomething)是方法的實(shí)現(xiàn)。

runtime的使用場(chǎng)景

1.在程序運(yùn)行過程中, 動(dòng)態(tài)創(chuàng)建一個(gè)類(比如KVO的底層實(shí)現(xiàn))

2.在程序運(yùn)行過程中, 動(dòng)態(tài)地為某個(gè)類添加屬性\方法, 修改屬性值\方法(Objective-C中的Category無法向既有的類添加屬性, 因此可以使用runtime的關(guān)聯(lián)對(duì)象(associated objects)來實(shí)現(xiàn).)

3.遍歷一個(gè)類的所有成員變量(屬性)\所有方法

4. 給一個(gè)類添加方法

5.Method Swizzling(方法調(diào)換:通過修改一個(gè)已存在類的方法, 來實(shí)現(xiàn)方法替換是比較常用的runtime技巧)

簡(jiǎn)單介紹一下KVO的實(shí)現(xiàn)原理:

當(dāng)設(shè)置一個(gè)類為觀察對(duì)象時(shí),系統(tǒng)會(huì)動(dòng)態(tài)地創(chuàng)建一個(gè)新的類,這個(gè)新的類繼承自被觀察對(duì)象的類,還重寫了基類被觀察屬性的setter方法。派生類在被重寫的setter方法中實(shí)現(xiàn)真正的通知機(jī)制。最后,系統(tǒng)將這個(gè)對(duì)象的isa指針指向這個(gè)新創(chuàng)建的派生類,這樣,被觀察對(duì)象就變成了新創(chuàng)建的派生類的實(shí)例。(注:runtime中,對(duì)象的isa指針指向該對(duì)象所屬的類,類的isa指針指向該類的metaclass。有關(guān)OC的對(duì)象、類對(duì)象、元類對(duì)象metaclass object和isa指針,請(qǐng)戳這里詳細(xì)了解)。同時(shí),新的派生類還重寫了dealloc方法(removeObserver)。

動(dòng)態(tài)添加屬性

#import ?<UIKit/UIKit.h>

@interface UIButton (FinshClick)

@property(nonatomic,copy)void(^FinshClickBlock)(UIButton*); // 給button點(diǎn)擊回調(diào)屬性

@property(nonatomic,assign)floatclickCount;//給button添加點(diǎn)擊次數(shù)屬性

@end

#import"UIButton+FinshClick.h"

#import <objc/runtime.h> ?// 導(dǎo)入runtime框架

@implementation UIButton (FinshClick)

static char FinshClickBlockKey;

static char clickCountKey;

@dynamic FinshClickBlock;

@dynamic clickCount;

//@synthesize是默認(rèn)的聲明,意思是編譯器在編譯階段自動(dòng)為你的屬性生成setter與getter;而@dynamic則告訴編譯器,別慌,小子,編譯階段不用為我生成setter與getter,在runtime我已經(jīng)自己實(shí)現(xiàn)了setter與getter。此處我們選擇@dynamic。

// FinshClickBlock的setter方法

- (void)setFinshClickBlock:(void(^)(UIButton*))FinshClickBlock

{

objc_setAssociatedObject(self, &FinshClickBlockKey, FinshClickBlock,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

//使用runtime實(shí)現(xiàn)setter方法。參數(shù)含義:1.所關(guān)聯(lián)的對(duì)象、2.分配地址、3.屬性值、4.這里的policy跟屬性聲明中的retain、assign、copy是一樣的

[selfaddTarget:selfaction:@selector(click)forControlEvents:UIControlEventTouchUpInside];//給button添加點(diǎn)擊方法

}

//FinshClickBlock的getter方法

- (void(^)(UIButton*))FinshClickBlock

{

returnobjc_getAssociatedObject(self, &FinshClickBlockKey);//

//使用runtime實(shí)現(xiàn)getter方法。參數(shù)含義:1.關(guān)聯(lián)對(duì)象2.分配地址

}

//點(diǎn)擊實(shí)現(xiàn)

- (void)click {

if(self.FinshClickBlock) {

self.clickCount++;

self.FinshClickBlock(self);

}

}

//點(diǎn)擊次數(shù)setter方法

- (void)setClickCount:(float)clickCount

{

objc_setAssociatedObject(self,&clickCountKey,@(clickCount),OBJC_ASSOCIATION_ASSIGN);

}

// getter方法

- (float)clickCount

{

return[objc_getAssociatedObject(self,&clickCountKey)floatValue];

}

@end

#import"ViewController.h"

#import"UIButton+FinshClick.h"

@interface ViewController()

@property(weak,nonatomic)IBOutlet UIButton*btn;

@end

@implementation ViewController

- (void)viewDidLoad {

[superviewDidLoad];

self.btn.FinshClickBlock= ^(UIButton*button) {

NSLog(@"%f",self.btn.clickCount);

};

動(dòng)態(tài)添加方法

#import"Student.h"

#import <objc/message.h>

void eat(id self,SEL sel) {

NSLog(@"----------eat");

}

+(BOOL)resolveInstanceMethod:(SEL)sel

{

if([NSStringFromSelector(sel)isEqualToString:@"eat"]) {

class_addMethod(self, sel, (IMP)eat,"v@:");

returnYES;

}

return[superresolveInstanceMethod:sel];

}

@end

- (void)viewDidLoad {

[superviewDidLoad];

Student*stu = [[Studentalloc]init];

[stu performSelector:@selector(eat)];

}

最后編輯于
?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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