在哪里把iVars放在“现代”的Objective-C中?

Ray Wenderlich编写的“iOS6 by Tutorials”一书有一个关于编写更多“现代”Objective-C代码的非常好的章节。 在一节中,这些书介绍了如何将iVars从类的头部移动到实现文件中。 由于所有的iVar应该是私人的,这似乎是正确的做法。

但是到目前为止,我发现了3种方法。 每个人都在做不同的事情。

1.)将iVar放在一个大括号内(这是本书的工作原理)。

2.)将iVar放在@implementantion下,不要用大括号

3.)将iVars放在@implementantion(一个类的扩展名)之上的私有接口中,

所有这些解决scheme似乎工作正常,迄今为止,我没有注意到我的应用程序的行为有任何差异。 我猜这样做没有“正确”的方式,但我需要写一些教程,我想只select一种方式为我的代码。

我该走哪条路?

编辑:我只是在这里谈论iVars。 不属性。 只有对象只需要附加的variables,不应该暴露在外面。

代码示例

1)

#import "Person.h" @implementation Person { int age; NSString *name; } - (id)init { self = [super init]; if (self) { age = 40; name = @"Holli"; } return self; } @end 

2)

 #import "Person.h" @implementation Person int age; NSString *name; - (id)init { self = [super init]; if (self) { age = 40; name = @"Holli"; } return self; } @end 

3)

 #import "Person.h" @interface Person() { int age; NSString *name; } @end @implementation Person - (id)init { self = [super init]; if (self) { age = 40; name = @"Holli"; } return self; } @end 

将实例variables放在@implementation块或类扩展中的能力是“现代Objective-C运行时”的一个特性,它被iOS的每个版本以及64位Mac OS X程序所使用。

如果要编写32位Mac OS X应用程序,则必须将实例variables放在@interface声明中。 不过,您可能不需要支持32位版本的应用。 OS X已经支持自从五年前发布的10.5版本(Leopard)以来的64位应用程序。

因此,我们假设您只编写将使用现代运行时的应用程序。 你应该把你的ivars放在哪里?

选项0:在@interface (不@interface

首先,让我们@interface一下为什么我们不想将实例variables放在@interface声明中。

  1. 将实例variables放在@interface中可以向类的用户公开实现的细节。 这可能会导致这些用户(甚至在使用自己的类的时候)依靠他们不应该实现的细节。 (这与我们是否宣布ivars @private无关。)

  2. 将实例variables放在@interface会使得编译时间更长,因为任何时候我们添加,更改或移除伊娃声明,我们都必须重新编译导入接口的每个.m文件。

所以我们不想把实例variables放在@interface 。 我们应该把他们放在哪里?

选项2:在没有大括号的@implementation (不要做)

接下来,让我们来讨论一下你的选项2,“把iVar放在@implementantion下面,不用花括号”。 这不会声明实例variables! 你在说这个:

 @implementation Person int age; NSString *name; ... 

该代码定义了两个全局variables。 它不声明任何实例variables。

.m文件中定义全局variables,甚至在@implementation定义全局variables也是可以的,如果你需要全局variables – 例如,因为你想让所有的实例共享某个状态,比如caching。 但是你不能用这个选项来声明ivars,因为它没有声明ivars。 (另外,对于实现来说私有的全局variables通常应该声明为static以避免污染全局名称空间并冒着连接时错误的风险。)

这留下了你的选项1和3。

选项1:在带有大括号的@implementation (做它)

通常我们要使用选项1:将它们放在主要的@implementation块中,如下所示:

 @implementation Person { int age; NSString *name; } 

我们把它们放在这里是因为它保持了它们的私密性,防止了我之前描述的问题,因为通常没有理由把它们放在类扩展中。

那么我们什么时候想使用你的选项3,把它们放在类扩展中?

选项3:在课程扩展中(仅在必要时进行)

几乎从来没有理由把它们放在与类的@implementation相同的文件中的类扩展中。 在这种情况下,我们不妨把它们放在@implementation中。

但是偶尔我们可能会写一个足够大的类,以便将源代码分成多个文件。 我们可以使用类别做到这一点。 例如,如果我们正在实现UICollectionView (一个相当大的类),我们可能会决定将pipe理可重用视图队列(单元格和补充视图)的代码放在单独的源文件中。 我们可以通过将这些消息分成一个类别来做到这一点:

 // UICollectionView.h @interface UICollectionView : UIScrollView - (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout; @property (nonatomic, retain) UICollectionView *collectionViewLayout; // etc. @end @interface UICollectionView (ReusableViews) - (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier; - (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier; - (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier; - (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier; - (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath; - (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath; @end 

好了,现在我们可以在UICollectionView.m实现主要的UICollectionView方法,并且可以实现在UICollectionView+ReusableViews.m中pipe理可重用视图的方法,这使得我们的源代码更易于pipe理。

但是我们可重用的视图pipe理代码需要一些实例variables。 这些variables必须暴露给@implementation中的主类UICollectionView.m ,所以编译器会在.o文件中发出它们。 我们还需要将这些实例variables暴露给UICollectionView+ReusableViews.m的代码,以便这些方法可以使用ivars。

这是我们需要类扩展的地方。 我们可以将可重用的视图pipe理ivars放在一个私有头文件的类扩展中:

 // UICollectionView_ReusableViewsSupport.h @interface UICollectionView () { NSMutableDictionary *registeredCellSources; NSMutableDictionary *spareCellsByIdentifier; NSMutableDictionary *registeredSupplementaryViewSources; NSMutableDictionary *spareSupplementaryViewsByIdentifier; } - (void)initReusableViewSupport; @end 

我们不会将这个头文件发送给我们的库的用户。 我们只需在UICollectionView.mUICollectionView+ReusableViews.m导入它,这样所有需要看到这些ivars的东西都可以看到它们。 我们还引入了一种方法,我们希望主要的init方法调用来初始化可重用的视图pipe理代码。 我们将从-[UICollectionView initWithFrame:collectionViewLayout:]调用该方法,我们将在UICollectionView+ReusableViews.m实现它。

选项2是错误的。 这些是全局variables,而不是实例variables。

选项1和3基本相同。 这完全没有区别。

可以select是将实例variables放在头文件还是实现文件中。 使用头文件的好处是你有一个快速简单的键盘快捷键(Command + Control + Up在Xcode中)来查看和编辑你的实例variables和接口声明。

缺点是你在公开的头部公开了你的类的私人细节。 这是不可取的,有些情况下,特别是如果你正在编写代码供其他人使用。 另一个潜在的问题是,如果你使用Objective-C ++,最好避免在你的头文件中放置任何C ++数据types。

对于某些情况来说,实现实例variables是很好的select,但是对于我的大部分代码,我仍然把实例variables放在头文件中,这是因为作为Xcode中的编码器更方便。 我的build议是做任何你觉得更方便的事情。

很大程度上与伊娃对亚类的知名度有关。 子类将无法访问@implementation块中定义的实例variables。

对于我计划分发的可重用代码(例如库或框架代码),我不希望将实例variables暴露给公众检查,那么我倾向于将Ivars放置在实现块(您的选项1)中。

您应该将实例variables放在实现上方的专用接口中。 选项3。

要阅读的文档是Objective-C指南中的编程 。

从文档:

您可以定义没有属性的实例variables

每当需要跟踪某个值或另一个对象时,最好在对象上使用属性。

如果您确实需要定义自己的实例variables而不声明属性,则可以将它们添加到类接口或实现的顶部的大括号中,如下所示:

公共ivars应该真正在@interface中声明属性(可能是你在1中想到的)。 私有ivars,如果你正在运行最新的Xcode,并使用现代的运行时(64位OS X或iOS),可以在@implementation(2)中声明,而不是在类扩展中声明,重新思考3。