将实例变量放在@implementation
块中或类扩展中的能力是“现代 Objective-C 运行时”的一个特性,每个版本的 iOS 和 64 位 Mac OS X 程序都使用它。
如果要编写 32 位 Mac OS X 应用程序,则必须将实例变量放在@interface
声明中。不过,您可能不需要支持 32 位版本的应用程序。自 5 年前发布的 10.5 (Leopard) 版本以来,OS X 就支持 64 位应用程序。
因此,假设您只编写将使用现代运行时的应用程序。你应该把你的 ivars 放在哪里?
选项 0:在@interface
(不要这样做)
首先,让我们回顾一下为什么我们不想将实例变量放在@interface
声明中。
将实例变量放在 an@interface
中会向该类的用户公开实现的细节。这可能会导致那些用户(甚至在使用自己的类时您自己!)依赖于他们不应该依赖的实现细节。(这与我们是否声明 ivars 无关@private
。)
将实例变量放入 an@interface
使得编译时间更长,因为任何时候我们添加、更改或删除 ivar 声明,我们都必须重新编译每个.m
导入接口的文件。
所以我们不想将实例变量放在@interface
. 我们应该把它们放在哪里?
选项 2:在@implementation
没有大括号的情况下(不要这样做)
接下来,让我们讨论您的选项 2,“将 iVars 放在 @implementantion 下,不要使用花括号块”。这不声明实例变量!你在谈论这个:
@implementation Person
int age;
NSString *name;
...
该代码定义了两个全局变量。它不声明任何实例变量。
.m
如果您需要全局变量,即使在您的文件中定义全局变量也很好@implementation
- 例如,因为您希望所有实例共享某些状态,例如缓存。但是你不能使用这个选项来声明 ivars,因为它没有声明 ivars。(此外,您的实现私有的全局变量通常应该被声明static
以避免污染全局命名空间和链接时错误的风险。)
这留下了您的选择 1 和 3。
选项 1:@implementation
带大括号(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
好的,现在我们可以实现 in 中的主要UICollectionView
方法,UICollectionView.m
并且可以实现管理可重用视图的方法UICollectionView+ReusableViews.m
,这使我们的源代码更易于管理。
但是我们的可重用视图管理代码需要一些实例变量。这些变量必须暴露给 中的主类@implementation
,因此编译器将在文件UICollectionView.m
中发出它们。.o
而且我们还需要将这些实例变量暴露给 中的代码UICollectionView+ReusableViews.m
,以便这些方法可以使用 ivars。
这是我们需要类扩展的地方。我们可以将可重用视图管理 ivars 放在私有头文件中的类扩展中:
// UICollectionView_ReusableViewsSupport.h
@interface UICollectionView () {
NSMutableDictionary *registeredCellSources;
NSMutableDictionary *spareCellsByIdentifier;
NSMutableDictionary *registeredSupplementaryViewSources;
NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}
- (void)initReusableViewSupport;
@end
我们不会将此头文件发送给我们库的用户。我们只需将其导入 inUICollectionView.m
和 in UICollectionView+ReusableViews.m
,以便需要查看这些 ivars 的所有内容都可以看到它们。我们还引入了一个方法,我们希望 maininit
方法调用该方法来初始化可重用视图管理代码。我们将从-[UICollectionView initWithFrame:collectionViewLayout:]
in调用该方法UICollectionView.m
,并在 in 中实现它UICollectionView+ReusableViews.m
。