runtime(四)-常见操作
2018-04-07 本文已影响4人
George_Luofz
runtime很复杂庞大,懂了oc对象布局,理解了发消息过程和消息转发流程,还不够。
接下来再学习一些常见用法,因为我们学到的那些毕竟是系统如何用的,自己会用才行。
runtime可以创建一个类,对一个类做各种改造:添加属性/变量、添加方法、添加协议
- 方法替换/新增方法
替换两个方法的imp,一般将替换操作放在load方法中(load方法在类加载时就会触发,这样在程序运行时就已经替换完毕了,相当于一个速度的优化;而且该方法只会执行一次,不会出现重复替换的情况)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalSEL = @selector(say);
SEL newSEL = @selector(newSay);
Method originalMethod = class_getInstanceMethod(self, originalSEL);
Method newMethod = class_getInstanceMethod(self, newSEL);
// 添加一个方法
BOOL flag = class_addMethod(self, originalSEL, method_getImplementation(newMethod), "v@:");
if(flag){
// 替换一个SEL的实现
class_replaceMethod(self, newSEL, method_getImplementation(originalMethod), "v@:");
}else{
// 交换两个方法的实现
method_exchangeImplementations(originalMethod, newMethod);
}
});
}
- 消息转发
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if([NSStringFromSelector(sel) isEqualToString:@"test"]){
Method method = class_getInstanceMethod(self, @selector(_testIMP));
bool success = class_addMethod(self, @selector(test), method_getImplementation(method), "v@:");
if(success) return YES;
return NO;
}
return NO;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if([NSStringFromSelector(aSelector) isEqualToString:@"test"]){
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
-(id)forwardingTargetForSelector:(SEL)aSelector{
// if([NSStringFromSelector(aSelector) isEqualToString:@"test"]){
// return [[TestObj alloc] init];
// }
return [super forwardingTargetForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
// SEL sel = anInvocation.selector;
// if([NSStringFromSelector(sel) isEqualToString:@"test"]){
// TestObj *obj = [TestObj new];
// if([obj respondsToSelector:sel]){
// [anInvocation invokeWithTarget:obj];
// return;
// }
// }
[super forwardInvocation:anInvocation];
}
可以在三个位置处理消息转发,在runtime(二)有说明
- category加属性
static char * keyName = "KAPropKey";
- (void)setProp:(NSString *)prop{
objc_setAssociatedObject(self, keyName, prop, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)prop{
return objc_getAssociatedObject(self, keyName);
}
OBJC_ASSOCIATION_COPY_NONATOMIC有几种取值,参照doc:
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
_NONATOMIC加下划线的表示非原子性,相当于@property(nonatomic)
关于category加属性如何实现,在category专题学习
- json转model
其实思路就是利用runtime获取property列表,然后取出json中的键值对,使用KVC为每一个property赋值
举个栗子:
- (void)_test_jsonToModel{
NSDictionary *dict = @{@"prop":@"value",@"prop2":@"value2",@"prop3":@"value3"};
TestRuntimeForward *model = [TestRuntimeForward new];
unsigned int count = 0;
// 获取属性列表
objc_property_t *propList = class_copyPropertyList(objc_getClass("TestRuntimeForward"), &count);
for(int i = 0 ; i < count;i++){
objc_property_t prop = propList[i];
const char *name = property_getName(prop);
NSString *nameStr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
// 取值
id value = [dict valueForKey:nameStr];
if(value){
// KVC设值
[model setValue:value forKey:nameStr];
}
}
free(propList);
NSLog(@"model:%@",model);
}
输出结果
2018-04-07 12:36:28.983197+0800 iOSLearnigDemo[13232:1087932] model:prop=value
持续整理中