Hide instance variable from header file in Objective C

后端 未结 10 994
梦如初夏
梦如初夏 2020-12-30 14:35

I came across a library written in Objective C (I only have the header file and the .a binary). In the header file, it is like this:

@interface MyClass : MyS         


        
相关标签:
10条回答
  • 2020-12-30 14:40

    No you can't. But you can do this if you're not using @property:

    .h

    @interface X : Y {
      struct X_Impl* impl;
    }
    -(int)getValue;
    @end
    

    .m

    struct X_Impl {
      int value;
    };
    ...
    @implementation X
    -(void)getValue {
      return impl->value * impl->value;
    }
    @end
    
    0 讨论(0)
  • 2020-12-30 14:46

    You can use a class extension. A class extension is similar as category but without any name. On the Apple documentation they just define private methods but in fact you can also declare your internal variables.

    MyClass.h

    @class PublicClass;
    
    // Public interface 
    @interface MyClass : NSObject
    
    @property (nonatomic, retain) PublicClass *publicVar;
    @property (nonatomic, retain) PublicClass *publicVarDiffInternal;
    
    - (void)publicMethod;
    
    @end
    

    MyClass.m

    #import "PublicClass.h"
    #import "InternalClass.h"
    
    // Private interface
    @interface MyClass ( /* class extension */ ) 
    {
    @private
        // Internal variable only used internally
        NSInteger defaultSize;
    
        // Internal variable only used internally as private property
        InternalClass *internalVar;  
    
    @private 
        // Internal variable exposed as public property 
        PublicClass *publicVar;
    
        // Internal variable exposed as public property with an other name
        PublicClass *myFooVar;
    }
    
    @property (nonatomic, retain) InternalClass *internalVar;
    
    - (void)privateMethod;
    
    @end
    
    // Full implementation of MyClass
    @implementation MyClass
    
    @synthesize internalVar;
    @synthesize publicVar;
    @synthesize publicVarDiffInternal = myFooVar
    
    - (void)privateMethod 
    {
    }
    
    - (void)publicMethod 
    {
    }
    
    - (id)init
    {
       if ((self = [super init]))
         {
           defaultSize = 512;
           self.internalVar = nil;
           self.publicVar = nil;
           self.publicVarDiffInternal = nil; // initialize myFooVar
         }
    
       return self;
    }
    @end
    

    You can give MyClass.h to anyone with just your public API and public properties. On MyClass.m you declare your member variable private and public, and your private methods, on your class extension.

    Like this it's easy to expose public interfaces and hide detail implementation. I used on my project without any troubles.

    0 讨论(0)
  • 2020-12-30 14:51

    I don't think the following code written in another answer is working as expected. The "SomeClass *someVars" defined in the extension class is not an instance variable of MyClass. I think it is a C global variable. If you synthesize someVars, you will get compile error. And self.someVars won't work either.

    myLib.h

    @interface MyClass : SomeSuperClass <SomeProtocol> {
        // Nothing in here
    }
    
    - (void)someMethods;
    @end
    

    myLib.m

    @interface MyClass () 
        SomeClass *someVars;
    
        @property (nonatomic, retain) SomeClass *someVars;
    @end
    
    @implementation MyClass
    
    @synthesize someVar;
    
    - (void)someMethods {
    }
    @end
    
    0 讨论(0)
  • 2020-12-30 14:54

    You may use of the same idiom used in Cocoa classes. If you have a look to NSString class interface in NSString.h you'll see that there is no instance variable declared. Going deeper in GNUstep source code you'll find the trick.

    Consider the following code.

    MyClass.h

    @interface MyClass : NSObject
    
    // Your methods here
    - (void) doSomething;
    
    @end
    

    MyClass.m

    @interface MyClassImpl : MyClass {
       // Your private and hidden instance variables here
    }
    @end
    
    @implementation MyClass
    
    + (id) allocWithZone:(NSZone *)zone
    {
       return NSAllocateObject([MyClassImpl class], 0, zone);
    }
    
    // Your methods here
    - (void) doSomething {
      // This method is considered as pure virtual and cannot be invoked
      [self doesNotRecognizeSelector: _cmd];          
    }
    
    @end
    
    @implementation MyClassImpl
    
    // Your methods here
    - (void) doSomething {
      // A real implementation of doSomething
    }
    
    @end
    

    As you can see, the trick consist in overloading allocWithZone: in your class. This code is invoked by default alloc provided by NSObject, so you don't have to worry about which allocating method should be used (both are valid). In such allocWithZone:, you may use the Foundation function NSAllocateObject() to allocate memory and initialize isa for a MyClassImpl object instead of MyClass. After that, the user is dealing with a MyClassImpl object transparently.

    Of course, the real implementation of your class shall be provided by MyClassImpl. The methods for MyClass shall be implemented in a way that considers a message receiving as an error.

    0 讨论(0)
  • 2020-12-30 14:55

    DON'T do this, but I feel it should be noted that the runtime has the ability to add ivars whenever you want with class_addIvar

    0 讨论(0)
  • 2020-12-30 14:58

    I was able to do the following in my library:

    myLib.h:

    @interface MyClass : SomeSuperClass <SomeProtocol> {
      // Nothing in here
    }
    
    - (void)someMethods;
    @end
    

    myLib.m

    @interface MyClass () 
        SomeClass *someVars;
    
        @property (nonatomic, retain) SomeClass *someVars;
    @end
    
    
    @implementation MyClass
    
    @synthesize someVar;
    
    - (void)someMethods {
    }
    @end
    

    The protocol is optional of course. I believe this also makes all your instance variables private though I'm not 100% certain. For me it's just an interface to my static library so it doesn't really matter.

    Anyway, I hope this helps you out. To anyone else reading this, do let me know if this is bad in general or has any unforeseen consequences. I'm pretty new to Obj-C myself so I could always use the advice of the experienced.

    0 讨论(0)
提交回复
热议问题