1. 全局替換系統的方法為自己的方法
使用UIImage imageNamed 加載圖片時,由于需要手動輸入名稱,這個很容易導致加載的圖片對象為空, 這個問題很容易忽視. 這時如果替換掉系統的方法, 并判斷是否為空,并打印出來 這就很直觀.
@implementation UIImage (image)
//分類被加入運行時的時候就調用,在main方法之前.
+(void)load{
// 1.獲取系統方法
Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
// 2.自己實現的方法
Method ln_imageNamedMethod = class_getClassMethod(self, @selector(ln_imageNamed:));
//3.交換方法
method_exchangeImplementations(imageNamedMethod, ln_imageNamedMethod);
}
+(UIImage *)ln_imageNamed:(NSString *)name{
//這里是關鍵,因為ln_imageNamed已經 和 imageNamed交換,這里實際調用的是imageNamed,并不會導致循環調用.
UIImage *image = [UIImage ln_imageNamed:name];
if (image) {
NSLog(@"runtime添加額外功能--加載成功");
}else{
NSLog(@"runtime添加額外的功能--加載失敗");
}
return image;
}
@end
//使用
[UIImage imageNamed:@"imageName"];
2. 給系統的類增加屬性.
系統的類不能滿足自己的需求,需要增加額外的屬性.其實本質就是給這個類添加關聯,并不是直接把這個值的內存空間添加到類存空間。
@interface NSObject (Property)
@property NSString *name;
@end
@implementation NSObject (Property)
- (void)setName:(NSString *)name{
//將屬性的值和傳遞進的name關聯
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)name{
//獲取關聯屬性的值
return objc_getAssociatedObject(self, @"name");
}
@end
3. 自己實現字典轉屬性
@interface NSObject (DictToModel)
+(instancetype)modelWithDict:(NSDictionary *)dict;
@end
@implementation NSObject (DictToModel)
+(instancetype)modelWithDict:(NSDictionary *)dict{
//1.創建對象
id objc = [[self alloc]init];
// 2.利用runtime給對象中的屬性賦值
/**
class_copyIvarList: 獲取類中的所有成員變量
Ivar:成員變量
第一個參數:表示獲取哪個類中的成員變量
第二個參數:表示這個類有多少成員變量,傳入一個Int變量地址,會自動給這個變量賦值
返回值Ivar *:指的是一個ivar數組,會把所有成員屬性放在一個數組中,通過返回的數組就能全部獲取到。
count: 成員變量個數
*/
unsigned int count = 0;
//3.獲取類中所有的成員變量
Ivar *ivarList = class_copyIvarList(self, &count);
//遍歷數組所有成員變量
for (int i = 0; i < count; i++) {
//從數組中取出對應的成員
Ivar ivar = ivarList[i];
//獲取成員變量的名字
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
//處理成員變量 -> 字典中的key(去掉_ )
NSString *key = [ivarName substringFromIndex:1];
NSLog(@"ivarName:%@",key);
//根據成員屬性名去字典查找對應的value
id value = dict[ivarName];
//找到value
if (value) {
//模型中的屬性賦值
[objc setValue:value forKey:key];
}
}
return objc;
}
@end
4.動態添加方法
如果一個類方法非常多,加載類到內存的時候比較耗費資源,需要給每個方法生產映射表,可以動態給某個類,添加方法來解決.
@implementation PersonModel
// 沒有返回值,1個參數
// void,(id,SEL)
void aaa(id self, SEL _cmd, NSNumber *meter) {
NSLog(@"跑了%@米", meter);
}
// 任何方法默認都有兩個隱式參數,self,_cmd(當前方法的方法編號)
// 什么時候調用:只要一個對象調用了一個未實現的方法就會調用這個方法,進行處理
// 作用:動態添加方法,處理未實現
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
// [NSStringFromSelector(sel) isEqualToString:@"run"];
if (sel == NSSelectorFromString(@"run:")) {
// 動態添加run方法
// class: 給哪個類添加方法
// SEL: 添加哪個方法,即添加方法的方法編號
// IMP: 方法實現 => 函數 => 函數入口 => 函數名(添加方法的函數實現(函數地址))
// type: 方法類型,(返回值+參數類型) v:void @:對象->self :表示SEL->_cmd
class_addMethod(self, sel, (IMP)aaa, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
//使用
// 默認PersonModel,沒有實現run:方法,可以通過performSelector調用,但是會報錯。
// 動態添加方法就不會報錯
PersonModel *person = [PersonModel new];
[person performSelector:@selector(run:) withObject:@10];
//輸出
跑了10米