Ray Wenderlich的《iOS6 by Tutorials》一书中有一章讲述了如何编写更"现代"的Objective-C代码.在其中一节中,这些书描述了如何将IVAR从类的头部移动到实现文件中.

但到目前为止,我找到了三种方法.每个人的做法都不一样.

1.)将IVAR放在@Implementation下的一块大括号内(这是本书中的做法).

2.)将IVAR放在@Implementation下,不带大括号

3.)将IVAR放在@implementantion(类扩展)上方的私有接口中

所有这些解决方案似乎都很好,到目前为止,我还没有注意到我的应用程序的行为有任何不同.

我该走哪条路?

Edit: I am only talking about iVars here. Not properties. Only additional variables the object needs only for itself and that should not be exposed to the outside.

代码示例

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

推荐答案

将实例变量放入@implementation块或类扩展中的能力是"现代Objective-C运行时"的一项功能,每个版本的iOS和64位Mac OS X程序都使用该功能.

如果要编写32位Mac OS X应用程序,必须将实例变量放入@interface声明中.不过,你可能不需要支持32位版本的应用程序.自5年前发布的10.5版(Leopard)以来,OS X一直支持64位apply.

所以,假设您只编写使用现代运行时的应用程序.你应该把IVAR放在哪里?

Option 0: In the @interface (Don't Do It)

首先,让我们来看一下为什么我们要在@interface声明中放置实例变量.

  1. 将实例变量放入@interface中会向类的用户公开实现的细节.这可能会导致这些用户(甚至你自己在使用自己的类时!)依靠他们不应该依赖的实施细节.(这与我们是否申报ivars @private无关.)

  2. 将实例变量放入@interface会使编译花费更长的时间,因为无论何时添加、更改或删除ivar声明,我们都必须重新编译导入接口的每个.m文件.

所以我们不想把实例变量放在@interface中.我们应该把它们放在哪里?

Option 2: In the @implementation without braces (Don't Do It)

接下来,让我们讨论一下您的选项2,"在@implementantion下放置IVAR,而不使用大括号块".这有not个声明实例变量!你说的是:

@implementation Person

int age;
NSString *name;

...

该代码定义了两个全局变量.它不声明任何实例变量.

如果需要全局变量,可以在.m文件中定义全局变量,甚至在@implementation文件中也可以,例如,因为您希望所有实例共享某些状态,比如缓存.但不能使用此选项声明IVAR,因为它不声明IVAR.(此外,实现专用的全局变量通常应声明为static,以避免污染全局命名空间和发生链接时间错误的风险.)

剩下你的 Select 1和3.

Option 1: In the @implementation with braces (Do It)

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

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

我们之所以把它们放在这里,是因为它将它们的存在保密,防止了我前面描述的问题,而且通常没有理由将它们放在类扩展中.

那么,我们什么时候要使用选项3,将它们放在类扩展中?

选项3:在类扩展中(仅在必要时执行)

几乎没有理由把它们放在与类的@implementation相同的文件中的类扩展名中.如果是那样的话,我们不妨把它们放进@implementation号.

但有时我们可能会编写一个足够大的类,以便将其源代码划分为多个文件.我们可以使用类别来实现这一点.例如,如果我们要实现UICollectionView(一个相当大的类),我们可能会决定将管理可重用视图(单元格和补充视图)队列的代码放在一个单独的源文件中.我们可以将这些信息分成一个类别:

// 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中实现管理可重用视图的方法,这使我们的源代码更易于管理.

但是我们的可重用视图管理代码需要一些实例变量.这些变量必须公开给UICollectionView.m中的主类@implementation,因此编译器将在.o文件中发出它们.我们还需要向UICollectionView+ReusableViews.m中的代码公开这些实例变量,以便这些方法可以使用IVAR.

这就是我们需要类扩展的地方.我们可以将可重用视图管理IVAR放在私有头文件的类扩展中:

// UICollectionView_ReusableViewsSupport.h

@interface UICollectionView () {
    NSMutableDictionary *registeredCellSources;
    NSMutableDictionary *spareCellsByIdentifier;

    NSMutableDictionary *registeredSupplementaryViewSources;
    NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}

- (void)initReusableViewSupport;

@end

我们不会将此头文件发送给我们库的用户.我们将在UICollectionView.mUICollectionView+ReusableViews.m中导入它,这样所有想要看到这些IVAR的人都能看到它们.我们还引入了一个方法,希望main init方法调用该方法来初始化可重用视图管理代码.我们将从-[UICollectionView initWithFrame:collectionViewLayout:]/UICollectionView.m调用该方法,并在UICollectionView+ReusableViews.m中实现它.

Objective-c相关问答推荐

将 NSMutableAttributedString 转换为 NSString

在 iOS8 上,以横向模式显示我的应用程序会隐藏状态栏,但在 iOS 7 上,状态栏会同时显示在两个方向上

将 CFDictionaryRef 转换为 NSDictionary?

调用了dismissViewControllerAnimated,但没有解除ViewController

右对齐的 UITextField 空格键在 iOS 7 中不前进光标

在基于视图的 NSTableView 上更改 Select colored颜色

Xcode:可以为协议接口所需的方法自动创建存根吗?

警告的含义在演示过程中!

如何在表格视图中的两个单元格之间留出空间?

我应该使用 NSUserDefaults 还是 plist 来存储数据?

如何在 iOS 7 启动期间更改状态栏样式

Xcode 在您的 keys 串中找不到此配置文件的有效私有证书/有效密钥对

如何从输入 IB 的 UILabel 文本中插入新行 \n?

从 URL Objective C 获取图像

检测 UITableView 滚动

Xcode 7 警告:目标文件是为比链接更新的 iOS 版本构建的

UICollectionView 断言失败

xcode 5.1 中的 Arm64 架构

UITableViewCell中的文本居中对齐问题

在 iOS 8 中使用 NSMutableAttributedString 的字符串的下划线部分不起作用