2011-12-14 10 views
8

ho cercato di rispondere con Using a UITableView subclass with a UITableViewControllerISA Switching in questo modo:Objective-C: come modificare la classe di un oggetto in fase di runtime?

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

Ma, ottengo l'errore di compilazione: Instance variable 'isa' is protected.

C'è un modo per aggirare questo? E, se è così, è sicuro farlo?

Sto chiedendo perché @AmberStar's answer to that question sembra leggermente imperfetto. (Vedi il mio commento.)

risposta

22

Se la classe tableview fornisce QUALSIASI storage questo si interromperà. Non consiglierei il percorso che stai percorrendo. Ma il metodo corretto sarebbe utilizzare object_setClass(tableView, [MyTableView class]).

Assicurati che questo sia proprio quello che desideri.

Ecco un piccolo esempio di codice che mostra come questa sia un'idea orribile.

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

e l'uscita

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 

Come si può vedere la scrittura per storage->c ha scritto al di fuori della memoria allocata per l'istanza, e nel blocco ho assegnato per casuale. Se quello era un altro oggetto, hai appena distrutto il puntatore isa.

+0

Cool grazie. Ma è sicuro impostare 'self.tableView' in' UITableViewController' in una nuova istanza di una sottoclasse personalizzata di 'UITableView'? – ma11hew28

+1

dire @JoshuaWeinberg, sai se sarà male usare [Riferimenti associativi] (http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html) So che è sicuro usare le categorie (l'ho usato diverse volte) ma se la sottoclasse ha bisogno di un'altra variabile, penso che questo farà il trucco ... –

+1

Li uso sempre, molto utile. –

5

Il modo sicuro è creare una nuova istanza.

Scambiare isa non è sicuro: non si ha idea di quale sia il layout di memoria di una classe o quale sarà in futuro. Anche spostare il grafico dell'ereditarietà non è sicuro perché l'inizializzazione e la distruzione degli oggetti non sarebbero eseguite correttamente, lasciando il tuo oggetto in uno stato potenzialmente invalido (che potrebbe far cadere l'intero programma).

+0

Cool, grazie. Ma è sicuro impostare 'self.tableView' in' UITableViewController' in una nuova istanza di una sottoclasse personalizzata di 'UITableView'? – ma11hew28

+2

@Matt yes, potresti sottoclasse 'UITableView', ma dovresti distruggere ricostruire la tabella nel processo di modifica del tipo di tabella. in scenari più complessi, è possibile favorire un oggetto che può essere sostituito più facilmente (ad esempio un oggetto che ha disegnato l'aspetto o presentato i dati in modo diverso). – justin