objc-class-enum
为 Objective-C 提供具有类属性的更优雅的枚举。或者如果你愿意,也可以是单例。
在不支持此类构造的语言中,创建更易于表达的平台类似于枚举的结构的一种常见方法是使用 静态 或类属性来封装枚举逻辑。其思想是作为单例公开封装数据对象的具体实例。这种方法有一些优点,因为它消除了需要具有关联值的数组和字典或使用大量的 switch
语句的需要,因为所有数据都被封装在特定的实例中。此外,它可以使使用此类 类枚举 的代码更加易于表达和阅读。这些都不是特定于 Objective-C 的。
@interface Color : NSObject
@property (class, readonly) Color *red;
@property (class, readonly, weak) Color *green;
@end
在 Objective-C 中,这种方法的问题在于需要编写大量的样板代码来设置类属性并实现附加功能,例如遍历枚举成员。如果只需要设置很少的单例,则这可能是可以管理的,但经常存在枚举成员数量为数十甚至数百的情况。编写 50 个以上的看起来都相同的 getter/setter 并为映射其他值到枚举成员设置数组或字典可能会变得繁琐。
这个小库试图去除这些样板代码。我想要一个函数调用就能完成设置样板代码的工作。幸运的是,这是 Objective-C,因此我们可以使用运行时来实现这一点。
设置类枚举
+ (void)initialize {
assert(class_createEnum(self));
}
您只需要使用 class_createEnum
函数。它将执行以下操作:
- 设置 getter 实现
- 如果属性是
readwrite
,则设置 setter 实现 - 设置一个包含所有枚举成员的
NSHashTable
属性,我们可以使用该属性遍历枚举成员。
BOOL class_createEnum(Class cls);
class_createEnum
将认为满足以下条件的属性作为枚举的成员
- 属性是
class
类属性 - 属性是
@dynamic
- 属性类型是类的一个实例
@interface Color : NSObject
@property (class) Color *red;
@end
@implementation Color
@dynamic red;
@end
遍历成员
@property (class, readonly) NSHashTable<Color *> *allColors;
此外,如果 class_createEnum
遇到满足以下条件的属性,它将设置一个 所有成员 属性
- 属性是
class
类属性 - 属性是
readonly
- 属性是
@dynamic
- 属性类型是
NSHashTable *
- 属性被命名为以下之一:
all
、allMembers
、allValues
或all[ClassName]s
(例如,对于Color
类,是allColors
)
@interface Color : NSObject
@property (class, readonly) NSHashTable<Color *> *allColors;
@end
@implementation Color
@dynamic allValues;
@end
调试
提供两个额外的功能用于调试
objc_property_t * class_copyEnumPropertyList(Class cls, unsigned int *outCount)
这与运行时的 class_copyPropertyList
类似,但返回的是枚举中包含的属性。它需要在调用 class_createEnum
之前进行。必须使用 free()
释放返回的指针。
id class_getEnumValue(Class cls, objc_property_t property)
将获取类 cls
的枚举成员的值,描述为 property
。
下面是使用上述功能进行调试的示例。
unsigned int count;
objc_property_t *properties;
if((properties = class_copyEnumPropertyList(cls, &count))) {
for (unsigned int i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSLog(@"%s: %@", property_getName(property), class_getEnumValue(cls, property));
}
free(properties);
}
示例
TestApp 中的代码是设置此类的一个好例子,以及如何调试其属性。请在这里查看:https://github.com/thecatalinstan/objc-class-enum/blob/master/TestApp/main.m。
以下是使用 Color
类来结构更多与 颜色 相关的信息的最小示例
@interface Color : NSObject
// Class enum (singletons)
@property (class, nonatomic, strong) Color *red;
@property (class, nonatomic, strong) Color *green;
// Example payload
@property NSColor *rawValue;
@property BOOL allowed;
@end
@implementation Color
@dynamic red, green;
+ (void)initialize {
assert(class_createEnum(self));
self.red = [Color initWithColor:NSColor.redColor allowed:NO];
self.green = [Color initWithColor:NSColor.greenColor allowed:YES];
}
@end