一 , 设置关联属性
分类中设置属性进行关联判断(其中SDWebimage中用到过)
/**
* 下载图像的 URL 字符串
* 一个属性:分类中不能有 ivar(成员变量) / getter / setter
*/
const char *jq_URLStringKey = "jq_URLStringKey";
@property (nonatomic, copy) NSString *jq_urlString;
- (NSString *)jq_urlString {
//利用运行时记录属性
return objc_getAssociatedObject(self, jq_URLStringKey);
}
- (void)setJq_urlString:(NSString *)jq_urlString {
objc_setAssociatedObject(self, jq_URLStringKey, jq_urlString, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
二, 运行时动态调用方法(在OC与JS的交互中用到)
#import <objc/message.h>
1 > 调用无返回值的方法
参数一位当前调用方法对象
参数二为方法对象
参数三为需要传递的参数
下面是最基本的调用格式
SEL sel = NSSelectorFromString(methodName);
((void(*)(id, SEL, id))objc_msgSend)(self, sel, parameter);
2 > 调用有返回值的方法
NSString *result = ((NSString *(*)(id, SEL, id, id, id))objc_msgSend)(self, sel, parameter, parameter2, parameter3);
- (NSString *)showFood1:(NSString *)food1 Food2:(NSString *)food2 Food3:(NSString *)food3 {
NSLog(@"%@ -- %@ -- %@", food1, food2, food3);
return @"delicious";
}
3 > 调用类方法, 需要将类转为类对象
Class personClass = [Person class];
[personClass performSelector:@selector(drink)];
((void(*)(id, SEL))objc_msgSend)(personClass, @selector(drink));
三, 动态获取类的属性和成员变量(字典转模型或KVC改变系统属性变量)
const void *propertyListKey = @"propertyListKey";
+ (NSArray *)jq_propertyList {
// 0. --- 判断属性数组是否存在,如果存在直接返回 `属性数组对象` ---
NSArray *result = objc_getAssociatedObject(self, propertyListKey);
if (result != nil) {
return result;
}
// 1. 获取属性数组
unsigned int count = 0;
objc_property_t *list = class_copyPropertyList([self class], &count);
NSLog(@"属性数量 %u", count);
NSMutableArray *arrM = [NSMutableArray array];
// 2. 遍历数组
for (unsigned int i = 0; i < count; i++) {
// 1> 通过下标获取属性对象
objc_property_t property = list[i];
// 2> 获取属性的名称
const char *pty = property_getName(property);
// 3> 转换成 OC 的字符串
NSString *str = [NSString stringWithUTF8String:pty];
[arrM addObject:str];
}
//释放数组
free(list);
// --- 保存属性数组对象 ---
objc_setAssociatedObject(self, propertyListKey, arrM, OBJC_ASSOCIATION_COPY_NONATOMIC);
return arrM.copy;
}
+ (NSArray *)jq_ivarList {
// 1. 取类的成员变量列表
unsigned int count = 0;
Ivar *list = class_copyIvarList([self class], &count);
// NSLog(@"%u", count);
NSMutableArray *arrayM = [NSMutableArray array];
// 2. 遍历数组
for (unsigned int i = 0; i < count; i++) {
// 1> 根据下标获取成员变量
Ivar ivar = list[i];
// 2> 取 ivar 的名字
const char *cName = ivar_getName(ivar);
// 3> 转换成 NSString
NSString *name = [NSString stringWithUTF8String:cName];
[arrayM addObject:name];
}
// 3. 释放列表
free(list);
return arrayM.copy;
}
四, 动态交换方法(在AFNetworking中 URLSession 做了一个交叉方法, 交换了系统的resume 方法, 以便在系统调用时可以发送一个通知来监听方法实现)
+ (void)load {
Method originalMethod = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method swizzledMethod = class_getClassMethod([UIImage class], @selector(jq_imageNamed:));
//交换方法
method_exchangeImplementations(originalMethod, swizzledMethod);
}
+ (UIImage *)jq_imageNamed:(NSString *)imageName {
// 1, 加载图片
UIImage *image = [UIImage jq_imageNamed:imageName];
// 2, 判断功能
if (image == nil) {
NSLog(@" image == nil");
}
return image;
}
五, 动态添加方法(不常用)
void laugh(id self, SEL _cmd, id param) {
NSLog(@"%@ - %@ - %@", self, NSStringFromSelector(_cmd), param);
}
//当调用了没有实现的方法就会调用
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"%@", NSStringFromSelector(sel));
//动态添加laugh方法
if (sel == @selector(laugh:)) {
/*
Class: 给哪个类添加
SEL: 方法编号
IMP: 方法实现, 函数名
types: 方法类型 v -> void @ -> 第一个参数 表示id类型 : -> 代码SEL 方法
*/
class_addMethod(self, sel, (IMP)laugh, "v@:@");
//处理完
return YES;
}
return [super resolveInstanceMethod:sel];
}