?什么是runtime朽肥?
runtime就是運行時,因為Objective-C是一門動態(tài)語言擎浴,它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了運行時來處理虱疏。也就是說只有編譯器是不夠的,還需要一個運行時系統(tǒng) (runtime system) 來執(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í)行代碼:當(dāng)程序執(zhí)行[object doSomething]時玉罐,會向消息接收者(object)發(fā)送一條消息(doSomething)屈嗤,runtime會根據(jù)消息接收者是否能響應(yīng)該消息而做出不同的反應(yīng)。OC中的方法調(diào)用:[receiver doSomething]; 在運行時都會轉(zhuǎn)換成消息發(fā)送代碼:objc_msgSend(receiver, @selector(doSomething));吊输,其中饶号,receiver是消息的接收者,@selector(doSomething)是方法的實現(xiàn)璧亚。
runtime的使用場景
1.在程序運行過程中, 動態(tài)創(chuàng)建一個類(比如KVO的底層實現(xiàn))
2.在程序運行過程中, 動態(tài)地為某個類添加屬性\方法, 修改屬性值\方法(Objective-C中的Category無法向既有的類添加屬性, 因此可以使用runtime的關(guān)聯(lián)對象(associated objects)來實現(xiàn).)
3.遍歷一個類的所有成員變量(屬性)\所有方法
4. 給一個類添加方法
5.Method Swizzling(方法調(diào)換:通過修改一個已存在類的方法, 來實現(xiàn)方法替換是比較常用的runtime技巧)
簡單介紹一下KVO的實現(xiàn)原理:
當(dāng)設(shè)置一個類為觀察對象時讨韭,系統(tǒng)會動態(tài)地創(chuàng)建一個新的類脂信,這個新的類繼承自被觀察對象的類癣蟋,還重寫了基類被觀察屬性的setter方法。派生類在被重寫的setter方法中實現(xiàn)真正的通知機制狰闪。最后疯搅,系統(tǒng)將這個對象的isa指針指向這個新創(chuàng)建的派生類,這樣埋泵,被觀察對象就變成了新創(chuàng)建的派生類的實例幔欧。(注:runtime中罪治,對象的isa指針指向該對象所屬的類,類的isa指針指向該類的metaclass礁蔗。有關(guān)OC的對象觉义、類對象、元類對象metaclass object和isa指針浴井,請戳這里詳細了解)晒骇。同時,新的派生類還重寫了dealloc方法(removeObserver)磺浙。
動態(tài)添加屬性
#import ?<UIKit/UIKit.h>
@interface UIButton (FinshClick)
@property(nonatomic,copy)void(^FinshClickBlock)(UIButton*); // 給button點擊回調(diào)屬性
@property(nonatomic,assign)floatclickCount;//給button添加點擊次數(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是默認的聲明洪囤,意思是編譯器在編譯階段自動為你的屬性生成setter與getter;而@dynamic則告訴編譯器撕氧,別慌瘤缩,小子,編譯階段不用為我生成setter與getter伦泥,在runtime我已經(jīng)自己實現(xiàn)了setter與getter剥啤。此處我們選擇@dynamic。
// FinshClickBlock的setter方法
- (void)setFinshClickBlock:(void(^)(UIButton*))FinshClickBlock
{
objc_setAssociatedObject(self, &FinshClickBlockKey, FinshClickBlock,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//使用runtime實現(xiàn)setter方法奄喂。參數(shù)含義:1.所關(guān)聯(lián)的對象铐殃、2.分配地址、3.屬性值跨新、4.這里的policy跟屬性聲明中的retain富腊、assign、copy是一樣的
[selfaddTarget:selfaction:@selector(click)forControlEvents:UIControlEventTouchUpInside];//給button添加點擊方法
}
//FinshClickBlock的getter方法
- (void(^)(UIButton*))FinshClickBlock
{
returnobjc_getAssociatedObject(self, &FinshClickBlockKey);//
//使用runtime實現(xiàn)getter方法域帐。參數(shù)含義:1.關(guān)聯(lián)對象2.分配地址
}
//點擊實現(xiàn)
- (void)click {
if(self.FinshClickBlock) {
self.clickCount++;
self.FinshClickBlock(self);
}
}
//點擊次數(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);
};
動態(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)];
}