【问题标题】:Giving each subclass its own copy of a class variable为每个子类提供自己的类变量副本
【发布时间】:2012-01-13 15:05:12
【问题描述】:

我的 iOS 应用程序中有以下类(它就像 Java 世界中的抽象类)。

@implementation WSObject

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

...

@end

然后我有多个类使用类方法dictionaryWSObject 上实现这个。问题是,这些类中的每一个都应该有自己的_dictionary,但它们都共享来自超类的同一个对象。当然,我可以复制到所有子类,但这会破坏可重用性。除了这个 getter,WSObject 中还有其他类方法可以改变字典。因此,每个子类中都应该有几个类方法。

我怎样才能巧妙地解决这个问题?如果我的描述不充分,请告诉我。

【问题讨论】:

    标签: objective-c inheritance subclassing class-variables


    【解决方案1】:

    Associative references 似乎他们会成功。您基本上可以在类对象本身上添加一些存储。 (我在这里使用NSStrings,代替您要使用的字典,仅用于演示。)

    超类:

    #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 SubClassVariables[36254:903] 叫我 Ishmael。

    这里唯一的缺点是每次需要对象时都必须调用访问器方法;您没有可以直接使用的指针。当然,您也可以将超类中的objc_getAssociatedObject 作为访问器调用,因为它可以访问key

    【讨论】:

    • @Chris:谢谢,解决这个问题很有趣。
    • 聪明。我喜欢使用“真正的”内置 Objective-C 方法来包装类名和反射到 NSDictionary 的想法。大概这样也快得多。
    【解决方案2】:

    为了给每个子类自己的字典,使用类名作为键在主字典中存储第二个字典对象。例如:

    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];
    }
    

    【讨论】:

    • 不需要[self class],只需self,除非你想从元类中创建一个字符串。
    • @JoshCaswell:你说得对,self 在这里就足够了,但有一点更正:+[NSObject class] 返回接收者,而不是接收者的元类。要获得元类,必须使用运行时 API 巫术。
    • @Chuck:哎呀。我收回了我评论的那部分。
    【解决方案3】:

    也许您可以返回字典的副本

    @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
    

    【讨论】:

      猜你喜欢
      • 2018-07-13
      • 2011-03-24
      • 2016-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-16
      • 1970-01-01
      相关资源
      最近更新 更多