Objective-C: How to change the class of an object at runtime?
Asked Answered
I

2

10

I tried to answer Using a UITableView subclass with a UITableViewController with ISA Switching like so:

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

But, I get the compile error: Instance variable 'isa' is protected.

Is there a way to get around this? And, if so, is it safe to do?

I'm asking because @AmberStar's answer to that question seems slightly flawed. (See my comment.)

Inhibition answered 14/12, 2011 at 22:42 Comment(0)
B
27

If your tableview class provides ANY storage this will break. I would not recommend the path you're going down. But the correct method would be to use object_setClass(tableView, [MyTableView class]).

Please make sure this is really what you want.

Here is a small code-sample showing how this is a horrible idea.

#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);
}

and the output

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

As you can see the write to storage->c wrote outside the memory allocated for the instance, and into the block I allocated for random. If that was another object, you just destroyed its isa pointer.

Blockus answered 14/12, 2011 at 22:45 Comment(4)
Cool thanks. But, is it safe to set self.tableView in UITableViewController to a new instance of a custom subclass of UITableView?Inhibition
say @JoshuaWeinberg, do you know if it'll be bad to use Associative References I know it's safe to use with categories (used it myself several times) but if the subclass need another variable, I think this will do the trick...Trimetric
I use them all the time, quite handy.Blockus
Wow! that was a fast reply! you get a point just for that! :) - now i understand how you got your Fanatic badge... :)Trimetric
S
5

The safe way is to create a new instance.

Swapping isa is not safe - you have no idea what the memory layout of a class is or what it will be in the future. Even moving up the inheritance graph is really not safe because objects' initialization and destruction would not be performed correctly - leaving your object in a potentially invalid state (which could bring your whole program down).

Samons answered 14/12, 2011 at 22:47 Comment(2)
Cool, thanks. But, is it safe to set self.tableView in UITableViewController to a new instance of a custom subclass of UITableView?Inhibition
@Matt yes, you may subclass UITableView, but you'd have to destroy rebuild the table in the process of changing the type of the table. in more complex scenarios, you may favor an object which can be swapped out more easily (e.g. an object which styled the appearance or presented the data differently).Samons

© 2022 - 2024 — McMap. All rights reserved.