Runtime的简单使用
Runtime一直都是iOS开发者讨论的比较多的问题,如果不知道一点点Runtime的使用的话,感觉都不好意思说自己是一个用OC来开发Apple应用的开发人员。虽然Runtime使用的这类博客在网上可以说是烂大街了,但是我还是想记录一下自己对Runtime的学习情况。
在OC中使用Runtime需要导入#import <objc/objc-runtime.h>,而在Swift中则不需要,系统已经为我们导入了
一、消息发送
objc_msgSend(<#id self#>, <#SEL op, ...#>)
要使用这个方法,在Xcode7以上需要修改下设置信息:targets --> Build settings --> 搜索 msg 修改为 NO 即可索引出该方法
- (void)viewDidLoad {
[super viewDidLoad];
objc_msgSend(self, @selector(msgSend:param2:), @"param1", @"param2");
}
- (void)msgSend:(NSString *)param1 param2:(NSString *)param2
{
NSLog(@"param1 = %@, param2 = %@",param1, param2);
}
但是在swift中已经没有这个方法了
二、获取属性名、属性的值
获取成员属性名和值
- (NSDictionary *)getPropertyListAndValue
{
NSMutableDictionary * dic = [NSMutableDictionary dictionary];
unsigned int count = 0;
Ivar * ivar = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i ++) {
// 获取属性名字
const char * name = ivar_getName(ivar[i]);
NSString * propertyName = [NSString stringWithUTF8String:name];
// 获取属性值
NSString * value = [self valueForKey:propertyName];
[dic setObject:value forKey:propertyName];
}
free(ivar);
return dic;
}
打印结果如下
2016-05-31 17:00:21.781 Runtime[4323:209497] array = {
"_IdCard" = 123;
"_age" = 12234;
"_name" = "\U5f20\U4e09";
"_sex" = 12;
}
获取属性名和值
- (NSDictionary *)getPropertyListAndValue
{
NSMutableDictionary * dic = [NSMutableDictionary dictionary];
unsigned int count = 0;
objc_property_t * property = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i ++) {
// 获取属性名字
const char * name = property_getName(property[i]);
NSString * propertyName = [NSString stringWithUTF8String:name];
// 获取属性值
NSString * value = [self valueForKey:propertyName];
[dic setObject:value forKey:propertyName];
}
free(property);
return dic;
}
2016-05-31 17:04:51.762 Runtime[4357:213684] array = {
IdCard = 123;
age = 12234;
block = blnl;
name = "\U5f20\U4e09";
sex = 12;
}
Swift中获取属性名和属性值
func getAllPropetyAndValue() -> [String: AnyObject] {
var count: UInt32 = 0
let ivar = class_copyIvarList(Persion.self, &count)
var dic: [String: AnyObject] = Dictionary()
for index in 0..<Int(count) {
// 获取属性名
let propety = ivar_getName(ivar[index])
let nameString = String.fromCString(propety)
// 获取值
let value = self.valueForKey(nameString!)
if let value = value {
dic[nameString!] = value
}
}
return dic
}
说明:class_copyPropertyList和property_getName都不能获取对应类父类的属性名,在OC中这两个方法获取的属性名存在一个"_"的差异,在Swift中则不存在
三、获取方法名
- (NSArray *)getMothodName
{
NSMutableArray * mothodArray = [NSMutableArray array];
unsigned int count = 0;
Method * method = class_copyMethodList([self class], &count);
for (int i = 0; i < count; i ++) {
// 获取方法名字
SEL methodName = method_getName(method[i]);
const char * name = sel_getName(methodName);
[mothodArray addObject:[NSString stringWithUTF8String:name]];
}
return mothodArray;
}
四、给OC的类目添加属性、把按钮的Action绑定Block
给OC的类目添加属性:
- (void)setParam:(NSString *)param
{
objc_setAssociatedObject(self, @selector(param), param, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)param
{
return objc_getAssociatedObject(self, _cmd);
}
Swift中添加属性
private var addressKey: UInt8 = 0
extension Persion {
var address: String? {
get {
return objc_getAssociatedObject(self, &addressKey) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&addressKey,
newValue as NSString?,objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC
)
}
}
}
}
把按钮的Selector绑定Block:
typedef void(^ButtonBlock)();
@interface UIButton (block)
- (void)addTarget:(id)target events:(UIControlEvents)event block:(ButtonBlock)block;
@end
const void * buttonKey = @"buttonKey";
@implementation UIButton (block)
- (void)addTarget:(id)target events:(UIControlEvents)event block:(ButtonBlock)block
{
objc_setAssociatedObject(self, buttonKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self addTarget:target action:@selector(processButton) forControlEvents:event];
}
- (void)processButton
{
objc_getAssociatedObject(self, buttonKey);
}
@end
调用:
- (void)viewDidLoad
{
[self.button addTarget:self events:UIControlEventTouchDown block:^{
NSLog(@"点击事件");
}];
}
在swift中绑定:
private var ActionBlockKey: UInt8 = 0
typealias ButtonBlock = ((sender: UIButton)-> Void)?
class ActionBlockWrapper : NSObject {
var block : ButtonBlock
init(block: ButtonBlock) {
self.block = block
}
}
extension UIButton {
func addTarget(controlEvents: UIControlEvents, blick: ButtonBlock) {
objc_setAssociatedObject(self, &ActionBlockKey, ActionBlockWrapper.init(block: blick), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
addTarget(self, action: #selector(UIButton.processButton(_:)), forControlEvents: controlEvents)
}
func processButton(sender: UIButton) {
let wrapper = objc_getAssociatedObject(self, &ActionBlockKey) as! ActionBlockWrapper
wrapper.block!(sender: sender)
}
}
四、方法交换
OC中的方法交换:
Method p1 = class_getInstanceMethod([Persion class], @selector(p1));
Method p2 = class_getInstanceMethod([Persion class], @selector(p2));
method_exchangeImplementations(p1, p2);
[p p1];
swift中的方法交换:
let p1: Method = class_getInstanceMethod(ViewController.self, #selector(ViewController.mothodFirst))
let p2: Method = class_getInstanceMethod(ViewController.self, #selector(ViewController.mothodSecond))
method_exchangeImplementations(p1, p2)
mothodFirst()
五、解档、归档:当一个对象有很多属性需要进行解/归档的时候,不用一个个属性的去写解/归档操作
这里我定义了一个Persion类来说明
@interface Persion : NSObject
@property (copy, nonatomic) NSString * name;
@property (assign, nonatomic) int age;
@property (copy, nonatomic) NSNumber * height;
@end
实现:
- (void)encodeWithCoder:(NSCoder *)coder
{
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([Persion class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char * name = ivar_getName(ivar);
NSString * strName = [NSString stringWithUTF8String:name];
// kvc 取值
id value = [self valueForKey:strName];
[coder encodeObject:value forKey:strName];
}
free(ivars);
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
unsigned int count = 0;
// 获取类中所有成员变量名
Ivar * ivars = class_copyIvarList([Persion class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char * name = ivar_getName(ivar);
NSString * strName = [NSString stringWithUTF8String:name];
// 进行减档
id value = [coder decodeObjectForKey:strName];
// 利用kvc对属性赋值
[self setValue:value forKey:strName];
}
free(ivars);
}
return self;
}
这样是不是很方便了呢