Zmienne poziomu klasy statycznej celu-C

143

Mam klasę Film, w której każdy przechowuje unikalny identyfikator. W C #, Javie itp. Mogę zdefiniować statyczny currentID int i za każdym razem, gdy ustawiam ID, mogę zwiększyć currentID, a zmiana następuje na poziomie klasy, a nie na poziomie obiektu. Czy można to zrobić w Objective-C? Bardzo trudno było mi znaleźć odpowiedź na to pytanie.

Cœur
źródło

Odpowiedzi:

158

Opis problemu :

  1. Chcesz, aby klasa ClassA miała zmienną klasy ClassB.
  2. Używasz Objective-C jako języka programowania.
  3. Objective-C nie obsługuje zmiennych klas, tak jak robi to C ++.

Jedna alternatywa :

Symuluj zachowanie zmiennej klasy przy użyciu funkcji Objective-C

  1. Zadeklaruj / Zdefiniuj zmienną statyczną w classA.m, aby była dostępna tylko dla metod classA (i wszystkiego, co umieścisz wewnątrz classA.m).

  2. Zastąp metodę klasy NSObject initialize, aby zainicjować tylko raz zmienną statyczną instancją klasy ClassB.

  3. Będziesz się zastanawiać, dlaczego powinienem nadpisywać metodę inicjalizacji NSObject. Dokumentacja firmy Apple dotycząca tej metody zawiera odpowiedź: „Środowisko wykonawcze wysyła inicjalizację do każdej klasy w programie dokładnie jeden raz tuż przed wysłaniem pierwszej wiadomości z programu do klasy lub klasy, która ją dziedziczy. nie może nigdy zostać wywołane, jeśli klasa nie jest używana.) ".

  4. Zachęcamy do używania zmiennej statycznej w dowolnej klasie / metodzie instancji klasy ClassA.

Przykład kodu :

plik: klasaA.m

static ClassB *classVariableName = nil;

@implementation ClassA

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

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

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

...

@end

Piśmiennictwo :

  1. Zmienne klasowe wyjaśniają porównanie podejść Objective-C i C ++
Albaregar
źródło
3
Czy możesz mieć zmienną statyczną typu ClassA w klasie classA.m?
goatlinks
6
to może być głupie pytanie, ale co z uwolnieniem wspomnianej pamięci? nie ma znaczenia, ponieważ musi żyć tak długo, jak działa aplikacja?
samiq,
1
@samiq, sprawdź Objective-C: Po co zachowywać zmienną statyczną? . Nie można usunąć wskaźnika do obiektu, ale sam obiekt tak. Prawdopodobnie nie chcesz go wypuszczać, ponieważ najprawdopodobniej chcesz go mieć tak długo, jak działa aplikacja, ale zaoszczędzisz pamięć, jeśli ją zwolnisz, więc jeśli wiesz, że już jej nie potrzebujesz, zwolnij to.
ma11hew28,
5
Jeśli istnieje gwarancja, że ​​funkcja initialize () zostanie wywołana tylko raz, dlaczego potrzebujesz warunkowego „if (! NazwaZmiennaKlasy)”?
jb
23
@jamie initializejest wywoływana raz dla każdej klasy (nadklasy przed podklasami), ale jeśli podklasa nie nadpisuje initialize, klasa nadrzędna initializezostanie wywołana ponownie. Dlatego strażnik jest wymagany, jeśli nie chcesz, aby kod był wykonywany dwukrotnie. Zobacz Inicjowanie obiektu klasy w dokumentach Apple Objective-C.
big_m
31

Począwszy od Xcode 8, można zdefiniować właściwości klasy w Obj-C. Zostało to dodane, aby współdziałać ze statycznymi właściwościami Swift.

Objective-C obsługuje teraz właściwości klas, które współdziałają z właściwościami typu Swift. Są one deklarowane jako: @property (class) NSString * someStringProperty ;. Nigdy nie są syntetyzowane. (23891898)

Oto przykład

@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

Następnie możesz uzyskać do niego dostęp w następujący sposób:

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

Oto bardzo interesujący post wyjaśniający, którego użyłem jako odniesienia do edycji tej starej odpowiedzi.


Odpowiedź z 2011 roku: (nie używaj tego, to straszne)

Jeśli naprawdę nie chcesz deklarować zmiennej globalnej, istnieje inna opcja, może niezbyt ortodoksyjna :-), ale działa ... Możesz zadeklarować metodę "get & set" w ten sposób, ze zmienną statyczną w środku:

+ (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;
}

Więc jeśli chcesz uzyskać wartość, po prostu zadzwoń:

NSString *testVal = [MyClass testHolder:nil]

A potem, kiedy chcesz to ustawić:

[MyClass testHolder:testVal]

W przypadku, gdy chcesz mieć możliwość ustawienia tej pseudo-statycznej-zmiennej na nil, możesz zadeklarować testHoldernastępująco:

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

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

    return test;
}

Oraz dwie przydatne metody:

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

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

Mam nadzieję, że to pomoże! Powodzenia.

Gonzalo Larralde
źródło
Fajnie, ale tak naprawdę nie jest to zmienna globalna, ponieważ nie można uzyskać do niej dostępu z innych .mplików i myślę, że dobrze jest, aby była „globalna” w Class.mpliku.
ma11hew28
29

W swoim pliku .m możesz zadeklarować zmienną jako statyczną:

static ClassName *variableName = nil;

Następnie możesz zainicjować go w swojej +(void)initializemetodzie.

Zwróć uwagę, że jest to zwykła zmienna statyczna C i nie jest statyczna w sensie, w jakim rozważają ją Java lub C #, ale da podobne wyniki.

pgb
źródło
16

W swoim pliku .m zadeklaruj globalną zmienną pliku:

static int currentID = 1;

następnie w swojej procedurze init, pamiętaj, że:

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

lub jeśli zajdzie potrzeba zmiany w innym czasie (np. w metodzie openConnection), zwiększ ją tam. Pamiętaj, że nie jest to bezpieczne dla wątków, ponieważ jest, musisz wykonać synchronizację (lub jeszcze lepiej, użyć dodatku atomowego), jeśli mogą wystąpić problemy z wątkami.

Peter N Lewis
źródło
11

Jak powiedział pgb, nie ma „zmiennych klasowych”, tylko „zmienne instancji”. Metoda Object-C zmiennych klasowych to statyczna zmienna globalna wewnątrz pliku .m klasy. „Statyczna” zapewnia, że ​​zmienna nie może być używana poza tym plikiem (tj. Nie może być extern).

Tom Dalling
źródło
3

Oto opcja:

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

Zauważ, że ta metoda będzie jedyną metodą dostępu do identyfikatora, więc będziesz musiał ją jakoś zaktualizować w tym kodzie.

Anonimowy
źródło
2

(Ściśle mówiąc nie jest to odpowiedź na pytanie, ale z mojego doświadczenia wynika, że ​​może być przydatna podczas wyszukiwania zmiennych klasowych)

Metoda klasowa może często odgrywać wiele ról, jakie zmienna klasowa pełniłaby w innych językach (np. Zmieniona konfiguracja podczas testów):

@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

Teraz obiekt klasy MyClswywołuje Resource:changeSomething:ciąg znaków @"Something general"po wywołaniu doTheThing:, ale obiekt wywodzący się MySpecialCasez ciągu @"Something specific".

Jacob Oscarson
źródło
0

Możesz zmienić nazwę klasy na classA.mm i dodać do niej funkcje C ++.

r & D_
źródło
0

Inną możliwością byłoby posiadanie małego NSNumbersingletona podklasy.

Rudolf Adamkovič
źródło