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.)

Community
  • 1
  • 1
ma11hew28
  • 121,420
  • 116
  • 450
  • 651

2 Answers2

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.

Joshua Weinberg
  • 28,598
  • 2
  • 97
  • 90
  • Cool thanks. But, is it safe to set `self.tableView` in `UITableViewController` to a new instance of a custom subclass of `UITableView`? – ma11hew28 Dec 14 '11 at 22:57
  • 2
    say @JoshuaWeinberg, do you know if it'll be bad to use [Associative References](http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html) 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... – Alex Zak Jan 08 '12 at 21:06
  • 1
    I use them all the time, quite handy. – Joshua Weinberg Jan 08 '12 at 21:07
  • Wow! that was a fast reply! you get a point just for that! :) - now i understand how you got your [Fanatic badge](http://stackoverflow.com/badges/83/fanatic)... :) – Alex Zak Jan 08 '12 at 21:08
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).

justin
  • 104,054
  • 14
  • 179
  • 226
  • Cool, thanks. But, is it safe to set `self.tableView` in `UITableViewController` to a new instance of a custom subclass of `UITableView`? – ma11hew28 Dec 14 '11 at 22:56
  • 3
    @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). – justin Dec 14 '11 at 23:02