Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Obj-c protocol properties are not implemented in conforming class

Problem


I've come across an interesting issue and wasn't able to find any documentation on it... Sometimes properties declared in a protocol are not implemented in a particular class conforming to that protocol and a runtime exception occurs. Are dynamic property definitions optimized away under some strange circumstance? Can protocols not be used with properties that were made to be declared dynamic? Any insight into this would be greatly appreciated.

Below are some more details.

Given a protocol:

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

and a class implementing the protocol like so:

@interface MyClass <MyProtocol>
@end

@implementation MyClass
@dynamic someProperty;
@end

I've noticed that sometimes I am unable to get any information from calling

class_getProperty(myClass, propertyName);

for the properties in the protocol. This only happens to some classes and seems to be sporadic.

I'm running the latest Xcode 4 and linking against the iOS 6 SDK. I do have the pre-release Xcode 5 installed on the same machine though it is not the default (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 get meta data on the property EVEN THOUGH the property doesn't exist. In other words you don't need to synthesize the property to get it's attributes. The runtime still knows about it. Try it for yourself. The problem is that sometimes this doesn't happen. I want to know the conditions that cause the runtime to be unaware of the property attributes.

Temporary Fix


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

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

@implementation MyClass
@dynamic someProperty;
@end

This runs fine, though it is far from ideal. However, it suggests that my code is working correctly and the issue lies elsewhere.

I'd be happy to provide more details or background if needed.

like image 442
Christopher Avatar asked Jul 10 '13 17:07

Christopher


2 Answers

protocols define methods, optional methods and required methods.

properties are abstracted methods, if the protocol defines a property as being required then you must implement the required methods: typically with @synthesize... but can be done in other ways

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

@property(readonly)int dog;

could be implimented:

@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: re dynamic properties

@dynamic is a keyword that does nothing to generate methods to satisfy the requirement of a property, what it does do is inform the compiler that it is "taken care of" in some other way...

this dynamic dispatch can be accomplished by a few different methods at runtime, one would be by adding method implementations at run time, another would be by using the runtime for unresolved selectors. (I had a similar question about using dynamic properties to use a generic KV store in a Dictionary)

see: Using NSMutableDictionary as backing store for properties

like image 183
Grady Player Avatar answered Oct 16 '22 20:10

Grady Player


There seems to be a confusion:

  1. Declaring a property is enough for the property to exist at runtime. There is no need for implementation. This is how objective-c works. Methods don't have to exist at compile time, you can add them dynamically (e.g. what Core Data does).

  2. @dynamic does absolutely nothing during runtime. At compile-time it's a placeholder which says "don't give me compiler warnings that the getter/setter is not defined here". On the newest LLVM it also says "don't synthesize automatically".

My suggestions:

  1. If you are adding the protocol via a category, make sure the category is loaded. This seems to be the most usual problem with runtime reflection.

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

like image 38
Sulthan Avatar answered Oct 16 '22 18:10

Sulthan