Цель-C: Как изменить класс объекта во время выполнения? - PullRequest
9 голосов
/ 15 декабря 2011

Я пытался ответить Использование подкласса UITableView с UITableViewController с Переключением ISA примерно так:

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

Но я получаю ошибку компиляции: Instance variable 'isa' is protected.

Есть ли способ обойти это? И, если это так, это безопасно сделать?

Я спрашиваю, потому что @ AmberStar ответ на этот вопрос кажется слегка ошибочным. (Смотрите мой комментарий.)

Ответы [ 2 ]

24 голосов
/ 15 декабря 2011

Если ваш класс табличного представления предоставляет ЛЮБУЮ память, это сломается. Я не рекомендовал бы путь, по которому вы идете. Но правильный метод будет использовать 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.

5 голосов
/ 15 декабря 2011

Безопасный способ - создать новый экземпляр.

Обмен isa небезопасен - вы понятия не имеете, что такое макет памяти класса или каким он будет в будущем. Даже перемещение по графу наследования на самом деле небезопасно, потому что инициализация и уничтожение объектов не будут выполняться правильно - оставляя ваш объект в потенциально недопустимом состоянии (что может привести к остановке всей вашей программы).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...