起因
由于oc 的动态性,支持在运行时生成函数,于是想测试下如何生成setter,getter。下方代码是一个简单的例子。
我把 property 声明成了 dynamic ,这样编译器就不会再替我们生成 getter setter ivar了,然后我在运行时动态追加 getter setter ,并测试是否会导致 crash,如果没有出现 unrecognized selector 报错,就成功了。
当然我这只是一个demo,有很多硬编码,真实运用不能这样写,得追加一个NSObject的分类进行全局 hook
源码
//
// ViewController.m
// TestDynamicProperty
//
// Created by zhiyunyu on 2019/6/10.
// Copyright © 2019年 zhiyunyu. All rights reserved.
//
#import "ViewController.h"
#import "CodeZipper.h"
#import <objc/runtime.h>
@interface ViewController ()
@property(nonatomic, assign) NSUInteger num;
@end
@implementation ViewController {
CZ_DYNAMIC_PROPERTYS_FLAG_VAR
NSUInteger _num;
}
@dynamic num;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 测试setter
self.num = 100;
// 测试getter
NSLog(@"self.num = %@", @(self.num));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *ivarName = @"_num";
Ivar ivar = class_getInstanceVariable([self class], [ivarName UTF8String]);
ptrdiff_t offset = ivar_getOffset(ivar);
if (sel == @selector(setNum:)) {
IMP imp = NULL;
imp = imp_implementationWithBlock(^(id receive, unsigned long value){
char *ptr = ((char *)(__bridge void*)receive) + offset;
memcpy(ptr, &value, sizeof(value));
});
class_addMethod([self class], sel, imp, "v@:L");
return YES;
} else if (sel == @selector(num)) {
IMP imp = NULL;
imp = imp_implementationWithBlock(^(id receive){
char *ptr = ((char *)(__bridge void*)receive) + offset;
unsigned long value;
memcpy(&value, ptr, sizeof(value));
return value;
});
class_addMethod([self class], sel, imp, "L@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end