从Objective C中的头文件隐藏实例变量

时间:2010-01-20 18:25:07

标签: objective-c header declaration private instance-variables

我遇到了一个用Objective C编写的库(我只有头文件和.a二进制文件)。 在头文件中,它是这样的:

@interface MyClass : MySuperClass 
{ 
    //nothing here
}

@property (nonatomic, retain) MyObject anObject;
- (void)someMethod;

我怎样才能达到同样的目的?如果我尝试在接口的{}内声明没有相应ivar的属性,编译器会给我一个错误。最后,我想在.a中隐藏我的类的内部结构,并将必要的方法暴露给头文件。如何在.m中声明实例变量?类别不允许我添加ivar,只是方法。

10 个答案:

答案 0 :(得分:14)

对于64位应用程序和iPhone应用程序(虽然不在模拟器中),属性合成也能够合成实例变量的存储。

即。这有效:

@interface MyClass : MySuperClass 
{ 
    //nothing here
}

@property (nonatomic, retain) MyObject *anObject;
@end

@implementation MyClass
@synthesize anObject;
@end

如果编译32位Mac OS X或iPhone模拟器,编译器将发出错误。

答案 1 :(得分:13)

您可以使用Cocoa类中使用的相同习语。如果您查看NSString.h中的NSString类接口,您将看到没有声明实例变量。深入了解GNUstep源代码,您将找到诀窍。

请考虑以下代码。

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

正如您所看到的,诀窍在于在您的课程中重载 allocWithZone:。此代码由 NSObject 提供的默认 alloc 调用,因此您不必担心应该使用哪种分配方法(两者都有效)。在这样的 allocWithZone:中,您可以使用Foundation函数 NSAllocateObject()来分配内存并为 MyClassImpl isa > object而不是 MyClass 。之后,用户透明地处理 MyClassImpl 对象。

当然,您的类的真正实现应由 MyClassImpl 提供。 MyClass 的方法应以将消息作为错误接收的方式实现。

答案 2 :(得分:6)

您可以使用类扩展。类扩展类似于类别但没有任何名称。在Apple文档中,他们只定义私有方法,但实际上您也可以声明内部变量。

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

您可以将MyClass.h提供给只有您的公共API和公共属性的任何人。在MyClass.m上,您在类扩展上声明您的成员变量private和public以及您的私有方法。

像这样,很容易公开公共接口并隐藏详细实现。我在没有任何麻烦的情况下使用了我的项目。

答案 3 :(得分:2)

根据我一直在看的文档,没有问题。隐藏实例变量所需要做的就是在{...}内的@implementation部分的开头声明它们。但是,我是Objective C的相对新手,我有可能误解了一些东西 - 我怀疑语言已经改变了。我实际上尝试过这个系统,使用XCode 4.2,为iPad构建代码,它似乎工作正常。

我的这个想法的一个来源是http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocDefiningClasses.html上的Apple开发人员文档,它提供了这种模式:

  

@implementation ClassName

     

{

// Instance variable declarations.
     

}

     

//方法定义。

     

@end

答案 4 :(得分:1)

不,你不能。但是,如果您不使用@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

答案 5 :(得分:1)

两种可能性:

  1. 正如bbum建议的那样,它可以利用现代运行时的合成实例变量的能力。
  2. 该属性可能没有该类中的基础实例变量。属性不一定与实例变量进行一对一映射。

答案 6 :(得分:0)

宏诡计怎么样?

测试了以下代码

  • 用dylibs测试过 - 工作正常
  • 已经测试了子类 - 警告!会破坏,我同意这使得这个技巧没那么有用,但我认为它仍然告诉了一些关于ObjC的工作原理......

MyClass.h

@interface MyClass : NSObject {
#ifdef MYCLASS_CONTENT
    MYCLASS_CONTENT // Nothing revealed here
#endif
}
@property (nonatomic, retain) NSString *name;
@property (nonatomic, assign) int extra;
- (id)initWithString:(NSString*)str;
@end

MyClass.m

// Define the required Class content here before the #import "MyClass.h"
#define MYCLASS_CONTENT \
  NSString *_name; \
  int _extra; \
  int _hiddenThing; 

#import "MyClass.h"

@implementation MyClass
@synthesize name=_name;
@synthesize extra=_extra;

- (id)initWithString:(NSString*)str
{
    self = [super init];
    if (self) {
        self.name = str;
        self.extra = 17;
        _hiddenThing = 19;
    }
    return self;
}

- (void)dealloc
{
    [_name release];
    [super dealloc];
}

@end

答案 7 :(得分:0)

不要这样做,但我觉得应该注意运行时能够使用class_addIvar

添加ivars 你想要的

答案 8 :(得分:0)

我能够在我的图书馆中执行以下操作:

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

当然,协议是可选的。我相信这也使你的所有实例变量都是私有的,虽然我不是100%肯定的。对我而言,它只是我的静态库的一个接口,所以它并不重要。

无论如何,我希望这会帮助你。对于其他阅读此内容的人,请告诉我一般情况下这是不好的还是有任何不可预见的后果。我自己对Obj-C很新,所以我总能使用经验丰富的建议。

答案 9 :(得分:-1)

我不认为以下代码中写的代码是按预期工作的。 扩展类中定义的“SomeClass * someVars”不是MyClass的实例变量。我认为这是一个C全局变量。如果您合成someVars,您将收到编译错误。并且self.someVars也不起作用。

<强> 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