116

iOS アプリと Objective C の作業を開始して以来、変数を宣言および定義できるさまざまな場所に本当に困惑してきました。一方では従来の C アプローチを採用し、他方ではその上に OO を追加する新しい ObjectiveC ディレクティブを採用しています。これらの場所を変数に使用し、おそらく現在の理解を修正したいベストプラクティスと状況を理解するのを手伝ってくれませんか?

サンプル クラス (.h および .m) を次に示します。

#import <Foundation/Foundation.h>

// 1) What do I declare here?

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

// 3) class-specific method / property declarations

@end

#import "SampleClass.h"

// 4) what goes here?

@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

@implementation SampleClass
{
    // 6) define ivars
}

// 7) define methods and synthesize properties from both public and private
//    interfaces

@end
  • 1 と 4 についての私の理解では、これらは C スタイルのファイルベースの宣言と定義であり、クラスの概念をまったく理解していないため、C で使用される方法とまったく同じように使用する必要があります。以前は静的変数ベースのシングルトンを実装するために使用されていました。私が見逃している他の便利な用途はありますか?
  • iOS での作業からの私の見解は、@synthesize ディレクティブの外では ivar がほぼ完全に段階的に廃止されたため、ほとんど無視できるということです。そうですか?
  • 5 について: プライベート インターフェイスでメソッドを宣言する必要があるのはなぜですか? 私のプライベート クラス メソッドは、インターフェイスで宣言しなくても問題なくコンパイルできるようです。主に読みやすさのためですか?

たくさんありがとう、皆さん!

4

4 に答える 4

154

あなたの混乱は理解できます。特に、Xcode と新しい LLVM コンパイラの最近の更新により、ivar とプロパティを宣言できる方法が変更されたためです。

「最新の」Objective-C (「古い」Obj-C 2.0) が登場する前は、多くの選択肢がありませんでした。インスタンス変数は、中括弧の間のヘッダーで宣言されていました{ }

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@end

これらの変数には実装でのみアクセスできましたが、他のクラスからはアクセスできませんでした。そのためには、次のようなアクセサ メソッドを宣言する必要がありました。

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}

- (int)myVar;
- (void)setMyVar:(int)newVar;

@end


// MyClass.m
@implementation MyClass

- (int)myVar {
   return myVar;
}

- (void)setMyVar:(int)newVar {
   if (newVar != myVar) {
      myVar = newVar;
   }
}

@end

このようにして、通常の角括弧構文を使用してメッセージを送信する (メソッドを呼び出す) ことにより、他のクラスからもこのインスタンス変数を取得および設定することができました。

// OtherClass.m
int v = [myClass myVar];  // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];

すべてのアクセサー メソッドを手動で宣言して実装するのは非常に面倒で@propertyあり@synthesize、アクセサー メソッドを自動的に生成するために導入されたためです。

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@synthesize myVar;
@end

その結果、より明確で短いコードになります。アクセサー メソッドが実装され、以前と同様にブラケット構文を引き続き使用できます。さらに、ドット構文を使用してプロパティにアクセスすることもできます。

// OtherClass.m
int v = myClass.myVar;   // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;

Xcode 4.4 以降、インスタンス変数を自分で宣言する必要がなくなり、スキップすることもでき@synthesizeます。ivar を宣言しない場合、コンパイラは ivar を追加し、. を使用しなくてもアクセサ メソッドも生成します@synthesize

自動生成された ivar のデフォルト名は、アンダースコアで始まる名前またはプロパティです。生成されたivarの名前は、次を使用して変更できます@synthesize myVar = iVarName;

// MyClass.h
@interface MyClass : NSObject 
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@end

これは、上記のコードとまったく同じように機能します。互換性の理由から、引き続き ivar をヘッダーで宣言できます。しかし、それを行う (プロパティを宣言しない) 唯一の理由は、プライベート変数を作成することであるため、実装ファイルでもそれを行うことができます。これが推奨される方法です。

実装ファイル内の@interfaceブロックは実際には拡張機能であり、メソッドの宣言を転送したり (不要になった)、プロパティを (再) 宣言したりするために使用できます。readonlyたとえば、ヘッダーでプロパティを宣言できます。

@property (nonatomic, readonly) myReadOnlyVar;

readwriteivarへの直接アクセスだけでなく、プロパティ構文を使用して設定できるように、実装ファイルで再宣言します。

@interfaceanyまたはブロックの完全に外側で変数を宣言することに関しては@implementation、はい、それらはプレーンな C 変数であり、まったく同じように機能します。

于 2012-09-28T01:38:19.547 に答える
44

まず、@DrummerB の回答を読んでください。その理由と、一般的に何をすべきかについての良い概要です。それを念頭に置いて、あなたの特定の質問に:

#import <Foundation/Foundation.h>

// 1) What do I declare here?

実際の変数定義はここには入りません (自分が何をしているのかを正確に知っていれば、技術的には合法ですが、絶対にこれを行ってはいけません)。他にもいくつかの種類のものを定義できます。

  • typdef
  • 列挙
  • 外部者

Externs は変数宣言のように見えますが、実際には別の場所で宣言するという約束にすぎません。ObjC では、定数を宣言するためにのみ使用し、通常は文字列定数のみを使用する必要があります。例えば:

extern NSString * const MYSomethingHappenedNotification;

次に、.mファイルで実際の定数を宣言します。

NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

DrummerB が指摘したように、これはレガシーです。ここには何も置かないでください。


// 3) class-specific method / property declarations

@end

うん。


#import "SampleClass.h"

// 4) what goes here?

上記の外部定数。ファイルの静的変数もここに入れることができます。これらは、他の言語のクラス変数に相当します。


@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

はい


@implementation SampleClass
{
    // 6) define ivars
}

しかし、めったにありません。ほとんどの場合、clang (Xcode) が変数を作成できるようにする必要があります。例外は通常、ObjC 以外の ivar (Core Foundation オブジェクト、特に ObjC++ クラスの場合は C++ オブジェクト)、または奇妙なストレージ セマンティクスを持つ ivar (何らかの理由でプロパティと一致しない ivar など) の周りにあります。


// 7) define methods and synthesize properties from both public and private
//    interfaces

通常、@synthesize はもう行うべきではありません。Clang (Xcode) が代わりにやってくれるので、任せてください。

ここ数年で、物事は劇的にシンプルになりました。副作用として、3 つの異なる時代 (壊れやすい ABI、壊れにくい ABI、壊れにくい ABI + 自動合成) が存在するようになりました。そのため、古いコードを見ると、少し混乱する可能性があります。したがって、単純さから生じる混乱:D

于 2012-09-28T01:51:29.277 に答える
6

これは、Objective-Cで宣言されているすべての種類の変数の例です。変数名はそのアクセスを示します。

ファイル:Animal.h

@interface Animal : NSObject
{
    NSObject *iProtected;
@package
    NSObject *iPackage;
@private
    NSObject *iPrivate;
@protected
    NSObject *iProtected2; // default access. Only visible to subclasses.
@public
    NSObject *iPublic;
}

@property (nonatomic,strong) NSObject *iPublic2;

@end

ファイル:Animal.m

#import "Animal.h"

// Same behaviour for categories (x) than for class extensions ().
@interface Animal(){
@public
    NSString *iNotVisible;
}
@property (nonatomic,strong) NSObject *iNotVisible2;
@end

@implementation Animal {
@public
    NSString *iNotVisible3;
}

-(id) init {
    self = [super init];
    if (self){
        iProtected  = @"iProtected";
        iPackage    = @"iPackage";
        iPrivate    = @"iPrivate";
        iProtected2 = @"iProtected2";
        iPublic     = @"iPublic";
        _iPublic2    = @"iPublic2";

        iNotVisible   = @"iNotVisible";
        _iNotVisible2 = @"iNotVisible2";
        iNotVisible3  = @"iNotVisible3";
    }
    return self;
}

@end

iNotVisible変数は、他のクラスからは表示されないことに注意してください。これは可視性の問題であるため、宣言する@property@public変更しないでください。

コンストラクター内では、副作用を回避するために、@property代わりにアンダースコアを使用して宣言された変数にアクセスすることをお勧めします。self

変数にアクセスしてみましょう。

ファイル:Cow.h

#import "Animal.h"
@interface Cow : Animal
@end

ファイル:Cow.m

#import "Cow.h"
#include <objc/runtime.h>

@implementation Cow

-(id)init {
    self=[super init];
    if (self){
        iProtected    = @"iProtected";
        iPackage      = @"iPackage";
        //iPrivate    = @"iPrivate"; // compiler error: variable is private
        iProtected2   = @"iProtected2";
        iPublic       = @"iPublic";
        self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private

        //iNotVisible   = @"iNotVisible";  // compiler error: undeclared identifier
        //_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier
        //iNotVisible3  = @"iNotVisible3"; // compiler error: undeclared identifier
    }
    return self;
}
@end

ランタイムを使用して、表示されていない変数に引き続きアクセスできます。

ファイル:Cow.m(パート2)

@implementation Cow(blindAcess)

- (void) setIvar:(NSString*)name value:(id)value {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    object_setIvar(self, ivar, value);
}

- (id) getIvar:(NSString*)name {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    id thing = object_getIvar(self, ivar);
    return thing;
}

-(void) blindAccess {
    [self setIvar:@"iNotVisible"  value:@"iMadeVisible"];
    [self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"];
    [self setIvar:@"iNotVisible3" value:@"iMadeVisible3"];
    NSLog(@"\n%@ \n%@ \n%@",
          [self getIvar:@"iNotVisible"],
          [self getIvar:@"_iNotVisible2"],
          [self getIvar:@"iNotVisible3"]);
}

@end

表示されていない変数にアクセスしてみましょう。

ファイル:main.m

#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        Cow *cow = [Cow new];
        [cow performSelector:@selector(blindAccess)];
    }
}

このプリント

iMadeVisible 
iMadeVisible2 
iMadeVisible3

_iNotVisible2サブクラス専用のバッキングivarにアクセスできたことに注意してください。Objective-Cでは、マークされている変数も含めて、すべての変数を読み取ったり設定したりできます@private。例外はありません。

関連するオブジェクトやC変数は別の鳥であるため、含めませんでした。C変数に関しては、外部で定義された変数、@interface X{}または@implementation X{}ファイルスコープと静的ストレージを持つC変数です。

メモリ管理属性、または読み取り専用/読み取り/書き込み、ゲッター/セッター属性については説明しませんでした。

于 2013-02-16T02:13:17.057 に答える
6

私もかなり新しいので、何も台無しにしないことを願っています。

1 & 4: C スタイルのグローバル変数: ファイル全体のスコープがあります。2 つの違いは、ファイル全体であるため、最初のヘッダーはヘッダーをインポートするすべてのユーザーが使用でき、2 番目のヘッダーは使用できないことです。

2: インスタンス変数。ほとんどのインスタンス変数は、プロパティを使用してアクセサーを介して合成され、取得/設定されます。これにより、メモリ管理が簡単になり、理解しやすいドット表記が得られるためです。

6: 実装 ivar はやや新しいものです。パブリックヘッダーで必要なものだけを公開したいので、プライベートivarを置くのに適した場所ですが、サブクラスはAFAIKを継承しません。

3 & 7: パブリック メソッドとプロパティの宣言、次に実装。

5: プライベート インターフェイス。物事をクリーンに保ち、ある種のブラック ボックス効果を生み出すために、私は常にプライベート インターフェイスを使用します。彼らがそれについて知る必要がない場合は、そこに置いてください。読みやすさのためにも行っていますが、他に理由があるかどうかはわかりません。

于 2012-09-28T01:23:55.437 に答える