首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Objc运行时将ivar设置为值

Objc运行时将ivar设置为值
EN

Stack Overflow用户
提问于 2018-06-25 23:58:23
回答 1查看 508关注 0票数 0

我正在尝试使用从JSON加载的数据动态创建一个类。我已经成功地创建了这个类,但仍然纠结于如何用从JSON加载的值添加几个常量。

例如,下面的JSON:

代码语言:javascript
复制
"Josh": {
    "age": 10
}

我想要一个像这样的类

代码语言:javascript
复制
class Josh {
    int age = 10;
}

但是,我只能做到

代码语言:javascript
复制
class Josh {
    int age;
}

使用以下代码:

代码语言:javascript
复制
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中?谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-06-26 04:33:06

你的所作所为几乎可以肯定是疯狂的,你不应该把它搞乱。

也就是说,您编写的代码甚至不能编译。

所以让我们来解决这个问题。

您不能声明Josh j,因为Josh是一个变量,而不是一个类型。相反,您可以使用id,它的意思是“任何Objective-C对象”。

代码语言:javascript
复制
id j = [[Josh alloc] init];

你也不能说[j age],除非你在某个地方声明了一个age方法,所以把这个放在某个地方:

代码语言:javascript
复制
@protocol Dummy <NSObject>
- (int)age;
@end

您也不能说NSLog([j age]),因为[j age]返回的是int,而不是NSString *。因此,请这样做:

代码语言:javascript
复制
NSLog(@"%d", [j age]);

现在,您会发现出现了一个“无法识别的选择器”错误,因为您没有向Josh类添加age方法。为此,您首先必须导入Objective-C运行时:

代码语言:javascript
复制
#import <objc/runtime.h>

然后,您可以像这样添加一个age方法:

代码语言:javascript
复制
        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@:");

下面是完整的、可编译的、不会崩溃的程序:

代码语言:javascript
复制
#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”:

代码语言:javascript
复制
2018-06-25 15:07:53.179809-0500 madness[35050:5762222] 0
Program ended with exit code: 0

如果希望将age初始化为10,则需要在Josh类中重写init。如果您正常编写init方法,它将如下所示:

代码语言:javascript
复制
- (instancetype)init {
    if (self = [super init]) {
        _age = 10;
    }
    return self;
}

但是我们只能在编译器将其识别为方法体的内容中发送给super,并且块(就像我们将传递给imp_implementationWithBlock)不能算作方法体。因此,我们必须通过调用objc_msgSendSuper来艰难地完成[super init]部分。为此,我们必须导入另一个头文件:

代码语言:javascript
复制
#import <objc/message.h>

然后我们可以编写实现init的代码块

代码语言:javascript
复制
        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, "@@:");

下面是完整的程序:

代码语言:javascript
复制
#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;
}

下面是输出:

代码语言:javascript
复制
2018-06-25 15:31:41.837748-0500 madness[35369:5786038] 10
Program ended with exit code: 0
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51027562

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档