Предоставление каждому подклассу своей собственной копии переменной класса - PullRequest
4 голосов
/ 06 декабря 2011

В моем приложении для iOS есть следующий класс (он похож на абстрактный класс из мира Java).

@implementation WSObject

static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
    if (_dictionary == nil) {
        _dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
    }
    return _dictionary;
}

...

@end

У меня есть несколько классов, которые реализуют это выше WSObject с помощью метода класса dictionary. Проблема в том, что каждый из этих классов должен иметь свой собственный _dictionary, но все они совместно используют один и тот же объект из суперкласса. Я мог бы, конечно, скопировать все подклассы, но это нарушило бы возможность повторного использования. Помимо этого геттера, в WSObject есть другие методы класса, которые изменяют словарь. По этой причине в каждом подклассе должно быть несколько методов класса.

Как я могу решить это умным способом? Пожалуйста, скажите мне, если мое описание недостаточно.

Ответы [ 3 ]

9 голосов
/ 06 декабря 2011

Ассоциативные ссылки похоже, что они добьются цели. Вы можете по существу привязать некоторое хранилище к самому объекту класса. (Я использую NSString здесь вместо словарей, которые вы хотите использовать, только для демонстрации.)

суперкласс:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface Stuper : NSObject

// Accessor method for the "class variable"
+ (NSString *) str;
// Analog to your +localStorePath
+ (NSString *) quote;

@end

#import "Stuper.h"

// The doc suggests simply using the address of a static variable as the key.
// This works fine, even though every class is (as in your problem) using
// the same key, because we are associating to a different class each time.
static char key;    
@implementation Stuper

+ (NSString *) str {
    NSString * s = objc_getAssociatedObject(self, &key);
    if( !s ){
        s = [self quote];
        // You'll probably want to use OBJC_ASSOCIATION_RETAIN for your dictionary.
        // self inside a class method is the class object; use that as
        // the associator. The string is now tied to the associator, i.e.,
        // has the same lifetime.
        objc_setAssociatedObject(self, &key, s, OBJC_ASSOCIATION_COPY);
    }
    return s;
}

+ (NSString *) quote {
    return @"It was the best of times, it was the worst of times.";
}

@end

<ч /> <ч />

Подкласс:

#import "Stuper.h"
@interface Stub : Stuper @end

#import "Stub.h"

@implementation Stub

+ (NSString *) quote {
    return @"Call me Ishmael.";
}

@end

<ч /> <ч />

Пробуем это:

#import <Foundation/Foundation.h>
#import "Stuper.h"
#import "Stub.h"

int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSLog(@"%@", [Stuper str]);
    NSLog(@"%@", [Stub str]);

    [pool drain];
    return 0;
}

Каждый объект класса теперь имеет свою собственную строку, связанную с ним.

2011-12-05 23: 11: 09.031 SubClassVariables [36254: 903] Это были лучшие времена, это были худшие времена.
2011-12-05 23: 11: 09.034 Переменные подкласса [36254: 903] Зовите меня Измаил.

Единственным недостатком здесь является то, что вам придется вызывать метод доступа каждый раз, когда вы хотите объект; у вас нет указателя, который вы можете использовать напрямую. Вы можете также вызвать objc_getAssociatedObject в суперклассе в качестве средства доступа, поскольку он имеет доступ к key.

7 голосов
/ 06 декабря 2011

Чтобы дать каждому подклассу свой собственный словарь, сохраните второй объект словаря в своем основном словаре, используя имя класса в качестве ключа. Например:

static NSMutableDictionary *_dictionary = nil;

+ (NSDictionary*)dictionary 
{
    if (_dictionary == nil) 
        _dictionary = [[NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]] mutableCopy];

    NSString *key = NSStringFromClass( [self class] );

    if ( [_dictionary objectForKey:key] == nil )
        [_dictionary setObject:[NSMutableDictionary dictionary] forKey:key];

    return [_dictionary objectForKey:key];
}
2 голосов
/ 06 декабря 2011

Возможно, вы сможете вернуть копию словаря

@implementation WSObject

static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
    if (_dictionary == nil) {
        _dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
    }
    return [_dictionary copy];
}

...

@end

Имейте в виду, что если вы измените _dictionary, вы получите копию этого измененного словаря, которая может отличаться от того, что находится на диске.

Как часто это называется? действительно ли необходимо кэшировать содержимое файла в этом статическом _dictionary объекте?

Почему бы просто не извлекать его каждый раз с диска, предполагая, что производительность не слишком часто ставится под сомнение.

@implementation WSObject

+(NSDictionary*) dictionary {
    return [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}

...

@end
...