6

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

@interface MyClass : MySuperClass 
{ 
    //nothing here
}

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

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

4

10 回答 10

14

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

即这有效:

@interface MyClass : MySuperClass 
{ 
    //nothing here
}

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

@implementation MyClass
@synthesize anObject;
@end

如果为 32 位 Mac OS X 或 iPhone Simulator 编译,编译器会报错。

于 2010-01-20T18:36:40.907 回答
13

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

考虑以下代码。

我的类.h

@interface MyClass : NSObject

// Your methods here
- (void) doSomething;

@end

我的班级.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对象而不是MyClass分配内存和初始化isa。之后,用户将透明地处理MyClassImpl对象。

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

于 2010-04-05T15:37:36.310 回答
7

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

我的类.h

@class PublicClass;

// Public interface 
@interface MyClass : NSObject

@property (nonatomic, retain) PublicClass *publicVar;
@property (nonatomic, retain) PublicClass *publicVarDiffInternal;

- (void)publicMethod;

@end

我的班级.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

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

像这样很容易暴露公共接口并隐藏细节实现。我用在我的项目上没有任何麻烦。

于 2011-09-09T10:11:42.437 回答
2

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

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

@implementation 类名

{

// Instance variable declarations.

}

// 方法定义。

@结尾

于 2012-01-16T18:46:34.667 回答
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
于 2010-01-20T18:28:24.700 回答
1

两种可能:

  1. 正如 bbum 所建议的,它可以利用现代运行时合成实例变量的能力。
  2. 该属性可能在该类中没有基础实例变量。属性不一定具有与实例变量的一对一映射。
于 2010-01-20T18:43:44.430 回答
0

不要这样做,但我觉得应该注意的是,运行时可以随时使用class_addIvar 添加 ivars

于 2010-04-05T15:45:06.013 回答
0

宏观技巧怎么样?

已经测试了下面的代码

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

我的类.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

我的班级.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
于 2010-01-20T18:58:15.020 回答
0

我能够在我的库中执行以下操作:

myLib.h:

@interface MyClass : SomeSuperClass <SomeProtocol> {
  // Nothing in here
}

- (void)someMethods;
@end

我的Lib.m

@interface MyClass () 
    SomeClass *someVars;

    @property (nonatomic, retain) SomeClass *someVars;
@end


@implementation MyClass

@synthesize someVar;

- (void)someMethods {
}
@end

该协议当然是可选的。我相信这也会使您的所有实例变量成为私有,尽管我不是 100% 确定。对我来说,它只是我的静态库的一个接口,所以它并不重要。

无论如何,我希望这可以帮助你。对于其他阅读本文的人,请让我知道这是否总体上很糟糕或有任何不可预见的后果。我自己对 Obj-C 很陌生,所以我总是可以使用有经验的人的建议。

于 2010-12-02T18:56:25.080 回答
-1

我认为在另一个答案中编写的以下代码没有按预期工作。扩展类中定义的“SomeClass *someVars”不是MyClass的实例变量。我认为它是一个 C 全局变量。如果你合成 someVars,你会得到编译错误。而且 self.someVars 也不起作用。

我的库

@interface MyClass : SomeSuperClass <SomeProtocol> {
    // Nothing in here
}

- (void)someMethods;
@end

我的Lib.m

@interface MyClass () 
    SomeClass *someVars;

    @property (nonatomic, retain) SomeClass *someVars;
@end

@implementation MyClass

@synthesize someVar;

- (void)someMethods {
}
@end
于 2011-03-31T21:19:16.467 回答