Las properties del protocolo Obj-c no se implementan en la class conforme

Problema


Me he encontrado con un problema interesante y no he podido encontrar ninguna documentation sobre él … Algunas veces, las properties declaradas en un protocol no se implementan en una class particular de acuerdo con ese protocol y se produce una exception de time de ejecución. ¿Se optimizan las definiciones de dynamic property bajo alguna circunstancia extraña? ¿No se pueden usar los protocols con las properties que se hicieron para ser declaradas dynamic ? Cualquier idea de esto sería muy apreciada.

A continuación se incluyen algunos más detalles.

Dado un protocol :

 @protocol MyProtocol <NSObject> @property (nonatomic, strong) id someProperty; @end 

y una class que implementa el protocol así:

 @interface MyClass <MyProtocol> @end @implementation MyClass @dynamic someProperty; @end 

Me he dado count de que a veces no puedo get información llamando

 class_getProperty(myClass, propertyName); 

para las properties en el protocol . Esto solo sucede con algunas classs y parece ser esporádico.

Estoy ejecutando el último Xcode 4 y enlazando con el iOS 6 SDK. Tengo el Xcode 5 pre-lanzamiento instalado en la misma máquina, aunque no es el pnetworkingeterminado (a través de xcode-select).

Ejemplo


Si ejecuta este código:

 @protocol MyProtocol <NSObject> @property (nonatomic, strong) id someData; @end @interface MyObject : NSObject <MyProtocol> @end @implementation MyObject @dynamic someData; @end 

y luego corres

 const char *name = [@"someData" UTF8String]; objc_property_t property = class_getProperty([MyObject class], name); const char *attributes = property_getAttributes(property); 

Usted obtendrá metadatos en la property AUNQUE la property no existe. En otras palabras , no necesita sintetizar la propiedad para get sus attributes . El time de ejecución todavía lo sabe. Pruébelo usted mismo. El problema es que a veces esto no sucede. Quiero saber las condiciones que hacen que el time de ejecución no tenga conocimiento de los attributes de la property .

Arreglo temporal


Mi solución temporal es simplemente copyr todas las definiciones de property en el protocol y pegarlas en el file .h:

 @interface MyClass <MyProtocol> @property (nonatomic, strong) id someProperty; @end @implementation MyClass @dynamic someProperty; @end 

Esto funciona bien, aunque está lejos de ser ideal. Sin embargo, sugiere que mi código funciona correctamente y el problema está en otra parte.

Estaría encantado de proporcionar más detalles o antecedentes si es necesario.

Parece que hay una confusión:

  1. Declarar una propiedad es suficiente para que la propiedad exista en time de ejecución. No hay necesidad de implementación. Así es como funciona el objective-c. Los methods no tienen que existir en time de compilation, puede agregarlos dinámicamente (por ejemplo, qué hace Core Data).

  2. @dynamic no hace absolutamente nada durante el time de ejecución. En el time de compilation es un marcador de position que dice "no me dé advertencias del comstackdor que el getter / setter no está definido aquí". En el último LLVM también dice "no sintetices automáticamente".

Mis sugerencias:

  1. Si está agregando el protocolo a través de una categoría, asegúrese de que la categoría está cargada. Este parece ser el problema más habitual con la reflexión de time de ejecución.

  2. Para depurar, también intente usar class_conformsToProtocol . Sería extraño tener una class que cumpla con un protocolo sin que tenga properties declaradas por el protocolo.

Los protocolos definen methods, methods opcionales y methods requeridos.

las properties son methods abstractos, si el protocolo define una propiedad como requerida, debe implementar los methods requeridos: generalmente con @synthesize … pero se puede hacer de otras maneras

(suponiendo que ABI / Modern Runtime no sea frágil) Usando solo lectura para simplificar

 @property(readonly)int dog; 

Podría implicarse:

 @synthesize dog; 

o

 @synthesize dog = _dog; // synthesize standard getter for the iVar _dog 

o

 - (int) dog { return _dog; // or dog, or cat/5 or 5 or whatever } 

EDITAR: re properties dinámicas

@dynamic es una palabra key que no hace nada para generar methods para satisfacer el requisito de una propiedad, lo que sí hace es informar al comstackdor que está "cuidado" de alguna otra manera …

este despacho dynamic puede lograrse mediante unos pocos methods diferentes en el time de ejecución, uno sería mediante la adición de implementaciones de methods en time de ejecución, otro sería mediante el uso del time de ejecución para los selectores no resueltos. (Tuve una pregunta similar sobre el uso de properties dinámicas para usar un almacén de KV genérico en un Diccionario)

ver: Uso de NSMutableDictionary como almacén de respaldo para properties

Después de muchas depuraciones y testings, he llegado a la conclusión de que se trata de un error. Si alguien tiene alguna testing o sugerencia contraria, siéntase libre de publicar. El error es el siguiente:

A veces cuando una property se define en un protocol y luego una class se ajusta a dicho protocol , el time de ejecución no es consciente de los attributes de la property's (por ejemplo, class_getProperty falla) si la property se marca como dynamic .

Rememeber, dynamic no proporciona ninguna implementación , simplemente suprime las advertencias, sin embargo, los attributes de property aún deben ser recuperables a través del time de ejecución.

Quería agregar algunos fragments de código útiles para resolver / depurar este tipo de problemas:

 - (NSArray *)propertyNamesForClass:(Class)aClass includeInherited:(BOOL)shouldIncludeInherited; { NSMutableArray *names = [NSMutableArray array]; uint propertyCount = 0; objc_property_t *properties = class_copyPropertyList(aClass, &propertyCount); for (uint i = 0; i < propertyCount; i++) { [names addObject:[NSString stringWithUTF8String:property_getName(properties[i])]]; } if (shouldIncludeInherited) { Class superClass = aClass; while ((superClass = class_getSuperclass(superClass))) { uint superPropertyCount = 0; objc_property_t *superProperties = class_copyPropertyList(superClass, &superPropertyCount); for (uint i = 0; i < superPropertyCount; i++) { [names addObject:[NSString stringWithUTF8String:property_getName(superProperties[i])]]; } } } NSArray *sortedNames = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; return sortedNames; } - (NSArray *)protocolNamesForClass:(Class)aClass includeInherited:(BOOL)shouldIncludeInherited; { NSMutableArray *names = [NSMutableArray array]; uint protocolCount = 0; __unsafe_unretained Protocol **protocolArray = class_copyProtocolList(aClass, &protocolCount); for (uint i = 0; i < protocolCount; i++) { [names addObject:NSStringFromProtocol(protocolArray[i])]; } if (shouldIncludeInherited) { Class superClass = aClass; while ((superClass = class_getSuperclass(superClass))) { uint superProtocolCount = 0; __unsafe_unretained Protocol **superProtocolArray = class_copyProtocolList(superClass, &superProtocolCount); for (uint j = 0; j < superProtocolCount; j++) { [names addObject:NSStringFromProtocol(superProtocolArray[j])]; } } } NSArray *sortedNames = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; return sortedNames; } - (NSArray *)propertyNamesForProtocol:(Protocol *)aProtocol { NSMutableArray *names = [NSMutableArray array]; uint protocolPropertyCount = 0; objc_property_t *properties = protocol_copyPropertyList(aProtocol, &protocolPropertyCount); for (uint j = 0; j < protocolPropertyCount; j++) { [names addObject:[NSString stringWithUTF8String:property_getName(properties[j])]]; } NSArray *sortedNames = [names sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; return sortedNames; }