*看完本篇之后你將獲得:
1.了解什么是runtime
2.知道可以利用runtime做到哪些事情
3.掌握用runtime開發(fā)的常用方法
*定義
1.RunTime簡稱運(yùn)行時(shí),就是系統(tǒng)在運(yùn)行的時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制。
2.對于C語言,函數(shù)的調(diào)用在編譯的時(shí)候會決定調(diào)用哪個(gè)函數(shù),編譯完成之后直接順序執(zhí)行,無任何二義性。
3.OC的函數(shù)調(diào)用成為消息發(fā)送。屬于動(dòng)態(tài)調(diào)用過程。在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)(事實(shí)證明,在編 譯階段,OC可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn),只要申明過就不會報(bào)錯(cuò)。而C語言在編譯階段就會報(bào)錯(cuò))。
4.只有在真正運(yùn)行的時(shí)候才會根據(jù)函數(shù)的名稱找 到對應(yīng)的函數(shù)來調(diào)用。
*進(jìn)階
我們?yōu)槭裁匆獙W(xué)習(xí)runtime
1.runtime可以遍歷類和對象的屬性、成員變量、方法、協(xié)議列表
2.runtime可以動(dòng)態(tài)添加/修改屬性,動(dòng)態(tài)添加/修改/替換方法,動(dòng)態(tài)添加/修改/替換協(xié)議
3.runtime可以方法攔截調(diào)用
1.遍歷類和對象的屬性、成員變量、方法
通過使用runtime獲取類或?qū)ο蟮乃袑傩?、成員變量,包括我們在"command+左擊"進(jìn)去看不到,然后通過
[object setValue:forKeyPath:@""];
進(jìn)行屬性賦值。最常見的應(yīng)該是TextField的placeholder字體顏色了:
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
具體代碼如下:
*遍歷屬性:
unsigned int count;
objc_property_t *properties = class_copyPropertyList([UIView class], &count);
for (int i = 0; i < count; i++) {
const char *propertiesName = property_getName(properties[i]);
NSString *str = [NSString stringWithCString:propertiesName encoding:NSUTF8StringEncoding];
NSLog(@"propertyName : %@", str);
}
*遍歷成員變量:
typedef struct objc_ivar *Ivar;
unsigned int count;
Ivar *ivars = class_copyIvarList([UIView class], &count);
for (int i = 0; i < count; i++) {
const char *ivarName = ivar_getName(ivars[i]);
NSString *str = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding];
NSLog(@"ivarName : %@", str);
}
*遍歷方法:
unsigned count = 0;
// 獲取所有方法
Method *methods = class_copyMethodList([UIView class], &count);
for (int i = 0; i < count; i++) {
Method method = methods[i];
// 方法類型是SEL選擇器類型
SEL methodName = method_getName(method);
NSString *str = [NSString stringWithCString:sel_getName(methodName) encoding:NSUTF8StringEncoding];
int arguments = method_getNumberOfArguments(method);
NSLog(@"methodName : %@, arguments Count: %d", str, arguments);
}
*遍歷協(xié)議:
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
for (unsigned int i; i<count; i++) {
Protocol *myProtocal = protocolList[i];
const char *protocolName = protocol_getName(myProtocal);
NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
}
2.runtime可以動(dòng)態(tài)添加/修改屬性,動(dòng)態(tài)添加/修改/替換方法,動(dòng)態(tài)添加/修改/替換協(xié)議
*替換方法(攔截系統(tǒng)方法):
Method origin = class_getInstanceMethod([UIView class], @selector(touchesBegan:withEvent:));
Method custom = class_getInstanceMethod([UIView class], @selector(custom_touchesBegan:withEvent:));
method_exchangeImplementations(origin, custom);
- (void)custom_touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
*動(dòng)態(tài)添加方法:(為什么動(dòng)態(tài)添加方法)
使用場景:一個(gè)類方法非常多,一次性加載到內(nèi)存比較耗費(fèi)資源, OC都是懶加載,有些方法可能很久不會調(diào)用(只有滿足特定條件才會調(diào)用),所以就要?jiǎng)討B(tài)添加方法。
調(diào)用eat方法
Person *p = [[Person alloc] init];
[p performSelector:@selector(eat)];
// 當(dāng)一個(gè)對象調(diào)用未實(shí)現(xiàn)的方法,會調(diào)用這個(gè)方法處理,并且會把對應(yīng)的方法列表傳過來.
// 剛好可以用來判斷,未實(shí)現(xiàn)的方法是不是我們想要?jiǎng)討B(tài)添加的方法
.m
#import "Person.h"
@implementation Person
void eat(id self,SEL sel){
NSLog(@"---runtime---%@ %@",self,NSStringFromSelector(sel));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(eat)) {
// 動(dòng)態(tài)添加eat方法
// 第一個(gè)參數(shù):給哪個(gè)類添加方法
// 第二個(gè)參數(shù):添加方法的方法編號
// 第三個(gè)參數(shù):添加方法的函數(shù)實(shí)現(xiàn)(函數(shù)地址)
// 第四個(gè)參數(shù):函數(shù)的類型,(返回值+參數(shù)類型)
class_addMethod(self, @selector(eat), (IMP)eat, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
*給category添加屬性:
.h
#import <Foundation/Foundation.h>
@interface NSObject (Property){
}
@property (nonatomic,copy) NSString *name;
@end
.m
#import "NSObject+Property.h"
@implementation NSObject (Property)
- (void)setName:(NSString *)name
{
/*
object:保存到哪個(gè)對象中
key:用什么屬性保存 屬性名
value:保存值
policy:策略,strong,weak
*/
objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, "name");
}