将实例变量放入@implementation块或类扩展中的能力是“现代Objective-C运行时”的一项功能,每个版本的iOS和64位Mac OS X程序都使用该功能。
如果要编写32位Mac OS X应用程序,则必须将实例变量放在@interface声明中。不过,您可能不需要支持应用程序的32位版本。自五年前发布的10.5版(Leopard)以来,OS X支持64位应用程序。
因此,假设您仅在编写将使用现代运行时的应用程序。您应该将ivars放在哪里?
选项0:在@interface(请勿执行)中
首先,让我们去了,为什么我们不希望把实例变量在@interface声明。
将实例变量放入可以@interface向类用户公开实现的详细信息。这可能导致这些用户(甚至在使用自己的类时甚至是您自己!)也要依赖他们不应该使用的实现细节。(这与我们是否声明ivars无关)@private。
将实例变量放入make中@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(一个相当大的类),我们可能会决定要将管理可重用视图(单元格和补充视图)队列的代码放在单独的源文件中。我们可以通过将这些消息分成一个类别来做到这一点:
@interface UICollectionView : UIScrollView
- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@property (nonatomic, retain) UICollectionView *collectionViewLayout;
@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方法,UICollectionView.m并且可以在中实现管理可重用视图的方法UICollectionView+ReusableViews.m,这使我们的源代码更易于管理。
但是我们可重用的视图管理代码需要一些实例变量。这些变量都被暴露在主类@implementation中UICollectionView.m,所以编译器将在发出这些.o文件。并且我们还需要将这些实例变量暴露给中的代码UICollectionView+ReusableViews.m,以便这些方法可以使用ivars。
这是我们需要类扩展的地方。我们可以将reusable-view-management ivars放在私有头文件的类扩展中:
@interface UICollectionView () {
NSMutableDictionary *registeredCellSources;
NSMutableDictionary *spareCellsByIdentifier;
NSMutableDictionary *registeredSupplementaryViewSources;
NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}
- (void)initReusableViewSupport;
@end
我们不会将此头文件发送给我们库的用户。我们只是将其导入inUICollectionView.m和in UICollectionView+ReusableViews.m,以便需要查看这些ivars的所有内容都可以看到它们。我们还抛出了一个方法,我们希望maininit方法可以调用该方法来初始化reusable-view-management代码。我们将调用该方法从-[UICollectionView initWithFrame:collectionViewLayout:]在UICollectionView.m,我们会实现它UICollectionView+ReusableViews.m。