如何在目标c中实施私人和公共成员?

时间:2011-11-20 17:04:58

标签: objective-c ios cocoa-touch oop properties

我有一些关于在工作中使用属性和实例变量的讨论,因此我想找到一个wiki的答案。现在,我知道在objective-c中没有真正的私有成员类型,一切都是公开的。但是,我有点担心我们应该如何设计我们的类以及遵守OOP原则。我想听听这三种设计方法的意见:

一个。根据各种帖子甚至是新的斯坦福大学iPhone开发课程,你应该随时随地使用属性。然而恕我直言,这种方法制定了OOP设计原则,因为在这种情况下,所有成员都会公开。为什么我需要将所有内部/本地实例变量发布到外部?此外,如果您通过属性使用合成设置器,而不是直接使用本地ivar,则会有一些(但仍然)开销很小。这是一个示例:

//==== header file =====//
@interface MyClass : NSObject

@property (nonatomic, retain) NSString *publicMemberWithProperty;
@property (nonatomic, retain) NSString *propertyForPrivateMember;

@end

B中。另一种方法是在私有成员的头文件中声明ivars(不声明相对属性),并在同一个头文件中为公共成员声明纯属性(不声明相对的ivars)。在这种情况下,ivars将直接在课堂上使用。这种方法很有意义但不会使用属性的所有好处,因为我们在设置新值之前手动释放旧值。这是一个示例:

//==== header file =====//
@interface MyClass : NSObject{
  NSString *_privateMember;
}

@property (nonatomic, retain) NSString *publicMemberWithProperty;

@end

℃。在头文件中为公共成员声明纯属性(不声明相对的ivars),并在实现文件的私有接口中为私有成员声明纯属性(不声明相对的ivars)。这种方法恕我直言比第一种更明确,但同样的问题仍然存在:为什么我们必须拥有内部/本地成员的财产?这是一个示例:

//==== header file =====//
@interface MyClass : NSObject

@property (nonatomic, retain) NSString *publicMemberWithProperty;

@end

//==== implementation file =====//
@interface MyClass()

@property (nonatomic, retain) NSString *propertyForPrivateMember;

@end

这个决定的自由让我感到很恼火,我想从各自的消息来源得到关于应该如何做的确认。但是,我无法在Apple文档中找到这样严格的声明,所以请发布一个指向苹果文档的链接(如果存在),或者发布任何其他清除它的理论。

3 个答案:

答案 0 :(得分:21)

通过使用类扩展,您可以拥有私有属性。

类扩展语法很简单:

在具有该类的.m文件中,创建一个未命名的类别:

·H

@interface OverlayViewController : UIViewController <VSClickWheelViewDelegate>
- (IBAction)moreButtonClicked:(id)sender;
- (IBAction)cancelButtonClicked:(id)sender;
@end

的.m

#import "OverlayViewController.h"

@interface OverlayViewController ()
@property(nonatomic) NSInteger amount;
@property(retain,nonatomic)NSArray *colors;
@end

@implementation OverlayViewController
@synthesize amount = amount_;
@synthesize colors = colors_;

//…

@end

现在,您已获得私有成员的所有属性方面,而不会将其暴露给公众。编写getter / setter的合成属性应该没有开销,因为编译器在编译时会创建或多或少的相同内容。

请注意,此代码使用合成的ivars。标题中不需要ivar声明。

关于这种方法,有一个很好的cocoawithlove article

您还会问为什么要使用私有ivars的属性。有几个很好的理由:


从LLVM 3开始,也可以在类扩展中声明ivars

@interface OverlayViewController (){
    NSInteger amount;
    NSArray *colors;
}
@end

或甚至在实施区块

@implementation OverlayViewController{
    NSInteger amount;
    NSArray *colors;
}
//…
@end

参见“WWDC2011:会议322--目标-C深度推进”(~03:00)

答案 1 :(得分:3)

确实没有一个干净,安全,零开销的解决方案,直接由语言支持。许多人对当前的可见性功能感到满意,而许多人认为它们缺乏。

运行时可以(但不会)与ivars和方法区分。 IMO,一流的支持是最好的。在那之前,我们有一些抽象习语:

选项A

很糟糕 - 一切都可见。我不同意这是一个好方法,那不是OOD(IMO)。如果一切都可见,那么你的班级应该:

  • 支持客户如何使用您的课程(通常不合理或不受欢迎)的所有案例
  • 或者您通过文档向他们提供了大量规则(文档更新可能会被忽视)
  • 或访问者应该没有副作用(不是OOD,经常转换为'不要覆盖访问者')

选项B

如果选项A存在缺陷,并且与选项A类似,则可以通过密钥访问成员。

选项C

这稍微安全一些。与所有其他人一样,您仍然可以使用键控访问,子类可以覆盖您的访问者(即使在不知情的情况下)。

选项D

一种方法是将您的类编写为实现类型的包装器。您可以使用ObjC类型或C ++类型。你可能喜欢速度很重要的C ++(在OP中提到过)。

一种简单的方法可以采用以下形式之一:

// inner ObjC type
@class MONObjectImp;

@interface MONObject : NSObject
{
@private
 MONObjectImp * imp;
}
@end


// Inner C++ type - Variant A
class MONObjectImp { ... };

@interface MONObject : NSObject
{
@private
 MONObjectImp imp;
}
@end


// Inner C++ type - Variant B
class MONObjectImp;

@interface MONObject : NSObject
{
@private
 MON::t_auto_pointer<MONObjectImp> imp;
}
@end

(注意:由于这是最初编写的,因此引入了在@implementation块中声明ivars的能力。如果没有必要支持旧的工具链或'脆弱的'32-,你应该在那里声明你的C ++类型位OS X ABI)。

C ++ Variant A并不像其他人那样“安全”,因为它要求客户端可以看到类的声明。在其他情况下,您可以在实现文件中声明和定义Imp类 - 将其隐藏在客户端中。

然后您可以公开您选择的界面。当然,客户仍然可以访问您的成员,如果他们真的想通过运行时。这对于他们安全地使用ObjC Imp类型来说是最容易的 - objc运行时不支持成员的C ++语义,所以客户端会要求UB(IOW它是运行时的所有POD)。

ObjC实现的运行时成本是编写一个新类型,为每个实例创建一个新的Imp实例,以及大量的消息传递。

除了分配(变体B)之外,C ++类型的成本几乎为零。

选项E

其他方法通常将ivars与接口分离。虽然这是一件好事,但它对于ObjC类型来说也是非常不寻常的。 ObjC类型/设计通常与他们的ivars和访问者保持密切关系 - 所以你将面临来自其他开发者的阻力。

答案 2 :(得分:2)

与C ++类似,Objective C提供公共,私有和受保护的范围。它还提供了一个包范围,它类似于Java中定义的包范围。 类的公共变量可以是程序中任何位置的引用。 私有变量只能在声明它的类的消息中引用。它可以在属于同一类的任何实例的消息中使用。 包范围类似于同一图像中的公共范围,即可执行文件或库。根据Apple的文档,在64位体系结构中,在不同图像中定义的包范围变量将被视为私有。 变量范围由@ public,@ private,@ protected,@ package修饰符定义。这些修饰符可以以类似于C ++或Java的方式使用。范围声明下列出的所有变量属于同一范围。此外,变量可以列在声明范围的同一行上。

    @interface VariableScope : NSObject {
        @public
        int iVar0;
        @protected
        int iVar1;
        @private
        int iVar2;
        @package
        int iVar3;

@public int iVar01, iVar02;
@protected int iVar11, iVar12;
@private int iVar21, iVar22;
@package int iVar31, iVar32;
}
  @end

有关详细信息,请使用以下链接

http://cocoacast.com/?q=node/100