iOS Runtime的那些小事(持续更新中)
runtime是动态语言的一个特征,初学者在编写程序的时候对这种运行时机制也是特别模糊。网上对这个东西的教程也是一大篇,所以具体的原理我在这里就不介绍了,我在这边学了实际开发中用到的几个小案例拿出来给童鞋找点感觉。
字典转模型
字典转模型是我们在实际开发中经常遇到的问题,我们可以自己一个一个把接口获取下来的json数据赋值给model里的属性,也可以采用网上流行的3方库来进行装换。
其实 实现这一个功能很简单(大牛的3方库,优化以及细节都考虑的很全面)。
我的基本思路是这样:我首先将字典的keys取出来,之后将model里面的成员变量获取到。对比key和成员变量的名称,如果相同就将value 赋值给成员变量。
实际代码:
声明一个NSObject的类目:
#import "Extension.h"
#import <objc/runtime.h>
@implementation NSObject(Extension)
+ (id)dicToModel:(NSDictionary *)dict {
//创建调用对象
id per = [self new];
//要获取model类的属性
unsigned int outCount;
Ivar *vars = class_copyIvarList([self class], &outCount);
//遍历传过来的数组
for (id name1 in dict.allKeys) {
NSString *name = [NSString stringWithFormat:@"_%@",name1];
for (int i = 0; i < outCount; i++) {
const char *varName = ivar_getName(vars[i]);
NSString *runName = [NSString stringWithUTF8String:varName];
if ([runName isEqualToString:name]) {
//字典里的属性和model里面的属性 吻合则转换
//用这个方法的话,基本上属性 得特殊处理
// object_setIvar(per, vars[i], [dict objectForKey:name1]);
[per setValue:[dict objectForKey:name1] forKey:runName];
}
}
}
return per;
}
@end
声明一个Person类:
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger *age;
@end
实际使用:
//字典转模型
NSDictionary *dict = @{@"name":@"李雷",@"age":@18};
Person *per = [Person dicToModel:dict];
NSLog(@"%@=======%ld",per.name,per.age);
打印结果:
2016-03-07 16:10:07.068 DicToModel[3639:134187] 李雷=======18
使AlertView 像UIAlertViewController 一样初始化只有直接处理点击事件
我们在使用alertView的时候 如果想处理点击方法 必须要实现代理方法,在代理方法里面处理点击事件。如果在一个C上有多个alertView公用一个代理方法。这时候,对我们使用起来和阅读都有一些麻烦。
而UIAlertViewController 可以初始化之后 设置点击方法。我感觉这样对代码的阅读直观一些,就自己写了一个类目来处理这些东西。
//使用
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"22" message:@"hah" delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles:@"sure", nil];
[alert handleSureAction:^(NSInteger index) {
if (index == 1) {
NSLog(@"确定被点击");
}else {
NSLog(@"取消被点击");
}
}];
[alert show];
为alertView 新加的类目
//
// UIAlertView+ActionBlock.h
// classCluster
//
// Created by DQ on 16/4/8.
// Copyright © 2016年 DQ. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef void (^handleAction)(NSInteger index) ;
@interface UIAlertView (ActionBlock)<UIAlertViewDelegate>
- (void) handleSureAction:(handleAction) handle ;
@end
//
// UIAlertView+ActionBlock.m
// classCluster
//
// Created by DQ on 16/4/8.
// Copyright © 2016年 DQ. All rights reserved.
//
#import "UIAlertView+ActionBlock.h"
#import <objc/runtime.h>
static void *KAlertViewActionKey = "alertViewKey";
@implementation UIAlertView (ActionBlock)
- (void)handleSureAction:(handleAction)handle {
self.delegate = self;
objc_setAssociatedObject(self, KAlertViewActionKey, handle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
handleAction handle = objc_getAssociatedObject(self, KAlertViewActionKey);
handle(buttonIndex);
}
@end
引入这个类目之后,就可以愉快的处理点击事件了。
======更新
method swizzling 全局修改字体
//
// UILabel+ChangeFont.m
// methodSwizzling-changeFont
//
// Created by DQ on 16/4/28.
// Copyright © 2016年 DQ. All rights reserved.
//
#import "UILabel+ChangeFont.h"
#import <objc/runtime.h>
@implementation UILabel (ChangeFont)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL oriSEL = @selector(willMoveToSuperview:);
SEL swizzlingSEL = @selector(myMovetoSuperView:);
Method oriMethod = class_getInstanceMethod([self class], oriSEL);
Method swizzlingMethod = class_getInstanceMethod([self class], swizzlingSEL);
BOOL isadd = class_addMethod([self class], oriSEL,method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod));
if (isadd) {
class_replaceMethod([self class], swizzlingSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
}else {
method_exchangeImplementations(oriMethod, swizzlingMethod);
}
});
}
- (void)myMovetoSuperView:(UIView *)superView {
[self myMovetoSuperView:superView];
self.font = [UIFont fontWithName:@"Aladdin" size:30];
}
这边有一个疑问就是,为什么不是直接交换方法,而是判断有没有添加?
百度南峰子博客下面评论有答案
1.didAddMethod一直为NO,是因为你当前类重载了父类的viewWillAppear方法。如果当前类没有重写父类的方法class_addMethod会把新的IMP替换掉父类的IMP,也就是重写了父类方法,并且返回yes;如果当前类重写了父类方法,就不在替换IMP,直接返回NO。
这是oc原话:
- @note class_addMethod will add an override of a superclass's implementation,
- but will not replace an existing implementation in this class.
- To change an existing implementation, use method_setImplementation.
如果说的有什么不对,还请提出,大家共同学习。最后感谢博主的无私分享。好人一生平安!
2.添加是为了重写父类的 viewWillAppear 方法,如果你自己已经手动重写了 viewWillAppear 方法,class_addMethod() 就会添加失败,返回 NO。
如果返回 NO ,说明已经重写,就直接交换。
如果返回 YES, 说明添加成功,也就是你之前没有重写 viewWillAppear 方法。因为在添加方法里面是将 originalSelector 与 swizzledMethod 的 IMP 绑定在一起了,所以接下来只用将 swizzledSelector 与 originalMethod 的 IMP 绑定就可以了。也就实现了交换。