我正在尝试使用从JSON加载的数据动态创建一个类。我已经成功创建了该类,但是仍然停留在如何使用从JSON加载的值添加几个常量的方法上。
例如,下面的JSON:
"Josh": {
"age": 10
}
我想要一个类似的课程
class Josh {
int age = 10;
}
但是,我只能实现
class Josh {
int age;
}
具有以下代码:
Class Josh = objc_allocateClassPair([NSObject class], "Josh", 0);
class_addIvar(Josh, "age", sizeof(int), rint(log2(sizeof(int))), @encode(int));
objc_registerClassPair(Josh);
// Below is the desired result
Josh j = [[Josh alloc] init];
NSLog([j age]); // prints 10
所以有人可以回答我,如何将原始值10添加到我添加的这个Ivar中?谢谢
答案 0 :(得分:2)
几乎可以肯定,您正在做的事情是疯狂的,您不应该对此感到困惑。
也就是说,您编写的代码甚至无法编译。
所以我们来解决它。
您不能声明Josh j
,因为Josh
是变量,而不是类型。相反,您可以使用id
,表示“任何Objective-C对象”。
id j = [[Josh alloc] init];
除非您在某个地方声明了一个[j age]
方法,否则您也不能说age
,所以请把它放在某个地方:
@protocol Dummy <NSObject>
- (int)age;
@end
您也不能说NSLog([j age])
,因为[j age]
返回int
,而不是NSString *
。而是这样做:
NSLog(@"%d", [j age]);
现在,您会发现遇到“无法识别的选择器”错误,因为没有向age
类添加Josh
方法。为此,您首先必须导入Objective-C运行时:
#import <objc/runtime.h>
然后,您可以像这样添加age
方法:
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, @selector(age), ageImp, "v@:");
这是完整,可编译且不会崩溃的程序:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@protocol Dummy <NSObject>
- (int)age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class Josh = objc_allocateClassPair([NSObject class], "Josh", 0);
class_addIvar(Josh, "age", sizeof(int), rint(log2(sizeof(int))), @encode(int));
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, @selector(age), ageImp, "v@:");
objc_registerClassPair(Josh);
// Below is the desired result
id j = [[Josh alloc] init];
NSLog(@"%d", [j age]); // prints 10
}
return 0;
}
但是它仍然只打印“ 0”:
2018-06-25 15:07:53.179809-0500 madness[35050:5762222] 0
Program ended with exit code: 0
如果要将age
初始化为10,则需要在init
类中覆盖Josh
。如果您正常地编写init
方法,它将看起来像这样:
- (instancetype)init {
if (self = [super init]) {
_age = 10;
}
return self;
}
但是我们只能发送到super
的编译器可识别为方法主体的内容中,而一个块(例如我们将传递给imp_implementationWithBlock
的)不算作方法主体。因此,我们必须通过调用[super init]
来艰难地完成objc_msgSendSuper
部分。为此,我们必须导入另一个标头:
#import <objc/message.h>
然后我们可以编写实现init
的块:
IMP initImp = imp_implementationWithBlock(^id(id self) {
struct objc_super superInfo = {
.receiver = self,
.super_class = NSObject.self
};
id (*msgSendSuper)(struct objc_super *, SEL) = (id (*)(struct objc_super *, SEL))objc_msgSendSuper;
self = msgSendSuper(&superInfo, @selector(init));
if (self) {
void *base = (__bridge void *)self;
*(int *)((unsigned char *)base + ageIvarOffset) = 10;
}
return self;
});
class_addMethod(Josh, @selector(init), initImp, "@@:");
这是完整的程序:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
@protocol Dummy <NSObject>
- (int)age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class Josh = objc_allocateClassPair([NSObject class], "Josh", 0);
class_addIvar(Josh, "age", sizeof(int), rint(log2(sizeof(int))), @encode(int));
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, @selector(age), ageImp, "v@:");
IMP initImp = imp_implementationWithBlock(^id(id self) {
struct objc_super superInfo = {
.receiver = self,
.super_class = NSObject.self
};
id (*msgSendSuper)(struct objc_super *, SEL) = (id (*)(struct objc_super *, SEL))objc_msgSendSuper;
self = msgSendSuper(&superInfo, @selector(init));
if (self) {
void *base = (__bridge void *)self;
*(int *)((unsigned char *)base + ageIvarOffset) = 10;
}
return self;
});
class_addMethod(Josh, @selector(init), initImp, "@@:");
objc_registerClassPair(Josh);
// Below is the desired result
id j = [[Josh alloc] init];
NSLog(@"%d", [j age]); // prints 10
}
return 0;
}
这是输出:
2018-06-25 15:31:41.837748-0500 madness[35369:5786038] 10
Program ended with exit code: 0