*看完本篇之后你將獲得:
1.了解什么是runtime
2.知道可以利用runtime做到哪些事情
3.掌握用runtime開發的常用方法
*定義
1.RunTime簡稱運行時,就是系統在運行的時候的一些機制,其中最主要的是消息機制。
2.對于C語言,函數的調用在編譯的時候會決定調用哪個函數,編譯完成之后直接順序執行,無任何二義性。
3.OC的函數調用成為消息發送。屬于動態調用過程。在編譯的時候并不能決定真正調用哪個函數(事實證明,在編 譯階段,OC可以調用任何函數,即使這個函數并未實現,只要申明過就不會報錯。而C語言在編譯階段就會報錯)。
4.只有在真正運行的時候才會根據函數的名稱找 到對應的函數來調用。
*進階
我們為什么要學習runtime
1.runtime可以遍歷類和對象的屬性、成員變量、方法、協議列表
2.runtime可以動態添加/修改屬性,動態添加/修改/替換方法,動態添加/修改/替換協議
3.runtime可以方法攔截調用
1.遍歷類和對象的屬性、成員變量、方法
通過使用runtime獲取類或對象的所有屬性、成員變量,包括我們在"command+左擊"進去看不到,然后通過
[object setValue:forKeyPath:@""];
進行屬性賦值。最常見的應該是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);
}
*遍歷協議:
__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可以動態添加/修改屬性,動態添加/修改/替換方法,動態添加/修改/替換協議
*替換方法(攔截系統方法):
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 {
}
*動態添加方法:(為什么動態添加方法)
使用場景:一個類方法非常多,一次性加載到內存比較耗費資源, OC都是懶加載,有些方法可能很久不會調用(只有滿足特定條件才會調用),所以就要動態添加方法。
調用eat方法
Person *p = [[Person alloc] init];
[p performSelector:@selector(eat)];
// 當一個對象調用未實現的方法,會調用這個方法處理,并且會把對應的方法列表傳過來.
// 剛好可以用來判斷,未實現的方法是不是我們想要動態添加的方法
.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)) {
// 動態添加eat方法
// 第一個參數:給哪個類添加方法
// 第二個參數:添加方法的方法編號
// 第三個參數:添加方法的函數實現(函數地址)
// 第四個參數:函數的類型,(返回值+參數類型)
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:保存到哪個對象中
key:用什么屬性保存 屬性名
value:保存值
policy:策略,strong,weak
*/
objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, "name");
}