【问题标题】:Hiding properties from public access隐藏公共访问的属性
【发布时间】:2009-04-13 10:47:39
【问题描述】:

我正在尝试在 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”的声明

正确的方法是什么?

【问题讨论】:

标签: objective-c


【解决方案1】:

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

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

有关更多信息,请参阅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 为扩展方式。我还在这个答案stackoverflow.com/questions/360968/…中写了一个不同类型的类别的快速比较
  • @Abizern - 很好的参考。您还可以将该答案链接到此问题,以获取有关属性的更多信息。
  • 值得注意:显然扩展文档的 URL 已更改为:developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…
  • 这很好,但它真的回答了这个问题吗?我还需要在我的类别中使用综合,并遇到了这篇文章。我需要在我的类别中使用一些属性,我需要使用动态指令并为访问器实现 setIVar: 和 IVar。
  • @Daniel:确实如此;如果您实际上不需要存储这些属性(即您已经有一个存储它们的地方),那么只实现 getter/setter 也可以。在这种情况下,您不想使用@dynamic;只是不要说@synthesize
【解决方案2】:

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

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

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

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

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

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

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

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

然后,在实现文件中:

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

@implementation MyClass
@synthesize targetView;
@end

最终结果?一个公开只读的属性,但私有的可读写的属性,无需针对与类别相关的所有有趣的脆弱性开放属性。”

【讨论】:

  • 感谢您的链接。真的很有帮助。
【解决方案3】:

Scott Stevenson (http://theocacao.com/) 在他的博客文章 "A Quick Objective-C 2.0 Tutorial: Part II" 中解释了如何获得 Public Properties with Private Setter。按照他的建议,您将获得一个对公众只读的属性,但有一个可以与点语法一起使用的私有 setter。希望这会有所帮助...

【讨论】:

    【解决方案4】:

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

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

    还有另一个问题解决了这个问题,but it's pretty scant on the details

    干杯

    【讨论】:

      【解决方案5】:

      因为类别只能为类添加方法,所以您无法通过尝试在类别中定义属性方法来解决此问题。

      您可以声明派生自现有类的属性。例如。如果您的类具有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 关键字至少为其状态添加一些保护。

      【讨论】:

        【解决方案6】:

        其实,使用最新的 LLVM 编译器,这个问题可以得到更好的解决。之前推荐的尽可能多地隐藏属性的方法是在 .h 中声明变量,以 _ 为前缀,在私有 .m 中的类扩展中声明属性,然后在 @synthesise 中 @synthesise实施。

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

        汽车.h:

        @interface Car : NSObject
        
        - (void)drive;
        
        @end
        

        汽车.m:

        @interface Car ()
        
        @property (assign) BOOL driving;
        
        @end
        
        @implementation Car
        @synthesize driving;
        
        - (void)drive {
        
            self.driving = YES;
        }
        
        @end
        

        【讨论】:

        • 您甚至不需要声明后备 ivar。它是自动合成的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-22
        • 2014-06-06
        • 2012-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多