隐藏公共访问的属性

时间:2009-04-13 10:47:39

标签: objective-c

我正在尝试声明仅供Private类别内部使用的属性:

@interface BarLayer (Private)

@property (readwrite, retain) MenuItemFont  *menuButton;
@property (readwrite, retain) Menu          *menuMenu;
@property (readwrite, retain) LabelAtlas    *messageLabel;

@end

现在我正试图找出我应该@synthesize那些确切的地方。

我试过了:

@implementation BarLayer (Private)

@synthesize menuButton      = _menuButton;
@synthesize menuMenu        = _menuMenu;
@synthesize messageLabel    = _messageLabel;

@end

这里,编译器抱怨:

  

在类别的实现中不允许使用@synthesize

所以我尝试将它放在BarLayer实现中,但是在BarLayer接口中没有找到声明。

  

在界面中找不到属性'menuButton'的声明

正确的方法是什么?

6 个答案:

答案 0 :(得分:42)

您不能将@synthesize与类别一起使用。

可以使用类扩展(a.k.a.匿名类别)执行此操作,该类扩展只是一个没有名称的类别,其方法必须在该类的主@implementation块中实现。对于您的代码,只需将“(私有)”更改为“()”并在主@synthesize块中使用@implementation以及该类的其余代码。

有关详情,请参阅the Apple docs on extensions。 (显然这是Mac OS 10.5中的新功能。)

编辑:一个例子:

// Main interface (in .h)
@interface Foo : NSObject
- (void)bar;
@end

// Private interface (in .m, or private .h)
@interface Foo ()
@property (nonatomic, copy) NSString *myData;
@end

@implementation Foo
@synthesize myData; // only needed for Xcode 4.3 and earlier
- (void)bar { ... }
@end

另一个解决方案是使用objc_setAssociatedObjectobjc_getAssociatedObject伪造其他实例变量。在这种情况下,您可以将这些声明为属性,并使用上面的objc_ *运行时方法自己实现setter和getter。有关这些功能的更多详细信息,请参阅the Apple docs on Objective-C runtime

答案 1 :(得分:12)

我找到了一个解释为什么在类别中禁止合成属性,但是如何使用类扩展

以下信息来自http://www.friday.com/bbum/2009/09/11/class-extensions-explained/

“合成在类别中被禁止的原因是因为合成需要存储,并且无法有效地在一个类别中声明存储,并且允许类别合成方法然后直接干掉类的ivars被认为是不可接受的太脆弱和丑陋。

但是,显然也希望能够声明一个公开只读的属性,但是为了内部到类或框架的目的,它的实现是readwrite。

另外一个要求是合成这些特性必须始终能够自然而精确地合成定位器和吸气剂。具体来说,当将属性声明为原子属性时,开发人员无法正确地手动写入只有1/2的getter setter对;锁定基础设施没有暴露,因此,在这种情况下无法保证原子性。

类扩展优雅地解决了这个问题。

具体来说,您可以声明如下属性:

@interface MyClass : NSObject
@property(readonly) NSView *targetView;
@end

然后,在实现文件中:

@interface MyClass()
@property(readwrite) NSView *targetView;
@end

@implementation MyClass
@synthesize targetView;
@end

最终结果?这是一种公开读取的属性,但是私下读写时不会打开属性,直到与类别相关的所有有趣的脆弱性。“

答案 2 :(得分:8)

斯科特史蒂文森(http://theocacao.com/)在他的博文"A Quick Objective-C 2.0 Tutorial: Part II"中解释了如何获得公共属性与私人二手。根据他的建议,您将获得一个只读给公众的属性,但有一个私有的setter可以使用dot-syntax。希望这会有所帮助...

答案 3 :(得分:2)

我只想添加2美分,让人们知道可以通过类别(不是类扩展)向现有类添加属性。它需要使用关联引用,但实际上并没有那么糟糕。

如果有人想了解更多细节,我写了post about it here

还有另一个问题涉及此主题,but it's pretty scant on the details

干杯

答案 4 :(得分:1)

因为类别只能向类中添加方法,所以尝试在类别中定义属性方法时无法解决这个问题。

您可以声明从现有类派生的属性。例如。如果您的类具有firstNamelastName属性,则可以在@implementation类别中声明名为fullName的属性。

@interface Bar (Private)
@property (readonly) NSString *fullName; // Note readonly, you have nothing to write to.
@end

但是,你不能只@synthesize这个属性,你必须编写自己的访问器,因为编译器不知道你想从哪里得到这个值。

@implementation Bar (Private)
- (NSString *)fullName {
    NSString *returnString = [NSString stringWithFormat:@"%@ %@", 
                             self.firstName, self.lastName];
}

从类设计的角度来看,我不确定私有属性的概念是否有意义:我个人认为属性是由类公开暴露的东西。

您可以使用BarLayer类中的@private关键字来至少为其状态添加一些保护。

答案 5 :(得分:1)

实际上,使用最新的LLVM编译器,可以更好地解决这个问题。以前建议的隐藏尽可能多的属性的方法是在你的.h中声明你的变量,在它们前面添加_,在私有.m中的类扩展中声明一个属性,然后@synthesise在你的@中的属性实施

使用最新的LLVM(3.0),您可以更进一步,隐藏您的财产的所有内容,包括支持ivar。它的声明可以移动到.m甚至在那里省略,在这种情况下它将由编译器合成(感谢Ivan):

Car.h:

@interface Car : NSObject

- (void)drive;

@end

Car.m:

@interface Car ()

@property (assign) BOOL driving;

@end

@implementation Car
@synthesize driving;

- (void)drive {

    self.driving = YES;
}

@end