Objective-C:如何在运行时更改对象的类?

10
2个回答

27

如果你的tableview类提供了任何存储,这将会破坏它。我不建议你继续这条路。但正确的方法是使用object_setClass(tableView, [MyTableView class])

请确保这确实是你想要的。

以下是一个小的代码示例,展示了这是个可怕的想法。

#import <objc/runtime.h>

@interface BaseClass : NSObject
{
    int a;
    int b;
}
@end

@implementation BaseClass

@end

@interface PlainSubclass : BaseClass
@end

@implementation PlainSubclass
@end

@interface StorageSubclass : BaseClass
{
@public
    int c;
}
@end

@implementation StorageSubclass
@end



int main(int argc, char *argv[])
{
    BaseClass *base = [[BaseClass alloc] init];
    int * random = (int*)malloc(sizeof(int));
    NSLog(@"%@", base);

    object_setClass(base, [PlainSubclass class]);
    NSLog(@"%@", base);

    object_setClass(base, [StorageSubclass class]);
    NSLog(@"%@", base);
    StorageSubclass *storage = (id)base;
    storage->c = 0xDEADBEEF;
    NSLog(@"%X == %X", storage->c, *random);
}

并输出结果

2011-12-14 16:52:54.886 Test[55081:707] <BaseClass: 0x100114140>
2011-12-14 16:52:54.889 Test[55081:707] <PlainSubclass: 0x100114140>
2011-12-14 16:52:54.890 Test[55081:707] <StorageSubclass: 0x100114140>
2011-12-14 16:52:54.890 Test[55081:707] DEADBEEF == DEADBEEF

你可以看到对 storage->c 的写入超出了为该实例分配的内存范围,并进入了我为随机数分配的块中。如果那是另一个对象,你刚刚摧毁了它的isa指针。


谢谢。但是,在UITableViewController中将self.tableView设置为自定义的UITableView子类的新实例是否安全? - ma11hew28
2
问@JoshuaWeinberg,你知道使用关联引用是否会有问题吗?我知道在类别中使用它是安全的(我自己已经使用了几次),但如果子类需要另一个变量,我认为这将解决问题... - Alex Zak
1
我一直在使用它们,非常方便。 - Joshua Weinberg
哇!回复好快啊!你因此得到了一个点数! :) - 现在我明白你是如何获得你的狂热者徽章了... :) - Alex Zak

5

安全的方法是创建一个新实例。

交换 isa 不安全 - 你不知道类的内存布局或将来会是什么样子。即使向上移动继承图也不安全,因为对象的初始化和销毁不会正确执行 - 可能会使对象处于无效状态(可能导致整个程序崩溃)。


好的,谢谢。但是,在UITableViewController中将self.tableView设置为自定义的UITableView子类的新实例是安全的吗? - ma11hew28
3
是的,你可以子类化 UITableView,但在更改表格类型的过程中,你必须销毁并重建表格。在更复杂的情况下,你可能更喜欢一个可以更轻松地替换的对象(例如,一个可以不同地呈现数据或样式的对象)。 - justin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接