Objective-C静的クラスレベル変数


143

Filmクラスがあり、それぞれに一意のIDが保存されています。C#、Javaなどでは、静的なint currentIDを定義できます。IDを設定するたびに、currentIDを増やすことができ、オブジェクトレベルではなくクラスレベルで変更が発生します。これはObjective-Cで実行できますか?これに対する答えを見つけるのは非常に困難です。

回答:


158

問題の説明

  1. ClassAにClassBクラス変数を設定します。
  2. プログラミング言語としてObjective-Cを使用しています。
  3. Objective-Cは、C ++のようにクラス変数をサポートしていません。

1つの代替

Objective-Cの機能を使用してクラス変数の動作をシミュレートする

  1. classA.m内で静的変数を宣言/定義して、classAメソッド(およびclassA.m内に配置したすべてのもの)のみがアクセスできるようにします。

  2. NSObject initialize classメソッドを上書きして、静的変数をClassBのインスタンスで一度だけ初期化します。

  3. NSObjectのinitializeメソッドをなぜ上書きする必要があるのか​​疑問に思われるでしょう。このメソッドに関するAppleのドキュメントの答えは次のとおりです。「ランタイムは、クラスまたはそのクラスから継承するクラスがプログラム内から最初のメッセージを送信する直前に、プログラム内の各クラスに初期化を1回送信します(したがって、メソッドクラスが使用されていない場合、呼び出されることはありません。) "

  4. ClassAクラス/インスタンスメソッド内で静的変数を自由に使用してください。

コードサンプル

ファイル:classA.m

static ClassB *classVariableName = nil;

@implementation ClassA

...
 
+(void) initialize
{
    if (! classVariableName)
        classVariableName = [[ClassB alloc] init];
}

+(void) classMethodName
{
    [classVariableName doSomething]; 
}

-(void) instanceMethodName
{
    [classVariableName doSomething]; 
}

...

@end

参照

  1. Objective-CとC ++のアプローチを比較してクラス変数を説明

3
classA.m内にClassAタイプの静的変数を設定できますか?
goatlinks 2010年

6
これはばかげた質問かもしれませんが、言った記憶の解放についてはどうですか?アプリが実行されている限り存続する必要があるため、問題ではありませんか?
samiq

1
@ samiq、Objective-Cを確認してください:なぜ静的変数を保持するのですか?。オブジェクトへのポインタは削除できませんが、オブジェクト自体は削除できます。アプリが実行されている間はほとんどの場合それを残しておきたいので、おそらく解放したくありませんが、解放するとメモリを節約できるので、もう必要ないことがわかっている場合は、それをリリースする。
ma11hew28

5
initialize()が1回だけ呼び出されることが保証されている場合、条件付きの「if(!classVariableName)」が必要なのはなぜですか?
jb

23
@jamie initializeは各クラス(サブクラスの前のスーパークラス)ごとに1回呼び出されますがinitialize、サブクラスがをオーバーライドしない場合、親クラスinitializeが再度呼び出されます。したがって、そのコードを2回実行したくない場合は、ガードが必要です。AppleのObjective-Cドキュメントの「クラスオブジェクトの初期化」を参照してください。
big_m

31

Xcode 8以降、Obj-Cでクラスプロパティを定義できます。これは、Swiftの静的プロパティと相互運用するために追加されました。

Objective-Cは、Swiftタイプのプロパティと相互運用するクラスプロパティをサポートするようになりました。@property(クラス)NSString * someStringProperty;として宣言されています。それらは決して合成されません。(23891898)

ここに例があります

@interface YourClass : NSObject

@property (class, nonatomic, assign) NSInteger currentId;

@end

@implementation YourClass

static NSInteger _currentId = 0;

+ (NSInteger)currentId {
    return _currentId;
}

+ (void)setCurrentId:(NSInteger)newValue {
    _currentId = newValue;
}

@end

その後、次のようにアクセスできます。

YourClass.currentId = 1;
val = YourClass.currentId;

これは、私がこの古い回答を編集するための参照として使用した非常に興味深い説明記事です。


2011回答:(これは使わないでください、ひどいです)

本当にグローバル変数を宣言したくない場合は、別のオプションがあり、おそらくあまりオーソドックスではないかもしれませんが:-)機能します...静的変数を使用して、次のような「get&set」メソッドを宣言できます。

+ (NSString*)testHolder:(NSString*)_test {
    static NSString *test;

    if(_test != nil) {
        if(test != nil)
            [test release];
        test = [_test retain];
    }

    // if(test == nil)
    //     test = @"Initialize the var here if you need to";

    return test;
}

したがって、値を取得する必要がある場合は、次のように呼び出します。

NSString *testVal = [MyClass testHolder:nil]

そして、それを設定したい場合:

[MyClass testHolder:testVal]

この疑似静的変数をnilに設定したい場合は、次のように宣言できますtestHolder

+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
    static NSString *test;

    if(shouldSet) {
        if(test != nil)
            [test release];
        test = [_test retain];
    }

    return test;
}

そして2つの便利な方法:

+ (NSString*)test {
    return [MyClass testHolderSet:NO newValue:nil];
}

+ (void)setTest:(NSString*)_test {
    [MyClass testHolderSet:YES newValue:_test];
}

それが役に立てば幸い!幸運を。


かっこいいですが、他の.mファイルからアクセスできないため、実際にはグローバル変数ではありませんClass.m。ファイル内で「グローバル」にしても問題ないと思います。
ma11hew28

29

.mファイルでは、変数を静的として宣言できます。

static ClassName *variableName = nil;

その後、+(void)initializeメソッドで初期化できます。

これは単純なC静的変数であり、JavaまたはC#が静的であると見なすという意味では静的ではありませんが、同様の結果が得られることに注意してください。


16

.mファイルで、ファイルグローバル変数を宣言します。

static int currentID = 1;

それからあなたのinitルーチンで、それを参照してください:

- (id) init
{
    self = [super init];
    if (self != nil) {
        _myID = currentID++; // not thread safe
    }
    return self;
}

または、別のときに変更する必要がある場合(たとえば、openConnectionメソッド内)、そこでインクリメントします。現状ではスレッドセーフではないことに注意してください。スレッド化の問題が発生する可能性がある場合は、同期化を行う必要があります(さらに、アトミックな追加を使用する必要があります)。


11

pgbが言ったように、「クラス変数」はなく、「インスタンス変数」だけがあります。クラス変数を行う目的Cの方法は、クラスの.mファイル内の静的グローバル変数です。「静的」は、変数がそのファイルの外部で使用できないことを保証します(つまり、externにすることはできません)。


3

ここにオプションがあります:

+(int)getId{
    static int id;
    //Do anything you need to update the ID here
    return id;
}

このメソッドはidにアクセスする唯一のメソッドになるため、このコードで何らかの方法で更新する必要があることに注意してください。


2

(厳密に言えば質問への回答ではありませんが、私の経験では、クラス変数を探すときに役立つ可能性があります)

多くの場合、クラスメソッドは、クラス変数が他の言語で果たす多くの役割を果たします(たとえば、テスト中に構成が変更されます)。

@interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
@end
@implementation
+ (NSString*)theNameThing { return @"Something general"; }
- (void)doTheThing {
  [SomeResource changeSomething:[self.class theNameThing]];
}
@end

@interface MySpecialCase: MyCls
@end
@implementation
+ (NSString*)theNameThing { return @"Something specific"; }
@end

さて、クラスのオブジェクトMyClsの呼び出しResource:changeSomething:文字列で@"Something general"の呼び出し時にdoTheThing:、しかしから派生したオブジェクトMySpecialCaseの文字列を持ちます@"Something specific"


0

クラス名をclassA.mmに変更して、C ++機能を追加できます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.