如何在Objective-C中创建可重用的类级属性构造?

时间:2014-04-24 20:54:12

标签: objective-c cocoa metaprogramming

我想拥有类级属性,我找到了https://stackoverflow.com/a/15811719/157384的解决方案

@interface Model
+ (int) value;
+ (void) setValue:(int)val;
@end

@implementation Model
static int value;
+ (int) value
{ @synchronized(self) { return value; } }
+ (void) setValue:(int)val
{ @synchronized(self) { value = val; } }
@end

现在您可以通过属性访问器进行调用,如下所示:

Model.value = 1
Model.value // => 1

奇!

现在,我想让这个代码块以某种形式的宏或元编程重用,它采用属性名称和类型。我们怎么写呢?

通过上面的示例,value(int)(和Model)应该是动态的。

更新

感谢@Rich,我现在有了这个:

// Common.h
#define CLASS_PROPERTY_INTERFACE(TYPE, METHOD, CMETHOD) \
+ (TYPE) METHOD; \
+ (void) set##CMETHOD:(TYPE)val; \

#define CLASS_PROPERTY_IMPLEMENTATION(TYPE, METHOD, CMETHOD) \
static TYPE _##METHOD; \
+ (TYPE) METHOD \
{ @synchronized(self) { return _##METHOD; } } \
+ (void) set##CMETHOD:(TYPE)val \
{ @synchronized(self) { _##METHOD = val; } } \

// User.h
@interface User : NSObject
CLASS_PROPERTY_INTERFACE(User *, me, Me)
@end

// User.m
@implementation User
CLASS_PROPERTY_IMPLEMENTATION(User *, me, Me)
@end

User.me = currentUser;
User.me // => currentUser

还有一件事要做,就是尽可能自动大写传递给宏的方法名称。

但它已经比它的样板更简洁了!

2 个答案:

答案 0 :(得分:2)

这与要求在实例使用的类上动态生成访问器没什么不同。事实上同样的问题。

对于直接对象值而言,无论对象类型如何,都不难做到,并且您可以通过利用关联对象在运行时纯粹生成它们。

但是,使用id之外的类型信息生成这些要求意味着您已将问题转移到编译时问题。

为此,您几乎可以编写一系列#define宏来生成您需要的任何代码。你至少需要两个宏;一个用于实现,一个用于接口。

答案 1 :(得分:2)

宏观方式......

注意: 这有点可怕,我不建议使用它,但是正在玩它们在宏中定义它们......

对此的警告(除了它不是非常“好”)是setter方法的格式为set_XXX:

#define CLASS_INTERFACE(CLS_NAME, METHOD, TYPE) @interface CLS_NAME : NSObject \
    + (TYPE) METHOD; \
    + (void) set_##METHOD:(TYPE)val; \
    @end \

#define CLASS_IMPLEMENTATION(CLS_NAME, METHOD, TYPE) @implementation CLS_NAME \
    static TYPE METHOD; \
    + (TYPE) METHOD \
    { @synchronized(self) { return METHOD; } } \
    + (void) set_##METHOD:(TYPE)val \
    { @synchronized(self) { METHOD = val; } } \
    @end \

将以下内容放在标题文件中:

CLASS_INTERFACE(Test, value, int)

这在.m个文件中:

CLASS_IMPLEMENTATION(Test, value, int)

然后使用Test类:

[Test set_value:4];
int i = [Test value];

这又是非常可怕的但是会起作用......!

编辑:

单身人士

正如评论中提到的,我认为使用单例更好,这让我编写了更可怕的代码:(

现在我们已经准备好(生病袋):

#define SINGLETON_INTERFACE_START(CLS_NAME) @interface CLS_NAME : NSObject \
    +(instancetype) sharedInstance; \

#define SINGLETON_INTERFACE_END(CLS_NAME) \
    @end \
    static inline CLS_NAME * CLS_NAME##Global () { return [CLS_NAME sharedInstance]; }

#define SINGLETON_IMPLEMENTATION(CLS_NAME) @implementation CLS_NAME \
    +(instancetype) sharedInstance \
    { \
        static id instance; \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            instance = [self new]; \
        }); \
        return instance; \
    } \
    @end \

在你的标题中:

SINGLETON_INTERFACE_START(Test)

@property (atomic, assign) NSUInteger value;

SINGLETON_INTERFACE_END(Test)

在您的.m

SINGLETON_IMPLEMENTATION(Test)

并使用它(可以产生更多的病):

TestGlobal().value = 1;
int i = TestGlobal().value;

或“更好”的Objective-C方式:

[Test sharedInstance].value = 1;
int i = [Test sharedInstance].value;

你甚至可以(Team America现在的病人数量)在界面中有属性#define

请注意,我已将@property中的@interface定义保留为atomic,因为OP似乎喜欢使用@synchronized。这不是必需的,因为它们设置为atomic

我知道OP想要类“属性”(只是说这让我颤抖),但还有其他更好的选项!