0

I have an existing class for which I do not have the source, and I want to add a property to the class. The private class implements a known protocol which is exposed, but the class type is not exposed.

Some callback happens and I receive the object named answer.

I want to extend the ComplexNumber type to have more properties, e.g.

 @interface NSObject()<ComplexNumber>
     @property (assign) BOOL offline;
 @end

 @implementation SomeClass 

 didReceiveAnswer:id<ComplexNumber>answer forEquation:(NSString*)equation {
     //
     if (answer.offline) {
        // 
     }
 }

 @end

This also fails: Cast unknown type to be of type NSObject:

     if (((NSObject*)answer).offline) {
        // 
     }
Wayne
  • 3,359
  • 3
  • 30
  • 50
  • Possible duplicate: https://stackoverflow.com/questions/8733104/objective-c-property-instance-variable-in-category – Daniele Pantaleone Aug 30 '18 at 12:06
  • @DanielePantaleone It does not seem to be related, this is specifically to anonymous classes, the example in the question you mentioned it is not an anonymous. – Wayne Aug 30 '18 at 12:21
  • Objective-C doesn't support Anonymous classes. – Daniele Pantaleone Aug 30 '18 at 12:23
  • @DanielePantaleone What would I call `idanswer` the class is not know (exposed)? - I interpreted this to be anonymous. – Wayne Aug 30 '18 at 12:39
  • id is a pointer to an objective-c object. When you use idanswer you are saying that "answer" is an objective-c object that conforms to the ComplexNumber protocol. – Daniele Pantaleone Aug 30 '18 at 12:45
  • @DanielePantaleone I have added some more code above, where I cast the pointer to be of type NSObject, and the extension is of type NSObject. Not sure if this will actually work; But for my specific implementation it does not. Maybe the class I am receiving does not inherit from NSObject - or this cannot be done at all. – Wayne Aug 30 '18 at 13:25
  • @Wayne If I'm correct in understanding that you're trying to make a category that only takes effect if an object implements a given protocol, you can't do that in Objective-C—it's all or nothing on the class. Swift can do this, though, via a protocol extension. – Charles Srstka Aug 30 '18 at 14:07
  • @CharlesSrstka Thanks. I think my approach is going to fail for several reasons: 1) Extensions can only add properties to classes to which you have the source code. 2) id is truly anonymous and not all objects inherit from NSObject, unlike Java where all classes share a common ancestor. 3) obj-c runtime seems the only one that will work using objc_setAssociatedObject and objc_getAssociatedObject and this will produce unmaintainable code eventually if the project becomes too big. – Wayne Aug 30 '18 at 14:28

1 Answers1

0

There appear to be two issues here:

  • get access to the private class
  • add a property to it.

If you know the name of the private class you can simply use it be defining it again:

// SomeClass.h

@interface SomeClass : NSObject <ComplexNumber>

@end

This might seem odd, but this will be sufficient to pass the compilation stage of your build process and allow you to use the property in your code. The existing implementation of the private class will be sufficient to deal with the link stage.

As Daniele Pantaleone points out, the second part is very close to Objective-C: Property / instance variable in category. However I've added it for completness:

// ComplexNumber.h

@protocol ComplexNumber <NSObject>
@property (assign) BOOL offline;
@end

//ComplexNumber.m

@import ObjectiveC;

@implementation NSObject (ComplexNumber)

static void *ComplexNumberKey = &ComplexNumberKey;

-(void)setOffline:(BOOL)offline
{
    objc_setAssociatedObject(self, &ComplexNumberKey, @(offline), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(BOOL)offline
{
    NSNumber *offline = objc_getAssociatedObject(self, &ComplexNumberKey);
    return offline.boolValue;
}

@end
jjrscott
  • 1,386
  • 12
  • 16