我有一些关于在工作中使用属性和实例变量的讨论,因此我想找到一个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文档中找到这样严格的声明,所以请发布一个指向苹果文档的链接(如果存在),或者发布任何其他清除它的理论。
答案 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)。如果一切都可见,那么你的班级应该:
选项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
有关详细信息,请使用以下链接