50

ここでの私の混乱は、「Java の考え方」にとらわれており、この場合の Obj-C の違いを理解していないことが原因であると確信しています。

Java では、次のようにクラスで変数を宣言できます。そのクラスの各インスタンスには独自の変数があります。

MyClass {

  String myVar;

  MyClass() {
    // constructor
  }
}

Obj-C では、次のように .m ファイルでのみ変数を宣言して、同じことを試みました。

#import "MyClass.h"

@implementation MyClass

NSString *testVar;

@end

ここでの私の期待は、この変数のスコープがこのクラスに限定されていることでした。だから私は2番目のクラス(同一)を作成しました:

#import "MySecondClass.h"

@implementation MySecondClass

NSString *testVar;

@end

私が見ている (そして困惑させられている) ことは、1 つのクラスで変数を変更すると、他のクラスで見られる値に影響するということです。実際、ブレークポイントを設定してから変数の「定義にジャンプ」すると、次の場所に移動します

ここで問題を示す非常に小さな Xcode プロジェクトを作成しました。

4

4 に答える 4

139

これを変える:

@implementation MyClass

NSString *testVar;

@end

に:

@implementation MyClass {
    NSString *testVar;
}

// methods go here

@end

そして、あなたはあなたが期待したものを手に入れるでしょう。

あなたがそれを持っていたように、あなたは実際にグローバル変数を作成しています。2つのグローバル変数はリンカーによって1つに結合されたため、1つを設定すると両方が変更されました。中括弧内の変数は、適切な(そしてプライベートな)インスタンス変数になります。

編集:明白な理由もなく反対票を投じられた後、私は物事の「古い」方法と新しい方法を指摘したいと思いました。

古い方法:

SomeClass.h

@interface SomeClass : UIViewController <UITextFieldDelegate> {
    UITextField *_textField;
    BOOL _someBool;
}

@property (nonatomic, assign) BOOL someBool;

// a few method declarations

@end

SomeClass.m

@implementation SomeClass

@synthesize someBool = _someBool;

// the method implementations

@end

現在、最新のObjective-Cコンパイラを使用した新しく改善された方法:

SomeClass.h

@interface SomeClass : UIViewController

@property (nonatomic, assign) BOOL someBool;

// a few method declarations

@end

SomeClass.m

@interface SomeClass () <UITextFieldDelegate>
@end

@implementation SomeClass {
    UITextField *_textField;
}

// the method implementations

@end

新しい方法にはいくつかの利点があります。主な利点は、クラスに関する実装固有の詳細が.hファイルに表示されないことです。クライアントは、実装に必要なデリゲートを知る必要はありません。クライアントは、私が使用しているivarを知る必要はありません。これで、実装に新しいivarが必要な場合、または新しいプロトコルを使用する必要がある場合、.hファイルは変更されません。これは、再コンパイルされるコードが少なくなることを意味します。それはよりクリーンではるかに効率的です。また、編集が簡単になります。.mファイルを編集していて、新しいivarが必要であることに気付いたら、すでに編集しているのと同じ.mファイルに変更を加えます。前後に交換する必要はありません。

@synthesizeまた、実装にはivarまたはプロパティが必要なくなったことにも注意してください。

于 2012-11-07T04:57:39.863 に答える
0

おそらく必要なのは (非常に古い OS とコンパイラを使用していない限り)、単にプロパティ構文を使用することです。すなわち:

@interface MyClass : NSObject

// method declarations here ...

@property (copy) NSString*    myVar;

// ... or here.

@end

これにより、意図したことが実行されます。これにより、インスタンス変数と、この変数の getter/setter ペアが暗黙的に合成されます。インスタンス変数を手動で作成したい場合 (コードが非常に古い MacOS バージョンで動作する必要がない限り、通常は必要ありません)、上記のコードが ivar を作成するために内部で行うことは次のとおりです。

@interface MyClass : NSObject
{
    NSString*    _myVar;
}

// method declarations here.

@end

中かっこに注意してください。これは、これがメソッド間のどこかにある単なるグローバル変数ではなく、実際にはこのオブジェクトに属するインスタンス変数であることをコンパイラに伝えます。

内部使用のためだけにプロパティを作成していて、クラスのクライアントがそれを台無しにしたくない場合は、クラスを「継続」するクラス拡張を使用して、最も古い ObjC コンパイラ以外のすべてでこれを少し隠すことができます。ヘッダーからの宣言ですが、ヘッダーとは別に配置できます (通常は実装ファイル内)。クラス拡張は、名前のないカテゴリのように見えます:

@interface MyClass ()

@property (copy) NSString*    myVar;

@end

そこにプロパティ宣言を入れることも、ivar 宣言を入れることもできます (ここでも中括弧で囲みます)。クラス インターフェイスと同じプロパティを宣言し、readonlyそれを同じように再宣言することもできreadwriteますが、拡張機能のように、クライアントはそれを読み取るだけで、コードはそれを変更できます。

ARC を使用しなかった場合 (つまり、自動参照カウントのデフォルトをオフにした場合)、メソッドですべてのプロパティを に設定する必要があることに注意しnilてくださいdealloc(それらが に設定されている場合を除きますweak) assign。 .

注意 - 上記はすべて@interfaceセクションです。実際のコードは別々@implementationのセクションに分けられます。これは、ヘッダー ファイル ( .h) をクラスのクライアントに渡して、使用する予定の部分だけを含めることができるようにするためです。また、実装ファイル ( .m) に実装の詳細を隠して、誰かを心配することなく変更することができます。誤ってそれらを使用した可能性があり、他のコードを壊してしまいます。

PS -NSStrings不変フレーバーが必要であるが、可変フレーバー (つまり、NSMutableString) にも存在するその他のオブジェクトは、常にcopyプロパティである必要があります。これは、NSMutableString を NSString に変換して、外部の誰も可変オブジェクトを変更できないようにするためです。あなたの下の文字列。他のすべてのオブジェクト タイプでは、通常はstrong( retainARC でない場合は) を使用します。クラスの所有者 (デリゲートなど) には、通常weak( assignARC でない場合は) を使用します。

于 2015-05-09T09:44:22.067 に答える
-1

Javaの場合

MyClass {

  String myVar;
  MyClass() {
    // constructor
  }
}

Objective-cで

MyClass.h

@interface MyClass : NSObject{

      NSString* str; // Declaration
}
@end

MyClass.m

@implementation MyClass

  -(void)initializieTheString
  {
     //Defination 
  }

@end
于 2012-11-07T09:11:57.800 に答える
-1

Objective-C では、次のようにして、変数をプライベートとして定義します。

MyClass.h

@interface MyClass : NSObject{

      NSString* _myTestVar; // Declaration
}
@end

MyClass.m のようにして、実装クラスで参照します。

#import "MyClass.h";

@implementation MyClass

  -(void)initializieTheString
  {
     _myTestVar= @"foo"; //Initialization

  }

@end
于 2015-05-09T08:18:06.407 に答える