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

时间:2011-12-14 22:42:49

标签: objective-c runtime uitableview protected

我尝试用Using a UITableView subclass with a UITableViewController回答ISA Switching,如下所示:

self.tableView->isa = [MyTableView class];

但是,我收到编译错误:Instance variable 'isa' is protected.

有没有办法解决这个问题?如果是这样,这样做是否安全?

我问,因为@AmberStar's answer to that question似乎有点瑕疵。 (见我的评论。)

2 个答案:

答案 0 :(得分:24)

如果你的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指针。

答案 1 :(得分:5)

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

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