[解決済み] モダンな "Objective-C "でiVarsをどこに置くか?
質問
Ray Wenderlich による書籍 "iOS6 by Tutorials" には、より現代的な Objective-C コードを書くための非常に素晴らしい章があります。この本のあるセクションで、iVarsをクラスのヘッダから実装ファイルに移動する方法が説明されています。 すべてのiVarsはprivateであるべきなので、これは正しいことのように思われます。
しかし、今のところ、私はそれを行う3つの方法を見つけました。誰もが異なる方法でそれを行っています。
1.) iVarsを@implementantionの下に中括弧で囲む(本ではこうなっている)。
2.) 中括弧のない@implementantionの下にiVarsを入れる。
3.) iVarsを@implementantionの上のprivate Interfaceの中に入れる(クラスの拡張)。
これらの解決策はすべてうまくいくようで、今のところ、私のアプリケーションの動作に違いは見られません。 私は、それを行うための正しい方法はないと思いますが、いくつかのチュートリアルを書く必要があり、私のコードのための唯一の方法を選択したいと思います。
どのようにすればよいのでしょうか。
編集:私はここでiVarsについてだけ話しています。プロパティではありません。オブジェクトがそれ自身のためにのみ必要とし、外部に公開されるべきではない追加の変数のみです。
コードサンプル
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
ブロック、またはクラス拡張にインスタンス変数を置く機能は、「最新の Objective-C ランタイム」の機能であり、iOS のすべてのバージョンと 64 ビット Mac OS X プログラムで使用されています。
32 ビットの Mac OS X アプリケーションを書きたい場合は、インスタンス変数を
@interface
宣言で指定する必要があります。 しかし、32 ビット版のアプリをサポートする必要はない可能性が高いです。 OS X は、5 年以上前にリリースされたバージョン 10.5 (Leopard) から 64 ビット アプリケーションをサポートしています。
では、最新のランタイムを使用するアプリだけを書くと仮定しましょう。 どこにアイバーを置くべきでしょうか。
オプション 0
@interface
(しないでください)
まず、なぜ
をしないのか
にインスタンス変数を置きたいのか、その理由を説明します。
@interface
宣言の中にインスタンス変数を入れたくありません。
-
インスタンス変数を
@interface
に置くと、そのクラスのユーザに実装の詳細が公開されます。 これは、そのようなユーザ(あなた自身のクラスを使っているときでさえも!)を、彼らがすべきでない実装の詳細に依存させるかもしれません。 (このことは、私たちがアイバー@private
.) -
インスタンス変数を
@interface
に置くと、コンパイルに時間がかかります。なぜなら、ivar 宣言を追加、変更、削除するたびに、すべての.m
ファイルを再コンパイルする必要があるからです。
ですから、インスタンス変数を
@interface
. どこに置けばいいのでしょうか?
オプション2: ファイル中の
@implementation
を中括弧なしで書く (Don't Do It)
次に、選択肢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のivarを置くことができます。
// UICollectionView_ReusableViewsSupport.h
@interface UICollectionView () {
NSMutableDictionary *registeredCellSources;
NSMutableDictionary *spareCellsByIdentifier;
NSMutableDictionary *registeredSupplementaryViewSources;
NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}
- (void)initReusableViewSupport;
@end
このヘッダーファイルは、私たちのライブラリのユーザには配布しません。 ただ、このヘッダファイルを
UICollectionView.m
で、そして
UICollectionView+ReusableViews.m
になるように、すべての
が必要とする
が見ることができるようにします。 また、メインとなる
init
メソッドに投げました。このメソッドは、再利用可能なビュー管理コードを初期化するために呼び出されます。 このメソッドは
-[UICollectionView initWithFrame:collectionViewLayout:]
の中で
UICollectionView.m
で実装し、それを
UICollectionView+ReusableViews.m
.
関連
-
[解決済み] Objective-CでNSStringsを連結するためのショートカット集
-
[解決済み] Objective-Cの定数
-
[解決済み] Objective-Cで乱数を発生させる
-
[解決済み] Objective-Cで、ある文字列が他の文字列を含んでいるかどうかを調べるにはどうすればよいですか?
-
[解決済み] Objective-Cのtypedef enumとは何ですか?
-
[解決済み] SwiftからObjective-Cのコードを呼び出すにはどうしたらいいですか?
-
[解決済み] Objective-Cでデリゲートを作成するにはどうしたらいいですか?
-
[解決済み] Objective-Cで、オブジェクトの種類をテストするにはどうしたらいいですか?
-
[解決済み] NSObject +loadと+initialize - これらは何をするのですか?
-
[解決済み] NSSetからNSMutableArrayを返す方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Objective-CのクラスでSwiftのプロトコルをインポートする
-
[解決済み] Objective-Cのセレクタ?
-
[解決済み] switch文の中で変数を宣言する [重複]。
-
[解決済み] キーチェーンのアイテムは何がユニークなのか(iOSの場合)?
-
[解決済み] Swift の外部読み取り専用、内部読み書き可能なプロパティ
-
[解決済み] Swift ネイティブベースクラスまたは NSObject
-
[解決済み] AppDelegateからストーリーボード上の異なる場所で条件付きで開始する。
-
[解決済み] NSSetからNSMutableArrayを返す方法
-
[解決済み] Category is implementing method which will be implemented by its primary class" の警告を抑制する。
-
[解決済み] ViewController の respondsToSelector: 割り当て解除されたインスタンスにメッセージが送信される (CRASH)