首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在“现代”Objective-C中把iVars放在哪里?

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

Stack Overflow用户
提问于 2012-11-26 22:26:35
回答 5查看 14K关注 0票数 83

Ray Wenderlich的"iOS6 by Tutorials“一书中有一个非常好的章节,讲述了如何编写更”现代“的Objective-C代码。在其中一节中,这两本书描述了如何将iVars从类头移动到实现文件中。既然所有的iVars都应该是私有的,那么这样做似乎是正确的。

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

1.)将iVars放在@implementantion下的一个大括号块中(书中就是这样做的)。

2.)将iVars放在@implementantion下,不带大括号

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

所有这些解决方案似乎都工作得很好,到目前为止,我还没有注意到我的应用程序的行为有任何不同。我想没有“正确”的方法,但我需要写一些教程,我只想为我的代码选择一种方法。

我该往哪边走呢?

编辑:我在这里只谈论iVars。不是属性。对象只需要额外的变量,而这些变量不应该暴露给外部。

代码示例

1)

代码语言:javascript
复制
#import "Person.h"

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

- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

2)

代码语言:javascript
复制
#import "Person.h"

@implementation Person

int age;
NSString *name;


- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

3)

代码语言:javascript
复制
#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
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-11-27 05:12:25

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

如果您想编写32位Mac应用程序,则必须将实例变量放入@interface声明中。不过,你可能不需要支持32位版本的应用程序。OS从五年前发布的10.5 (Leopard)版本开始支持64位应用程序。

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

选项0:在@interface中(不这样做)

首先,让我们来看看为什么我们不想将实例变量放在@interface声明中。

  1. 将实例变量放在@interface中会向类的用户公开实现的详细信息。这可能会导致这些用户(甚至您自己在使用自己的类时也是如此!)依赖于他们不应该依赖的实现细节。(这与我们是否在@interface中声明ivar @private.)
  2. Putting实例变量无关,这会使编译花费更长的时间,因为每当我们添加、更改或删除ivar声明时,我们都必须重新编译每个导入接口的.m文件。

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

选项2:在不带大括号的@implementation中(不要这么做)

接下来,让我们讨论一下选项2,“将iVars放在@implementantion下,不带大括号”。这不是声明实例变量,而是!您正在谈论以下内容:

代码语言:javascript
复制
@implementation Person

int age;
NSString *name;

...

该代码定义了两个全局变量。它没有声明任何实例变量。

如果您需要全局变量,那么可以在.m文件中定义全局变量,即使在@implementation中也是如此-例如,因为您希望所有实例共享某种状态,比如缓存。但是你不能使用这个选项来声明ivars,因为它没有声明ivars。(此外,您的实现专用的全局变量通常应该声明为static,以避免污染全局名称空间和冒链接时错误的风险。)

这就只剩下选项1和3了。

选项1:在@implementation中使用大括号(执行)

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

代码语言:javascript
复制
@implementation Person {
    int age;
    NSString *name;
}

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

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

选项3:在类扩展中(仅在必要时这样做)

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

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

代码语言:javascript
复制
// 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中的代码,以便这些方法可以使用ivars。

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

代码语言:javascript
复制
// UICollectionView_ReusableViewsSupport.h

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

    NSMutableDictionary *registeredSupplementaryViewSources;
    NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}

- (void)initReusableViewSupport;

@end

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

票数 163
EN

Stack Overflow用户

发布于 2012-11-27 04:11:32

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

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

选择是将实例变量放在头文件中还是放在实现文件中。使用头文件的优点是,您可以使用快捷的键盘快捷键(Xcode中的Command+ Control + Up )来查看和编辑实例变量和接口声明。

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

在某些情况下,实现实例变量是很好的选择,但对于我的大多数代码,我仍然将实例变量放在头中,因为作为一个在Xcode中工作的程序员,这对我来说更方便。我的建议是做你觉得对你更方便的事情。

票数 5
EN

Stack Overflow用户

发布于 2012-11-26 22:31:46

这在很大程度上与ivar对子类的可见性有关。子类将不能访问@implementation块中定义的实例变量。

对于我计划分发的可重用代码(例如,库或框架代码),我不喜欢公开实例变量以供公共检查,然后我倾向于将ivars放在实现块中(您的选择1)。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13566862

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档