在“现代"中将 iVar 放在哪里?目标-C? [英] Where to put iVars in "modern" Objective-C?

查看:27
本文介绍了在“现代"中将 iVar 放在哪里?目标-C?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Ray Wenderlich 的iOS6 by Tutorials"一书有一个非常好的章节,讲述了编写更现代"的 Objective-C 代码.在一个部分中,这些书描述了如何将 iVar 从类的头文件移动到实现文件中.由于所有 iVar 都应该是私有的,这似乎是正确的做法.

The book "iOS6 by Tutorials" by Ray Wenderlich has a very nice chapter about writing more "modern" Objective-C code. In one section the books describes how to move iVars from the header of the class into the implementation file. Since all iVars should be private this seems to be the right thing to do.

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

But so far I found 3 ways of doing so. Everyone is doing it differently.

1.) 将 iVars 放在 @implementantion 下的花括号块内(这就是书中的做法).

1.) Put iVars under @implementantion inside a block of curly braces (This is how it is done in the book).

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

2.) Put iVars under @implementantion without block of curly braces

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

3.) Put iVars inside private Interface above the @implementantion (a class extension)

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

All these solutions seems to work fine and so far I haven't noticed any difference in the behavior of my application. I guess there is no "right" way of doing it but I need to write some tutorials and I want to choose only one way for my code.

我应该走哪条路?

我在这里只谈论 iVar.不是属性.对象仅需要其自身且不应暴露于外部的附加变量.

代码示例

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 块或类扩展中的能力是现代目标"的一个特性-C runtime",每个版本的 iOS 和 64 位 Mac OS X 程序都使用它.

The ability to put instance variables in the @implementation block, or in a class extension, is a feature of the "modern Objective-C runtime", which is used by every version of iOS, and by 64-bit Mac OS X programs.

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

If you want to write 32-bit Mac OS X apps, you must put your instance variables in the @interface declaration. Chances are you don't need to support a 32-bit version of your app, though. OS X has supported 64-bit apps since version 10.5 (Leopard), which was released over five years ago.

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

So, let's assume you are only writing apps that will use the modern runtime. Where should you put your ivars?

首先,让我们回顾一下为什么我们想要将实例变量放在 @interface 声明中.

First, let's go over why we don't want to put instance variables in an @interface declaration.

  1. 将实例变量放入 @interface 会将实现的细节暴露给类的用户.这可能会导致那些用户(甚至在使用自己的类时您自己!)依赖于他们不应该依赖的实现细节.(这与我们是否声明 ivars @private 无关.)

  1. Putting instance variables in an @interface exposes details of the implementation to users of the class. This may lead those users (even yourself when using your own classes!) to rely on implementation details that they should not. (This is independent of whether we declare the ivars @private.)

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

Putting instance variables in an @interface makes compiling take longer, because any time we add, change, or remove an ivar declaration, we have to recompile every .m file that imports the interface.

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

So we don't want to put instance variables in the @interface. Where should we put them?

接下来,让我们讨论您的选项 2,将 iVars 放在 @implementantion 下,不要使用花括号块".这声明实例变量!你说的是这个:

Next, let's discuss your option 2, "Put iVars under @implementantion without block of curly braces". This does not declare instance variables! You are talking about this:

@implementation Person

int age;
NSString *name;

...

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

That code defines two global variables. It does not declare any instance variables.

如果您需要全局变量,可以在 .m 文件中定义全局变量,甚至在 @implementation 中定义全局变量 - 例如,因为您想要所有共享某些状态的实例,例如缓存.但是你不能使用这个选项来声明 ivars,因为它没有声明 ivars.(此外,您的实现私有的全局变量通常应声明为 static 以避免污染全局命名空间并冒链接时错误的风险.)

It's fine to define global variables in your .m file, even in your @implementation, if you need global variables - for example, because you want all of your instances to share some state, like a cache. But you can't use this option to declare ivars, because it doesn't declare ivars. (Also, global variables private to your implementation should usually be declared static to avoid polluting the global namespace and risking link-time errors.)

剩下的选项 1 和 3.

That leaves your options 1 and 3.

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

Usually we want to use option 1: put them in your main @implementation block, in braces, like this:

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

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

We put them here because it keeps their existence private, preventing the problems I described earlier, and because there's usually no reason to put them in a class extension.

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

So when do we want to use your option 3, putting them in a class extension?

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

There's almost never a reason to put them in a class extension in the same file as the class's @implementation. We might as well just put them in the @implementation in that case.

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

But occasionally we might write a class that's big enough that we want to divide up its source code into multiple files. We can do that using categories. For example, if we were implementing UICollectionView (a rather big class), we might decide that we want to put the code that manages the queues of reusable views (cells and supplementary views) in a separate source file. We could do that by separating out those messages into a category:

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

OK, now we can implement the main UICollectionView methods in UICollectionView.m and we can implement the methods that manage reusable views in UICollectionView+ReusableViews.m, which makes our source code a little more manageable.

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

But our reusable view management code needs some instance variables. Those variables have to be exposed to the main class @implementation in UICollectionView.m, so the compiler will emit them in the .o file. And we also need to expose those instance variables to the code in UICollectionView+ReusableViews.m, so those methods can use the ivars.

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

This is where we need a class extension. We can put the reusable-view-management ivars in a class extension in a private header file:

// 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<中实现它/代码>.

We won't ship this header file to users of our library. We'll just import it in UICollectionView.m and in UICollectionView+ReusableViews.m, so that everything that needs to see these ivars can see them. We've also thrown in a method that we want the main init method to call to initialize the reusable-view-management code. We'll call that method from -[UICollectionView initWithFrame:collectionViewLayout:] in UICollectionView.m, and we'll implement it in UICollectionView+ReusableViews.m.

这篇关于在“现代"中将 iVar 放在哪里?目标-C?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆