Obj-c protocol properties are not implemented in the corresponding class

Problem


I ran into an interesting problem and could not find the documentation for it ... Sometimes the properties declared in protocol are not implemented in a specific class corresponding to this protocol and a runtime exception occurs. Are dynamic property definitions defined in some strange circumstances? Can protocols not be used with properties that was declared as dynamic ? Any understanding of this would be very helpful.

Below are some details.

Given protocol :

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

and a class that implements protocol as follows:

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

I noticed that sometimes I can’t get any information from the call

 class_getProperty(myClass, propertyName); 

for properties in protocol . This only happens with some classes and seems sporadic.

I launch the latest Xcode 4 and get attached to the iOS 6 SDK. I have a preliminary release of Xcode 5 installed on the same computer, although it is not standard (via xcode-select).

Example


If you run this code:

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

and then you run

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

You will receive the metadata in the property EVEN THOUGH property does not exist. In other words, you do not need to synthesize a property to get its attributes . Runtime still knows this. Try it yourself. The problem is that sometimes this does not happen. I want to know the conditions that cause the runtime to not know property attributes.

Interim fix


My temporary fix is ​​to simply copy all the property definitions into the protocol and paste them into the .h file:

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

This works great, although far from ideal. However, this suggests that my code is working correctly, and the problem lies elsewhere.

I would be happy to provide more details or information if necessary.

+6
source share
3 answers

There seems to be confusion:

  • Declaring a property is sufficient for the property to exist at run time. No implementation needed. This is how objective-c works. Methods should not exist at compile time, you can add them dynamically (for example, what the main data does).

  • @dynamic does nothing at runtime. At compile time, this is a placeholder that says: "Don't tell me about the compiler that the getter / setter is not defined here." At the latest LLVM, he also says: "Don't synthesize automatically."

My suggestions:

  • If you are adding a protocol through a category, make sure the category is loaded. This is apparently the most common issue with runtime display.

  • To debug, try also using class_conformsToProtocol . It would be strange to have a class that conforms to the protocol without having the properties declared by the protocol.

+1
source

protocols define methods, optional methods, and required methods.

properties are abstract methods, if the protocol defines the property as necessary, then you must implement the required methods: usually with @synthesize ... but can be done in other ways

(assuming non-fragile ABI / Modern Runtime) Using readonly for simplicity

 @property(readonly)int dog; 

can be changed:

 @synthesize dog; 

or

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

or

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

EDIT: dynamic properties

@dynamic is a keyword that does nothing to create methods to satisfy the property requirement, what it does is tell the compiler that it "took care" of some other way ...

this dynamic dispatch can be performed in several different ways at runtime, one could add implementations of methods at runtime, and another using the runtime for unresolved selectors. (I had a similar question about using dynamic properties to use shared KV storage in a dictionary)

see Using NSMutableDictionary as a backup storage for properties

+2
source

After much debugging and testing, I came to the conclusion that this is a mistake. If anyone has any opposing evidence or suggestions, you are free to post. Bug fixed:

Sometimes, when a property is defined in protocol , and then the class matches the specified protocol , the runtime does not know the property's attributes (for example, class_getProperty does not work) if the property marked as dynamic .

Rememeber, dynamic does not provide any implementation, it just suppresses warnings, however property attributes can still be restored through the runtime.

I wanted to add useful snippets of code to solve / debug these types of problems:

 - (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; } 
0
source

Source: https://habr.com/ru/post/949144/


All Articles