83

RayWenderlichによる本「iOS6byTutorials」には、より「現代的な」Objective-Cコードの記述に関する非常に優れた章があります。あるセクションでは、本はiVarsをクラスのヘッダーから実装ファイルに移動する方法を説明しています。すべてのiVarはプライベートである必要があるため、これは正しいことのようです。

しかし、これまでのところ、私はそうするための3つの方法を見つけました。みんな違うやり方でやっています。

1.)中括弧のブロック内の@implementantionの下にiVarsを配置します(これは本で行われている方法です)。

2.)中括弧のブロックなしでiVarsを@implementantionの下に置きます

3.)iVarsを@implementantion(クラス拡張)の上のプライベートインターフェイス内に配置します

これらのソリューションはすべて正常に機能しているようで、これまでのところ、アプリケーションの動作に違いはありません。それを行う「正しい」方法はないと思いますが、いくつかのチュートリアルを作成する必要があり、コードに対して1つの方法のみを選択したいと思います。

どちらに行けばいいですか?

編集:ここでは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
4

5 に答える 5

163

インスタンス変数を@implementationブロックまたはクラス拡張に配置する機能は、iOSのすべてのバージョンおよび64ビットMacOSXプログラムで使用される「最新のObjective-Cランタイム」の機能です。

32ビットのMacOSXアプリを作成する場合は、インスタンス変数を@interface宣言に含める必要があります。ただし、32ビットバージョンのアプリをサポートする必要はない可能性があります。OS Xは、5年以上前にリリースされたバージョン10.5(Leopard)以降、64ビットアプリをサポートしています。

したがって、最新のランタイムを使用するアプリのみを作成していると仮定しましょう。ivarをどこに置くべきですか?

オプション0:(@interfaceしないでください)

まず、インスタンス変数を宣言に入れたくない理由を見ていきましょう。@interface

  1. インスタンス変数を@interface公開に入れると、実装の詳細がクラスのユーザーに公開されます。これにより、それらのユーザー(独自のクラスを使用している場合でも!)は、実装の詳細に依存する必要がなくなる可能性があります。(これは、ivarsを宣言するかどうかとは関係ありません@private。)

  2. インスタンス変数をに@interface入れると、コンパイルに時間がかかります。これは、ivar宣言を追加、変更、または削除するたびに.m、インターフェイスをインポートするすべてのファイルを再コンパイルする必要があるためです。

したがって、インスタンス変数をに入れたくありません@interface。どこに置けばいいの?

オプション2:@implementation中かっこなし(しないでください)

次に、オプション2「中括弧のブロックなしでiVarsを@implementantionの下に置く」について説明しましょう。これはインスタンス変数を宣言しません!あなたはこれについて話している:

@implementation Person

int age;
NSString *name;

...

そのコードは2つのグローバル変数を定義します。インスタンス変数は宣言しません。

.mグローバル変数が必要な場合は、ファイル内であっても、ファイル内でグローバル変数を定義する@implementationことは問題ありません。たとえば、すべてのインスタンスでキャッシュなどの状態を共有する必要があるためです。ただし、このオプションはivarを宣言しないため、このオプションを使用してivarを宣言することはできません。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

これで、でメインUICollectionViewメソッドをUICollectionView.m実装でき、で再利用可能なビューを管理するメソッドを実装できるようになりましたUICollectionView+ReusableViews.m。これにより、ソースコードがもう少し管理しやすくなります。

ただし、再利用可能なビュー管理コードには、いくつかのインスタンス変数が必要です。これらの変数はのメインクラス@implementationに公開する必要UICollectionView.mがあるため、コンパイラはそれらを.oファイルに出力します。また、これらのインスタンス変数をのコードに公開してUICollectionView+ReusableViews.m、これらのメソッドがivarを使用できるようにする必要もあります。

ここでクラス拡張が必要です。reusable-view-management ivarsをプライベートヘッダーファイルのクラス拡張子に入れることができます:

// UICollectionView_ReusableViewsSupport.h

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

    NSMutableDictionary *registeredSupplementaryViewSources;
    NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}

- (void)initReusableViewSupport;

@end

このヘッダーファイルをライブラリのユーザーに出荷することはありません。これらのivarを表示する必要があるすべてのものがそれらを表示できるようUICollectionView.mに、インポートするだけです。また、mainメソッドが再利用可能なビュー管理コードを初期化するために呼び出すメソッドをスローしました。からそのメソッドを呼び出し、で実装します。UICollectionView+ReusableViews.minit-[UICollectionView initWithFrame:collectionViewLayout:]UICollectionView.mUICollectionView+ReusableViews.m

于 2012-11-26T21:12:25.423 に答える
5

オプション2は完全に間違っています。これらはグローバル変数であり、インスタンス変数ではありません。

オプション1と3は基本的に同じです。まったく違いはありません。

選択は、インスタンス変数をヘッダーファイルに配置するか実装ファイルに配置するかです。ヘッダーファイルを使用する利点は、インスタンス変数とインターフェイス宣言を表示および編集するためのすばやく簡単なキーボードショートカット(XcodeではCommand + Control + Up)があることです。

欠点は、クラスのプライベートな詳細をパブリックヘッダーで公開することです。これは望ましくない場合があります。特に、他の人が使用するコードを作成している場合はそうです。もう1つの潜在的な問題は、Objective-C ++を使用している場合、ヘッダーファイルにC++データ型を入れないようにすることをお勧めします。

実装インスタンス変数は特定の状況に最適なオプションですが、ほとんどのコードでは、Xcodeで作業するコーダーとしての方が便利であるという理由だけで、インスタンス変数をヘッダーに配置しています。私のアドバイスは、あなたがあなたにとってより便利だと思うことは何でもすることです。

于 2012-11-26T20:11:32.083 に答える
4

主に、サブクラスに対するivarの可視性と関係があります。@implementationサブクラスは、ブロックで定義されたインスタンス変数にアクセスできなくなります。

配布する予定の再利用可能なコード(ライブラリやフレームワークコードなど)で、インスタンス変数を公開検査に公開したくない場合は、実装ブロック(オプション1)にivarを配置する傾向があります。

于 2012-11-26T14:31:46.420 に答える
3

インスタンス変数は、実装の上のプライベートインターフェイスに配置する必要があります。オプション3。

これについて読むべきドキュメントは、Objective-Cでのプログラミングガイドです。

ドキュメントから:

プロパティなしでインスタンス変数を定義できます

値または別のオブジェクトを追跡する必要があるときはいつでも、オブジェクトのプロパティを使用することをお勧めします。

プロパティを宣言せずに独自のインスタンス変数を定義する必要がある場合は、次のように、クラスインターフェイスまたは実装の上部にある中括弧内にそれらを追加できます。

于 2012-11-26T14:30:59.337 に答える
1

パブリックivarは、実際には@interfaceでプロパティとして宣言する必要があります(おそらく1で考えていることです)。最新のXcodeを実行していて、最新のランタイム(64ビットOS XまたはiOS)を使用している場合、プライベートivarは、クラス拡張ではなく@implementation(2)で宣言できます。 3で考え直します。

于 2012-11-26T14:31:07.623 に答える