起因
由于oc 的動態(tài)性笋除,支持在運行時生成函數(shù)听隐,于是想測試下如何生成setter,getter织咧。下方代碼是一個簡單的例子。
我把 property 聲明成了 dynamic ,這樣編譯器就不會再替我們生成 getter setter ivar了懦底,然后我在運行時動態(tài)追加 getter setter 唇牧,并測試是否會導(dǎo)致 crash,如果沒有出現(xiàn) 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