类实例变量的Objective-C模式?

时间:2022-09-07 07:42:48

What would be a nice pattern in Objective-C for class variables that can be "overridden" by subclasses?

在Objective-C中,对于可以被子类“覆盖”的类变量,这将是一个很好的模式吗?

Regular Class variables are usually simulated in Objective-C using a file-local static variables together with exposed accessors defined as Class methods.

常规类变量通常使用文件本地静态变量和定义为Class方法的公开访问器在Objective-C中进行模拟。

However, this, as any Class variables, means the value is shared between the class and all its subclasses. Sometimes, it's interesting for the subclass to change the value for itself only. This is typically the case when Class variables are used for configuration.

但是,这与任何类变量一样,意味着该值在类及其所有子类之间共享。有时,子类只更改自身的值是有趣的。这通常是使用类变量进行配置时的情况。

Here is an example: in some iOS App, I have many objects of a given common abstract superclass (Annotation) that come in a number of concrete variations (subclasses). All annotations are represented graphically with a label, and the label color must reflect the specific kind (subclass) of its annotation. So all Foo annotations must have a green label, and all Bar annotations must have a blue label. Storing the label color in each instance would be wasteful (and in reality, perhaps impossible as I have many objects, and actual configuration data - common to each instance - is far larger than a single color).

这是一个例子:在一些iOS应用程序中,我有许多给定的常见抽象超类(Annotation)的对象,它们有许多具体的变体(子类)。所有注释都使用标签以图形方式表示,标签颜色必须反映其注释的特定种类(子类)。因此,所有Foo注释都必须具有绿色标签,并且所有Bar注释必须具有蓝色标签。在每个实例中存储标签颜色将是浪费的(实际上,可能不可能,因为我有许多对象,并且实际配置数据 - 每个实例共有 - 远远大于单个颜色)。

At runtime, the user could decide that all Foo annotations now will have a red label. And so on.

在运行时,用户可以决定现在所有Foo注释都将具有红色标签。等等。

Since in Objective-C, Classes are actual objects, this calls for storing the Foo label color in the Foo class object. But is that even possible? What would be a good pattern for this kind of things? Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.

因为在Objective-C中,Classes是实际对象,所以这要求在Foo类对象中存储Foo标签颜色。但这甚至可能吗?对于这种事情,什么是好的模式?当然,可以定义某种将类映射到其配置值的全局字典,但这有点难看。

2 个解决方案

#1


1  

Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.

当然,可以定义某种将类映射到其配置值的全局字典,但这有点难看。

Why do you think this would be ugly? It is a very simple approach since you can use [self className] as the key in the dictionary. It is also easy to make it persistent since you can simply store the dictionary in NSUserDefaults (as long as it contains only property-list objects). You could also have each class default to its superclass's values by calling the superclass method until you find a class with a value.

为什么你认为这会很难看?这是一种非常简单的方法,因为您可以使用[self className]作为字典中的键。由于您可以简单地将字典存储在NSUserDefaults中(只要它只包含属性列表对象),因此也很容易使其持久化。您还可以通过调用超类方法将每个类默认为其超类的值,直到找到具有值的类。

+ (id)classConfigurationForKey:(NSString *)key {
    if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
    Class c = [self class];
    id value = nil;
    while(value == nil) {
        NSDictionary *classConfig = [_configurationDict objectForKey:[c className]];
        if(classConfig) {
            value = [classConfig objectForKey:key];
        }
        c = [c superclass];
    }
    return value;
}
+ (void)setClassConfiguration:(id)value forKey:(NSString *)key {
    if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
    NSMutableDictionary *classConfig = [_configurationDict objectForKey:[self className]];
    if(classConfig == nil) {
        classConfig = [NSMutableDictionary dictionary];
        [_configurationDict setObject:classConfig forKey:[self className]];
    }
    [classConfig setObject:value forKey:key];
}

This implementation provides no checking to make sure you don't go over the top superclass, so you will need to ensure that there is a value for that class to avoid an infinite loop.

此实现不提供检查以确保您不会超过*超类,因此您需要确保该类有一个值以避免无限循环。

If you want to store objects which can't be stored in a property list, you can use a method to convert back and forth when you access the dictionary. Here is an example for accessing the labelColor property, which is a UIColor object.

如果要存储无法存储在属性列表中的对象,可以使用方法在访问字典时来回转换。以下是访问labelColor属性的示例,该属性是UIColor对象。

+ (UIColor *)classLabelColor {
    NSData *data = [self classConfigurationForKey:@"labelColor"];
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
+ (void)setClassLabelColor:(UIColor *)color {
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:color];
    [self setClassConfiguration:data forKey:@"labelColor"];
}

#2


0  

my answer here may help:

我的回答可能会有所帮助:

What is the recommended method of styling an iOS app?

设计iOS应用程序的推荐方法是什么?

in that case, your annotation just holds a reference to a style (e.g. you need only one per style), and the size of a pointer for an entire style is not bad. either way, that post may give you some ideas.

在这种情况下,您的注释只包含对样式的引用(例如,每个样式只需要一个),并且整个样式的指针大小也不错。无论哪种方式,该帖子可能会给你一些想法。

Update

Jean-Denis Muys: That addresses the sample use case of my question, but not my question itself (a pattern to simulate class instance variables).

Jean-Denis Muys:这解决了我的问题的示例用例,但不是我的问题本身(模拟类实例变量的模式)。

you're right, i didn't know how closely your example modeled your problem and i considered commenting on that.

你是对的,我不知道你的例子与你的问题建模有多紧密,我考虑过对此进行评论。

for a more general and reusable solution, i'd probably just write a threadsafe global dictionary if your global data is nontrivial (as you mentioned in your OP). you could either populate it in +initialize or lazily by introducing a class method. then you could add a few categories to NSObject to access and mutate the static data -- do this for syntactical ease.

对于更通用和可重用的解决方案,如果您的全局数据非常重要(正如您在OP中提到的那样),我可能只会写一个线程安全的全局字典。您可以通过引入类方法在+ initialize或lazily中填充它。那么你可以在NSObject中添加一些类别来访问和改变静态数据 - 这样做很容易理解语法。

i suppose the good thing about that approach is that you can reuse it in any program (even though it may appear ugly or complex to write). if that's too much locking, then you may want to divide dictionaries by prefixes or create a simple thread safe dictionary which your class holds a reference to -- you can then synthesize an instance variable via the objc runtime to store it and declare an instance method to access it. the class method would still have to use the global data interface directly.

我认为这种方法的好处在于你可以在任何程序中重复使用它(即使它可能看起来很丑或很复杂)。如果锁定太多,那么你可能想用前缀来划分字典或者创建一个简单的线程安全字典,你的类可以引用它 - 你可以通过objc运行时合成一个实例变量来存储它并声明一个实例方法访问它。类方法仍然必须直接使用全局数据接口。

#1


1  

Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.

当然,可以定义某种将类映射到其配置值的全局字典,但这有点难看。

Why do you think this would be ugly? It is a very simple approach since you can use [self className] as the key in the dictionary. It is also easy to make it persistent since you can simply store the dictionary in NSUserDefaults (as long as it contains only property-list objects). You could also have each class default to its superclass's values by calling the superclass method until you find a class with a value.

为什么你认为这会很难看?这是一种非常简单的方法,因为您可以使用[self className]作为字典中的键。由于您可以简单地将字典存储在NSUserDefaults中(只要它只包含属性列表对象),因此也很容易使其持久化。您还可以通过调用超类方法将每个类默认为其超类的值,直到找到具有值的类。

+ (id)classConfigurationForKey:(NSString *)key {
    if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
    Class c = [self class];
    id value = nil;
    while(value == nil) {
        NSDictionary *classConfig = [_configurationDict objectForKey:[c className]];
        if(classConfig) {
            value = [classConfig objectForKey:key];
        }
        c = [c superclass];
    }
    return value;
}
+ (void)setClassConfiguration:(id)value forKey:(NSString *)key {
    if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
    NSMutableDictionary *classConfig = [_configurationDict objectForKey:[self className]];
    if(classConfig == nil) {
        classConfig = [NSMutableDictionary dictionary];
        [_configurationDict setObject:classConfig forKey:[self className]];
    }
    [classConfig setObject:value forKey:key];
}

This implementation provides no checking to make sure you don't go over the top superclass, so you will need to ensure that there is a value for that class to avoid an infinite loop.

此实现不提供检查以确保您不会超过*超类,因此您需要确保该类有一个值以避免无限循环。

If you want to store objects which can't be stored in a property list, you can use a method to convert back and forth when you access the dictionary. Here is an example for accessing the labelColor property, which is a UIColor object.

如果要存储无法存储在属性列表中的对象,可以使用方法在访问字典时来回转换。以下是访问labelColor属性的示例,该属性是UIColor对象。

+ (UIColor *)classLabelColor {
    NSData *data = [self classConfigurationForKey:@"labelColor"];
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
+ (void)setClassLabelColor:(UIColor *)color {
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:color];
    [self setClassConfiguration:data forKey:@"labelColor"];
}

#2


0  

my answer here may help:

我的回答可能会有所帮助:

What is the recommended method of styling an iOS app?

设计iOS应用程序的推荐方法是什么?

in that case, your annotation just holds a reference to a style (e.g. you need only one per style), and the size of a pointer for an entire style is not bad. either way, that post may give you some ideas.

在这种情况下,您的注释只包含对样式的引用(例如,每个样式只需要一个),并且整个样式的指针大小也不错。无论哪种方式,该帖子可能会给你一些想法。

Update

Jean-Denis Muys: That addresses the sample use case of my question, but not my question itself (a pattern to simulate class instance variables).

Jean-Denis Muys:这解决了我的问题的示例用例,但不是我的问题本身(模拟类实例变量的模式)。

you're right, i didn't know how closely your example modeled your problem and i considered commenting on that.

你是对的,我不知道你的例子与你的问题建模有多紧密,我考虑过对此进行评论。

for a more general and reusable solution, i'd probably just write a threadsafe global dictionary if your global data is nontrivial (as you mentioned in your OP). you could either populate it in +initialize or lazily by introducing a class method. then you could add a few categories to NSObject to access and mutate the static data -- do this for syntactical ease.

对于更通用和可重用的解决方案,如果您的全局数据非常重要(正如您在OP中提到的那样),我可能只会写一个线程安全的全局字典。您可以通过引入类方法在+ initialize或lazily中填充它。那么你可以在NSObject中添加一些类别来访问和改变静态数据 - 这样做很容易理解语法。

i suppose the good thing about that approach is that you can reuse it in any program (even though it may appear ugly or complex to write). if that's too much locking, then you may want to divide dictionaries by prefixes or create a simple thread safe dictionary which your class holds a reference to -- you can then synthesize an instance variable via the objc runtime to store it and declare an instance method to access it. the class method would still have to use the global data interface directly.

我认为这种方法的好处在于你可以在任何程序中重复使用它(即使它可能看起来很丑或很复杂)。如果锁定太多,那么你可能想用前缀来划分字典或者创建一个简单的线程安全字典,你的类可以引用它 - 你可以通过objc运行时合成一个实例变量来存储它并声明一个实例方法访问它。类方法仍然必须直接使用全局数据接口。