我正在尝试使用从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中?谢谢
发布于 2018-06-26 04:33:06
你的所作所为几乎可以肯定是疯狂的,你不应该把它搞乱。
也就是说,您编写的代码甚至不能编译。
所以让我们来解决这个问题。
您不能声明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]);
现在,您会发现出现了一个“无法识别的选择器”错误,因为您没有向Josh
类添加age
方法。为此,您首先必须导入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,则需要在Josh
类中重写init
。如果您正常编写init
方法,它将如下所示:
- (instancetype)init {
if (self = [super init]) {
_age = 10;
}
return self;
}
但是我们只能在编译器将其识别为方法体的内容中发送给super
,并且块(就像我们将传递给imp_implementationWithBlock
)不能算作方法体。因此,我们必须通过调用objc_msgSendSuper
来艰难地完成[super init]
部分。为此,我们必须导入另一个头文件:
#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
https://stackoverflow.com/questions/51027562
复制相似问题