6

I am trying to retrieve a list of all the properties that my class or any of its subclasses define. The following code snippet is the code that I have been using, and it has worked properly all the way until the recent iOS8 beta 4.

if(!dictionary) {
    dictionary = [NSMutableDictionary dictionary];

    // Get all properties we have until we hit CBLNestedModel
    while(klass != [CBLNestedModel class]) {
        unsigned count;
        objc_property_t* properties = class_copyPropertyList(klass, &count);
        for (unsigned i = 0; i < count; i++) {
            objc_property_t property = properties[i];

            const char* propertyNameC = property_getName(property);
            NSString* propertyName = [NSString stringWithUTF8String:propertyNameC];
            const char* propertyAttrC = property_getAttributes(property);
            NSString* propertyAttrS = [NSString stringWithUTF8String:propertyAttrC];
            NSArray* propertyAttr = [propertyAttrS componentsSeparatedByString:@","];
            NSLog(@"%@ has property %@", NSStringFromClass(klass), propertyName);

            dictionary[propertyName] = propertyAttr;
        }
        free(properties);
        klass = [klass superclass];
    }

    propertyDictionary[klassString] = dictionary;
}

CBLNestedModel derives from NSObject. Basically, I want all properties that any subclass of CBLNestedModel declares, or its subclasses. The issue that I'm facing is that now, this code is returning extraneous properties that are not defined in my subclasses.. propertyNames are coming back with @"superclass", @"description", @"debugDescription", @"hash" for certain classes, even though I have never defined these properties anywhere in my subclasses.

A weird thing is that these extraneous properties are not returned for all subclasses of CBLNestedModel, but only for certain subclasses. However, they will reliably be returned for those subclasses on every run of my app.

Any idea why this is happening now?

André Morujão
  • 6,963
  • 6
  • 31
  • 41
rvijay007
  • 1,357
  • 12
  • 20
  • Same problem here. It's very strange because `superclass`, `description`, `debugDescription` & `hash` are not properties, but `NSObjectProtocol` methods implemented by `NSObject`. First question: is it a bug? Second one: shoud we create a radar? – Martin Aug 12 '14 at 15:55
  • 1
    Yes, this has to be a bug. This code works perfectly in iOS7. Lets definitely file bug reports via bugreport.apple.com. Hopefully the more people who report this issue will cause them to fix it by the next update. – rvijay007 Aug 13 '14 at 16:15
  • @Martin @rvijay007 It's not a bug, they are now properties in iOS 8. The question is why do they only show up for **some** subclasses. I am experiencing the same problem, but I have an XC test case with an object that is a direct subclass of NSObject that does NOT show those when calling a properties method on it. But I have a different object, another direct subclass that during normal operation of the app DOES return `hash`, `description`, `superclass`, and `debugDescription`. Can you share anything more about your subclasses that might help uncover the problem? Or an update on the report? – Dean Kelly Oct 26 '14 at 04:28
  • I'm having the same issue and again its only for some classes. I'm not exactly sure why its only some of the classes. Did you ever figure it out? – odyth Dec 07 '14 at 06:20
  • @odyth I may have figured it out and posted my answer below. – Dean Kelly Dec 10 '14 at 17:07

2 Answers2

9

Do your CBLNestedModel subclasses adhere to any protocols? I was previously seeing a similar issue with an object I had and could not figure out why hash, description, superclass, and debugDescription were showing up and I finally figured it out. Here was what I had:

@interface FOOObject : NSObject<NSCopying, FOOOtherProtocol>

Looks fine, it was a direct subclass of NSObject. In fact I had other objects for testing this, one with properties and one without.

@interface FOOObjectWithProperties : NSObject

@property NSString *someProperty;

@end

and

@interface FOOObjectWithoutProperties : NSObject
@end

During the tests FOOObjectWithProperties and FOOObjectWithoutProperties both did not have the four aforementioned NSObject properties included, but the original FOOObject DID.

So what was the difference? Well looking at NSCopying it didn't appear to add any properties so I looked at FOOOtherProtocol, some protocol I had implemented and it didn't have any properties declared either.

HOWEVER

Look at the declaration of FOOOtherProtocol:

@protocol FOOOtherProtocol<NSObject>

THERE IT IS. The objective-c runtime stuff does NOT include superclass properties in what is returned it will however include properties declared in protocol extensions (protocols that force adherence to other protocols).

Notice anything about hash, description, superclass, and debugDescription?

Look at where they are declared in the NSObject protocol declaration

Remove the forced NSObject protocol adherence from your subclasses' protocol(s) (since they are subclasses of NSObject anyway that already adheres to it) and you should see those properties go away.

Dean Kelly
  • 598
  • 7
  • 13
  • I think you hit the nail on the head. The classes that have the problem in my code ultimately have a protocol that conforms to NSObject protocol. nice find. – odyth Dec 10 '14 at 22:45
  • @odyth Thanks! It took some digging haha, but now that I know it feels like I know the secret to a magic trick; sort of a bitter-sweet gratification. – Dean Kelly Dec 11 '14 at 00:05
1

Looks like on iOS 8 those 4 methods are now declared as read-only properties in NSObject.

More info here.

André Morujão
  • 6,963
  • 6
  • 31
  • 41