2012-06-06 4 views
7

Si consideri il seguente codice:Objective-C oggetto Typedef rendimenti strano @encode e si rompe KVC

#import <Foundation/Foundation.h> 
#import <objc/runtime.h> 

typedef NSString* MyStringRef; 
typedef NSString MyString; 

@interface ClassA : NSObject 
@property (nonatomic, copy) MyStringRef stringA; 
@property (nonatomic, copy) MyString *stringB; 
@end 

@implementation ClassA 
@synthesize stringA = _stringA; 
@synthesize stringB = _stringB; 
@end 

int main() { 
    unsigned int count = 0; 
    Ivar *ivars = class_copyIvarList([ClassA class], &count); 
    for (unsigned int i = 0; i < count; i++) { 
     Ivar thisIvar = ivars[i]; 
     NSLog(@"thisIvar = %s, %s", ivar_getName(thisIvar), ivar_getTypeEncoding(thisIvar)); 
    } 

    ClassA *a = [[ClassA alloc] init]; 
    NSLog(@"Out: %@", [a valueForKey:@"stringA"]); 
    NSLog(@"Out: %@", [a valueForKey:@"stringB"]); 
} 

Questa è l'uscita:

$ clang --version 
Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn) 
Target: x86_64-apple-darwin11.4.0 
Thread model: posix 

$ clang -o typedef -fobjc-arc -framework Foundation typedef.m && ./typedef 
2012-06-06 20:14:15.881 typedef[37282:707] thisIvar = _stringA, @"NSString" 
2012-06-06 20:14:15.884 typedef[37282:707] thisIvar = _stringB, ^{NSString=#} 
2012-06-06 20:14:15.885 typedef[37282:707] Out: (null) 
2012-06-06 20:14:15.888 typedef[37282:707] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<ClassA 0x7fabe0501480> valueForUndefinedKey:]: this class is not key value coding-compliant for the key stringB.' 
*** First throw call stack: 
(
    0 CoreFoundation      0x00007fff835fef56 __exceptionPreprocess + 198 
    1 libobjc.A.dylib      0x00007fff878e5d5e objc_exception_throw + 43 
    2 CoreFoundation      0x00007fff836891b9 -[NSException raise] + 9 
    3 Foundation       0x00007fff83e77703 -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 240 
    4 Foundation       0x00007fff83dae38e _NSGetUsingKeyValueGetter + 108 
    5 Foundation       0x00007fff83dae315 -[NSObject(NSKeyValueCoding) valueForKey:] + 392 
    6 typedef        0x000000010e84bc6d main + 317 
    7 typedef        0x000000010e84b9c4 start + 52 
) 

La domanda che ho qui è che cosa si tratta Obiettivo -C che causa il typedef NSString MyString per creare in modo efficace una struttura che contiene una variabile di tipo Class e quindi utilizza quello in cui utilizzo MyString. per esempio. struct si presenta così (dopo aver consultato this):

struct NSString { 
    Class a; 
}; 

E 'sorta di senso, ma fa sì che il valueForKey: a fallire, presumibilmente perché si tratta di una struttura, in modo che esso non può restituire che allo stesso modo come oggetti . O più precisamente, rientra nella parte "throw exception" dell'ordine di ricerca described in the docs.

Vorrei solo capire che cos'è il linguaggio che causa questo e perché non può trattare i miei 2 typedef nello stesso modo.

+0

Sebbene si sintetizzino le proprietà, non sembra che un vero ivar le supporti. –

+4

Le versioni recenti del compilatore creano automaticamente ivars per le proprietà sintetizzate. –

+0

In generale, le stringhe @encode non sono molto utili. Ad esempio, diventano rapidamente eccessivamente complessi quando si tratta di tipi C++. – bbum

risposta

3

ho avuto una risposta a questa al WWDC 2012. Si scopre che questo comportamento è atteso come è per la compatibilità binaria con GCC.

Mi sembra strano che introdurranno effettivamente quello che considererei un bug compatibile con i vecchi GCC. Spero che questo genere di cose finirà per essere eliminato a favore di un compilatore corretto.

+0

Penso di vederlo con clang 4.2, FWIW. Coerentemente con l'intenzione di comportamento scorretto. – nmr

+0

Infatti. Questo è ciò che ha detto Apple. – mattjgalloway

1

Posso riprodurlo, ma solo con gcc. Con clang, ottengo:

$ clang --version 
Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn) 
Target: x86_64-apple-darwin10 
Thread model: posix 
$ ./a.out 
2012-06-06 15:16:37.947 a.out[61063:903] thisIvar = _stringA, @"NSString" 
2012-06-06 15:16:37.949 a.out[61063:903] thisIvar = _stringB, @"NSString" 
2012-06-06 15:16:37.949 a.out[61063:903] Out: (null) 
2012-06-06 15:16:37.950 a.out[61063:903] Out: (null) 

Così, sembra il clangore sviluppatori sono d'accordo con voi su come dovrebbe funzionare.

(ho anche provato di Apple clang versione 3.0. Lo stesso buon risultato.)

+0

Interessante, visto che sto usando anche clang. Hai abilitato ARC in questo? – mattjgalloway

+0

Aggiunta la mia versione clang e la riga di comando che ho usato per costruire la mia domanda. – mattjgalloway

+0

Non si utilizza ARC. 'clang -std = gnu99 foo.m -framework Foundation' Avevo bisogno di gnu99 a causa della dichiarazione di' i' all'interno del ciclo 'for'. –