Objective-Cでクラスレベルのプロパティを宣言するにはどうすればよいですか?


205

多分これは明らかですが、Objective-Cでクラスプロパティを宣言する方法がわかりません。

クラスごとにディクショナリをキャッシュし、それをクラスにどのように配置するのか疑問に思います。

回答:


190

プロパティはObjective-Cで特定の意味を持っていますが、静的変数と同等の意味ですか?たとえば、すべてのタイプのFooのインスタンスは1つだけですか?

Objective-Cでクラス関数を宣言するには、-ではなく+プレフィックスを使用するので、実装は次のようになります。

// Foo.h
@interface Foo {
}

+ (NSDictionary *)dictionary;

// Foo.m
+ (NSDictionary *)dictionary {
  static NSDictionary *fooDict = nil;
  if (fooDict == nil) {
    // create dict
  }
  return fooDict;
}

23
これは正解?ディクショナリメソッドの最初の行として、常にfooDictをnilに設定すると、常にディクショナリが毎回再作成されますか?
PapillonUK


59
行static NSDictionary * fooDict = nil; 一度だけ実行されます!複数回呼び出されるメソッドでも、この名前の静的変数が存在する場合、キーワードstaticを使用した宣言(およびこの例では初期化)は無視されます。
Binarian 2013

3
@ BenC.R.Leggieroはい、絶対に。.-accessor構文はObjective-Cの内のプロパティに縛られていない、それはちょうどコンパイル時にショートカットのいずれかの方法のためにその任意の引数を取ることなく、リターンの何かが。この場合、私はそれを好み.ます。私は個人的に、クライアントコードがアクションを実行するのではなく、何かを取得するつもりの構文を好みます(実装コードが何かを作成したり、副作用アクションを実行したりする場合でも)。使用頻度の.高い構文でもコードが読みやすくなります。sの存在は[…]、フェッチが.構文を使用するときに重要なことが行われていることを意味します。
Slipp D. Thompson、2015

4
アレックスNolascoの答えを見て、クラスのプロパティは、Xcodeの8リリース以降利用できます。stackoverflow.com/a/37849467/6666611
n3wbie

112

私はこのソリューションを使用しています:

@interface Model
+ (int) value;
+ (void) setValue:(int)val;
@end

@implementation Model
static int value;
+ (int) value
{ @synchronized(self) { return value; } }
+ (void) setValue:(int)val
{ @synchronized(self) { value = val; } }
@end

そして、私はそれがシングルトンパターンの置き換えとして非常に役立つとわかりました。

使用するには、ドット表記でデータにアクセスします。

Model.value = 1;
NSLog(@"%d = value", Model.value);

3
それは本当にクールです。しかしself、クラスメソッド内ではいったい何を意味するのでしょうか。
トッドリーマン

8
@ToddLehman self、メッセージを受信したオブジェクトです。そしてクラスもオブジェクトなので、この場合selfModel
spooki

6
なぜゲッターが必要なのでしょう@synchronizedか?
Matt Kantor 2014年

1
これは実際にこれが機能するのはかなりクールです。実物とまったく同じように機能する独自のクラスレベルのプロパティを書き込めること。自己の同期は、プロパティ宣言で「アトミック」を使用することと同等であり、「非アトミック」バージョンが必要な場合は省略できると思いますか?また、Appleのデフォルトである「_」を使用してバッキング変数に名前を付けることを検討します。また、getter / setterからself.valueを返す/設定すると無限再帰が発生するため、読みやすさが向上します。私の意見では、これは正しい答えです。
Peter Segerblom、2014年

3
かっこいいですが...静的メンバーを1つ作成するだけの10行のコードですか。なんとハック。Appleはこれを機能にすべきです。
ジョンヘンケル2014

92

WWDC 2016 / XCode 8( LLVMセッション @ 5:05 の新。クラスプロパティは次のように宣言できます。

@interface MyType : NSObject
@property (class) NSString *someString;
@end

NSLog(@"format string %@", MyType.someString);

クラスのプロパティは決して合成されないことに注意してください

@implementation
static NSString * _someString;
+ (NSString *)someString { return _someString; }
+ (void)setSomeString:(NSString *)newString { _someString = newString; }
@end

8
これは、アクセサ宣言の単なる砂糖であることを明示的にする必要があります。お気づきのように、プロパティは合成されていませんstatic。以前と同じように、()変数を宣言して使用し、メソッドを明示的に実装する必要があります。以前にもドット構文は機能していました。全体として、これは実際よりも大きな問題のように聞こえます。
jscs 2016年

6
重要なことは、これは()を使用せずにSwiftコードからシングルトンにアクセスできることを意味し、型のサフィックスは慣例により削除されます。例:XYZMyClass.sharedMyClass()の代わりにXYZMyClass.shared(Swift 3)
Ryan

これはスレッドセーフではありません。これがコード内の別のスレッドから変更される可能性がある場合、これが作成する可能性のある競合状態を確実に処理するようにします。
smileBot

クラスを消費するためのすっきりとしたインターフェースですが、それでも大量の作業です。静的ブロックでこれを試してみましたが、あまり面白くありませんでした。実際、スタティックを使用する方がはるかに簡単でした。
デパルタメントB

1
実装は、dispatch_onceトークンを使用して、スレッドセーフな「シングルトン」動作用に簡単に拡張できますが、それ以外の場合、ソリューションは答えを正しく示します
Motti Shneor

63

クラスレベルでと同等のものを探している場合@property、答えは「そのようなものはありません」です。でも覚えておいて、@propertyてください。とにかく、構文上の砂糖だけです。適切な名前のオブジェクトメソッドを作成するだけです。

他の人が言ったように、わずかに異なる構文しか持たない静的変数にアクセスするクラスメソッドを作成したいとします。


考えられるプロパティも構文です[MyClassクラス]の代わりにMyClass.classのようなものにドット構文を使用できるようにするとよいでしょう。
Zaky German、2012

4
@ZakyGermanできます!UIDevice.currentDevice.identifierForVendor私のために働く。
tc。

1
@tc。ありがとうございました!今愚かな充填。どういうわけか、私は過去にそれを試したと確信しましたが、それはうまくいきませんでした。たぶんそれは新機能ですか?
Zaky German

1
@ZakyGermanクラスメソッドで少なくとも1〜2年は機能しました。ゲッター/セッターメソッドが期待されるタイプを持っている場合、インスタンスメソッドでは常に機能していると思います。
tc。

21

スレッドセーフな方法を次に示します。

// Foo.h
@interface Foo {
}

+(NSDictionary*) dictionary;

// Foo.m
+(NSDictionary*) dictionary
{
  static NSDictionary* fooDict = nil;

  static dispatch_once_t oncePredicate;

  dispatch_once(&oncePredicate, ^{
        // create dict
    });

  return fooDict;
}

これらの編集により、fooDictが1度だけ作成されることが保証されます。

Appleのドキュメントから:「dispatch_once-ブロックオブジェクトを、アプリケーションの存続期間中に一度だけ実行します。」


3
+(NSDictionary *)ディクショナリメソッドの最初の行で静的NSDictionaryを初期化できるため、dispatch_onceコードは無関係ではありませんか?
jcpennypincher 2013年

@jcpennypincher静的宣言と同じ行で辞書を初期化しようとすると、次のコンパイラエラーが発生しますInitializer element is not a compile-time constant
ジョージWS

@GeorgeWS関数の結果(allocとinitは関数です)に初期化しようとしているため、このエラーが発生しています。それをnilに初期化してから、if(obj == nil)を追加してそこで初期化すると、問題ありません。
Robは

1
Rob、それはスレッドセーフではありません。ここに示されているコードが最適です。
Ian Ollmann、2015年

完全でスレッドセーフなので、この回答が一番好きです。ただし、opの問題はクラスプロパティの宣言についてでしたが、実装ではありません(実装とは関係ありません)。とにかくありがとう。
Motti Shneor

11

Xcode 8以降、Objective-Cはクラスプロパティをサポートするようになりました。

@interface MyClass : NSObject
@property (class, nonatomic, assign, readonly) NSUUID* identifier;
@end

クラスプロパティは合成されないため、独自の実装を作成する必要があります。

@implementation MyClass
static NSUUID*_identifier = nil;

+ (NSUUID *)identifier {
  if (_identifier == nil) {
    _identifier = [[NSUUID alloc] init];
  }
  return _identifier;
}
@end

クラス名に通常のドット構文を使用してクラスプロパティにアクセスします。

MyClass.identifier;

7

プロパティは、オブジェクトではなく、クラスではなく値を持っています。

クラスのすべてのオブジェクトについて何かを格納する必要がある場合は、グローバル変数を使用する必要があります。static実装ファイルで宣言することで非表示にできます。

オブジェクト間の特定の関係の使用を検討することもできます。マスターの役割をクラスの特定のオブジェクトに割り当て、他のオブジェクトをこのマスターにリンクします。マスターは、辞書を単純なプロパティとして保持します。Cocoaアプリケーションのビュー階層に使用されているようなツリーを思い浮かべます。

別のオプションは、「クラス」辞書と、この辞書に関連するすべてのオブジェクトのセットの両方で構成される専用クラスのオブジェクトを作成することです。これはNSAutoreleasePoolココアのようなものです。


7

Xcode 8以降では、Berbieが回答したクラスプロパティ属性を使用できます。

ただし、実装では、iVarの代わりに静的変数を使用して、クラスプロパティのクラスゲッターとセッターの両方を定義する必要があります。

Sample.h

@interface Sample: NSObject
@property (class, retain) Sample *sharedSample;
@end

Sample.m

@implementation Sample
static Sample *_sharedSample;
+ ( Sample *)sharedSample {
   if (_sharedSample==nil) {
      [Sample setSharedSample:_sharedSample];
   }
   return _sharedSample;
}

+ (void)setSharedSample:(Sample *)sample {
   _sharedSample = [[Sample alloc]init];
}
@end

2

クラスレベルのプロパティが多数ある場合は、シングルトンパターンが適切です。このようなもの:

// Foo.h
@interface Foo

+ (Foo *)singleton;

@property 1 ...
@property 2 ...
@property 3 ...

@end

そして

// Foo.m

#import "Foo.h"

@implementation Foo

static Foo *_singleton = nil;

+ (Foo *)singleton {
    if (_singleton == nil) _singleton = [[Foo alloc] init];

    return _singleton;
}

@synthesize property1;
@synthesize property2;
@synthesise property3;

@end

次のように、クラスレベルのプロパティにアクセスします。

[Foo singleton].property1 = value;
value = [Foo singleton].property2;

4
このシングルトン実装はスレッドセーフではないため、使用しないでください
klefevre

1
もちろん、それはスレッドセーフではありません。スレッドセーフについて最初に言及したのはあなたであり、デフォルトでは、非スレッドセーフコンテキスト、つまりシングルスレッドでのスレッドセーフオーバーロードは意味がありません。
Pedro Borges、2015

dispatch_onceここで使うのはかなり簡単でしょう。
Ian MacDonald

POは、実装ではなく宣言側での回答を求めていました-提案された実装でさえ不完全です(スレッドセーフではありません)。
Motti Shneor

-3

[このソリューションを試すのは簡単です] Swiftクラスで静的変数を作成し、任意のObjective-Cクラスから呼び出すことができます。


1
OPはSwiftで静的プロパティを作成する方法を尋ねませんでした、これは彼の問題を解決しません。
ネイサンF.

または、問題は解決しましたが、質問には回答しませんでした。それに値するかどうかは
わかり
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.