代码之家  ›  专栏  ›  技术社区  ›  lhunath

从公共访问隐藏属性

  •  24
  • lhunath  · 技术社区  · 15 年前

    我试图声明仅供内部使用的属性 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
    

    在这里,编译器抱怨:

    @类别的实现中不允许合成

    所以我试着把它放进我的 BarLayer 实现,但在这里它在 驳船 接口。

    在接口中找不到属性“menubutton”的声明

    正确的方法是什么?

    6 回复  |  直到 12 年前
        1
  •  42
  •   Jesse Rusak    12 年前

    你不能用 @synthesize 有一个类别。

    可以 用一个类扩展(也就是匿名类)来实现这一点,它只是一个没有名称的类,其方法必须在主目录中实现。 @implementation 为该类设置块。对于您的代码,只需将“(private)”更改为“()”,然后使用 @合成 在主要 @实现 与该类的其余代码一起阻塞。

    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_setAssociatedObject objc_getAssociatedObject 伪造其他实例变量。在这种情况下,您可以将这些声明为属性,并使用上面的objc_u*运行时方法自己实现setter和getter。有关这些函数的更多详细信息,请参见 the Apple docs on Objective-C runtime .

        2
  •  12
  •   Rose Perrone    14 年前

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

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

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

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

    另外一个要求是,这种性质的合成必须始终能够自然、准确地合成setter和getter。具体地说,在将属性声明为原子属性时,开发人员无法仅正确地手动写入getter-setter对的1/2;锁定基础结构没有公开,因此在这种情况下无法保证原子性。

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

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

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

    然后,在实现文件中:

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

    最终结果?一种公开只读的属性,但不公开读写,不打开与类别相关的所有有趣的脆弱性的属性。”

        3
  •  8
  •   f3lix    15 年前

    斯科特·史蒂文森( http://theocacao.com/ )在他的博客中解释 "A Quick Objective-C 2.0 Tutorial: Part II" 如何获得 具有私有setter的公共属性 . 按照他的建议,您将获得一个对公众只读的属性,但是有一个私有的setter,它可以与点语法一起使用。希望这有帮助…

        4
  •  2
  •   Community agent420    7 年前

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

    我写了一篇 post about it here 如果有人想了解更多细节的话。

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

    干杯

        5
  •  1
  •   Abizern    15 年前

    因为类别只能向类中添加方法,所以您无法通过尝试在类别中定义属性方法来绕过这一点。

    可以声明从已经存在的类派生的属性。例如。如果你的班级有 firstName 和A lastName 属性,您可以声明一个名为 fullName 在类别@implementation中。

    @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];
    }
    

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

    你可以用 @private Barlayer类中的关键字,以至少为其状态添加一些保护。

        6
  •  1
  •   lhunath    13 年前

    实际上,使用最新的LLVM编译器,这个问题可以更好地解决。先前建议尽可能隐藏属性的方法是在.h中声明变量,在变量前面加上u,在private.m的类扩展中声明属性,并在@implementation中@synthese该属性。

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

    汽车:

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

    汽车:

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