關(guān)于runtime簡單來說就是運(yùn)行時,是系統(tǒng)運(yùn)行的一些機(jī)制,其中最主要的是消息機(jī)制。
example:
OC里面調(diào)用方法:
[objc setName];
在編譯的時候runtime會將上述代碼轉(zhuǎn)換成:objc_msgSend(objc,@seletor(setName));
更詳細(xì)的定義網(wǎng)上有許多,這里就不詳細(xì)介紹,這里主要介紹幾個常用的方法。
一、 數(shù)據(jù)的轉(zhuǎn)換(字典到model)
class_copyPropertyList可以獲取當(dāng)前類的屬性
///通過運(yùn)行時獲取當(dāng)前對象的所有屬性的名稱,以數(shù)組的形式返回
- (NSArray *) allPropertyNames{
///存儲所有的屬性名稱
NSMutableArray *allNames = [[NSMutableArray alloc] init];
///存儲屬性的個數(shù)
unsigned int propertyCount = 0;
///通過運(yùn)行時獲取當(dāng)前類的屬性
objc_property_t *propertys = class_copyPropertyList([self class], &propertyCount);
//把屬性放到數(shù)組中
for (int i = 0; i < propertyCount; i ++) {
///取出第一個屬性
objc_property_t property = propertys[i];
const char * propertyName = property_getName(property);
[allNames addObject:[NSString stringWithUTF8String:propertyName]];
}
///釋放
free(propertys);
return allNames;
}
/// 通過字符串來創(chuàng)建該字符串的Setter方法,并返回
- (SEL) creatSetterWithPropertyName: (NSString *) propertyName{
//1.首字母大寫
propertyName = propertyName.capitalizedString;
//2.拼接上set關(guān)鍵字
propertyName = [NSString stringWithFormat:@"set%@:", propertyName];
//3.返回set方法
return NSSelectorFromString(propertyName);
}
/// 把字典賦值給當(dāng)前實(shí)體類的屬性
-(void) assginToPropertyWithDictionary: (NSDictionary *) data{
if (data == nil) {
return;
}
///1.獲取字典的key
NSArray *dicKey = [data allKeys];
///2.循環(huán)遍歷字典key, 并且動態(tài)生成實(shí)體類的setter方法,把字典的Value通過setter方法
///賦值給實(shí)體類的屬性
for (int i = 0; i < dicKey.count; i ++) {
///屬性的存在
if ([[self allPropertyNames]containsObject:dicKey[i]]) {
///2.1 通過getSetterSelWithAttibuteName 方法來獲取實(shí)體類的set方法
SEL setSel = [self creatSetterWithPropertyName:dicKey[i]];
if ([self respondsToSelector:setSel]) {
///2.2 獲取字典中key對應(yīng)的value
NSString *value = [NSString stringWithFormat:@"%@", data[dicKey[i]]];
///2.3 把值通過setter方法賦值給實(shí)體類的屬性
[self performSelectorOnMainThread:setSel
withObject:value
waitUntilDone:[NSThread isMainThread]];
}
}
}
}
- 思考
上面的代碼是在model的成員變量的名稱和字典的key一致的情況下,如何實(shí)現(xiàn)不一致的情況?(一一映射)
二、 歸檔
實(shí)現(xiàn)NSCoding協(xié)議,class_copyIvarList可以獲取當(dāng)前類的成員變量
- (void)encodeWithCoder:(NSCoder *)aCoder {
Class c = self.class;
// 截取類和父類的成員變量
while (c && c != [NSObject class]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(c, &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
c = [c superclass];
// 釋放內(nèi)存
free(ivars);
}
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
Class c = self.class;
// 截取類和父類的成員變量
while (!c && c != [NSObject class]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i=0; i<count; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
c = [c superclass];
free(ivars);
}
}
return self;
}
三、 關(guān)聯(lián)對象
分類不可以創(chuàng)建屬性,但是可以通過運(yùn)行時來設(shè)置成員變量
static NSString *value = nil;
//設(shè)置關(guān)鍵key
static const char associatedkey;
//保存value值
value = @"test";
objc_setAssociatedObject(self, &associatedkey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//通過 objc_getAssociatedObject 獲取關(guān)聯(lián)對象
value = objc_getAssociatedObject(self, &associatedkey);
四、 方法替換
增加一個分類,在分類中實(shí)現(xiàn):
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 如果 swizzling 的是類方法, 采用如下的方式:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
//交換實(shí)現(xiàn)
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
///Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
應(yīng)用場景:一個項(xiàng)目,替換所有圖片,但是圖片多了一個后綴“_new”,可以替換imageName方法;埋點(diǎn);