修改變量值
用runtime方法修改變量值,相當(dāng)于賦值語(yǔ)句晶密,主要原理是遍歷類的IvarList虐秋,找到名字相同的實(shí)例變量,然后修改其屬性值努潘。
// 獲取指定名的實(shí)例變量
- (Ivar)getIvarWithName: (const char *)ivarName {
unsigned int count = 0;
// 獲取實(shí)例變量列表
Ivar *ivarList = class_copyIvarList(_person.class, &count);
// 遍歷屬性名
for (int i=0; i<count; i++) {
Ivar ivar = ivarList[i];
const char *name = ivar_getName(ivar);
if (strcmp(name, ivarName) == 0) {
// class_copyIvarList會(huì)申請(qǐng)內(nèi)存诽偷,需要手動(dòng)釋放
free(ivarList);
return ivar;
}
}
free(ivarList);
return NULL;
}
// 修改變量值
Ivar name = [self getIvarWithName:"_name"];
if (name != NULL) {
object_setIvar(_person, name, @"new name");
}
添加屬性
常見于在category里添加屬性,自定義getter和setter疯坤,因?yàn)閏ategory不是類报慕,沒有指向類的isa指針,也就沒有ivar_list贴膘,即使添加屬性也不會(huì)生成setter和getter的實(shí)現(xiàn)以及成員變量卖子,所以只能通過關(guān)聯(lián)方式添加屬性。
// 添加
const char *key = "age";
objc_setAssociatedObject(_person, key, @"18 years old", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// 獲取屬性
objc_getAssociatedObject(_person, key)
添加方法
可用通過class_addMethod
添加方法刑峡,主要是通過selector和imp創(chuàng)建一個(gè)method_t添加到類的method_list中洋闽。最后一個(gè)參數(shù)是 type encodings 玄柠,用來(lái)標(biāo)識(shí)IMP函數(shù)實(shí)現(xiàn)的返回值與參數(shù),具體可參考官方給出的對(duì)應(yīng)表 type encodings诫舅。
- (void)dynamicallyAddMethod {
SEL selector = @selector(tempMethod);
IMP imp = class_getMethodImplementation(self.class, selector);
const char *type = method_getTypeEncoding(class_getInstanceMethod(self.class, selector));
// 添加方法
if (![_person respondsToSelector:selector]) {
class_addMethod(_person.class, selector, imp, type);
}
if ([_person respondsToSelector:selector]) {
// 調(diào)用方法
IMP personImp = [_person methodForSelector:selector];
NSString *(*func)(id, SEL) = (void *)personImp;
NSLog(@"%@", func(_person, selector));
} else {
NSLog(@"Fail to add method dynamically");
}
}
交換方法
可以交換對(duì)象中的兩個(gè)方法羽利,還可以交換不同對(duì)象的方法
- (void)exchangeMethod {
NSLog(@"Before exchanging method1 result: ");
[_person method1];
Method imp1 = class_getInstanceMethod(_person.class, @selector(method1));
Method imp2 = class_getInstanceMethod(self.class, @selector(methodForExchange));
method_exchangeImplementations(imp1, imp2);
NSLog(@"After exchanging method1 result: ");
[_person method1];
// 換回去
method_exchangeImplementations(imp1, imp2);
}
拓展功能
主要原理還是上面的交換方法,不過拓展功能應(yīng)不影響原本方法刊懈,因此在新方法中这弧,需要調(diào)用原本方法。此功能比較常見虚汛,比如想要記錄ViewController生命周期匾浪,可以另外寫一個(gè)ViewController的分類,然后在load
方法里拓展對(duì)應(yīng)方法卷哩,添加自定義記錄蛋辈。
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originMethod = class_getInstanceMethod(self.class, @selector(viewDidLoad));
Method extendMethod = class_getInstanceMethod(self.class, @selector(extendMethodForViewDidLoad));
method_exchangeImplementations(originMethod, extendMethod);
});
}
- (void)extendMethodForViewDidLoad {
// 進(jìn)行自定義操作
NSLog(@"Intercept viewDidLoad method and do something... >_>");
// 調(diào)用原來(lái)的viewDidLoad
[self extendMethodForViewDidLoad];
}
歸檔解檔
當(dāng)我們使用 NSCoding 進(jìn)行歸檔及解檔時(shí),我們需要對(duì)所有屬性實(shí)現(xiàn)一遍 encodeObject 和 decodeObjectForKey 方法将谊,如果模型里面有 10000 個(gè)屬性, 那么我們就需要寫 10000 次冷溶,這個(gè)時(shí)候可以用 runtime 簡(jiǎn)化操作 。(注意:自定義類型要自行轉(zhuǎn)換)
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(self.class, &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivarList[i];
// 獲取屬性名
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 根據(jù)屬性名稱獲取對(duì)應(yīng)的值
id value = object_getIvar(self, ivar);
[aCoder encodeObject:value forKey:key];
}
free(ivarList);
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(self.class, &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivarList[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 解檔取值
id value = [aDecoder decodeObjectForKey:key];
// 賦值
object_setIvar(self, ivar, value);
}
free(ivarList);
}
return self;
}
模型轉(zhuǎn)字典
目前常用模型轉(zhuǎn)字典第三方庫(kù)尊浓,比如MJExtension逞频、YYModel,其實(shí)現(xiàn)方式主要也是通過runtime的方法獲取和遍歷屬性栋齿,然后對(duì)名稱苗胀、類型、值進(jìn)行轉(zhuǎn)換操作瓦堵。
- (void)modelToDict {
unsigned int count = 0;
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
// 獲取成員屬性數(shù)組
Ivar *ivarList = class_copyIvarList(_person.class, &count);
// 遍歷所有的成員屬性名
for (int i = 0; i < count; i ++) {
// 獲取成員屬性
Ivar ivar = ivarList[i];
NSString *key = [[NSString stringWithUTF8String:ivar_getName(ivar)] substringFromIndex:1];
id value = object_getIvar(_person, ivar);
dictionary[key] = value;
}
free(ivarList);
NSLog(@"Model to dictionary: %@", dictionary);
}