2

在我的项目中,我通常有一个复合对象(GameObject),它需要将 ivar 的一些属性公开到 GameObject 的接口中。例如,一个游戏对象有一个带有“位置”属性的精灵,我想使用精灵的位置作为游戏对象的属性。这很容易:

// GameObject.h
@interface GameObject : NSObject
@property CGPoint position;
...
@end

// GameObject.m
@interface GameObject ()
@property Sprite* sprite;   // private property
@end 

@implementation
- (CGPoint)position { return sprite.position; };
- (void)setPosition:(CGPoint)p { sprite.position = p; };
...

作为一个附带项目,我一直在研究使用 C 宏生成 getter/setter。理想情况下,我能够做到:

@implementation
EXPOSE_SUBCOMPONENT_PROPERTY(subcomponent,propertyName,propertyType);
...

我最近失败的尝试是:

#define EXPOSE_SUBCOMPONENT_PROPERTY(sub,property,type) \
- (type)property { id x = sub; return x.##property;} \
- (void)setProperty:(type)set_val { id x = sub; x.##property = set_val; } \

那里有任何宏向导可以提供帮助吗?其次,有没有办法不需要向宏提供属性的类型?

4

2 回答 2

0

好吧,你要求它,但要振作起来,它会很快变得丑陋:

#import <objc/runtime.h>

#define EXPOSE_SUBCOMPONENT_PROPERTY(selfClass, sub, property) \

static typeof([[selfClass new] property]) ___get##property(id self, SEL _cmd)\
{\
return ((selfClass *)self)->sub.property;\
}\
static void ___set##property(id self, SEL _cmd, ...)\
{\
selfClass *selfCasted = (selfClass *) self; \
va_list args; va_start(args, _cmd); \
typeof(selfCasted.property) _val = va_arg(args, typeof (selfCasted.property));\
selfCasted->sub.property = _val;\
}\
__attribute__((constructor)) \
static void __init##__property() \
{\
Class cls = [selfClass class];\
class_addMethod(cls, @selector(property), (IMP) ___get##property, "");\
/* for capitalization */ \
char selAsStr[sizeof(#property) + 4] = "set";\
strcat(selAsStr, #property ":");\
selAsStr[3] = toupper(selAsStr[3]);\
class_addMethod(cls, sel_registerName(selAsStr), (IMP) ___set##property, "");\
}\

您不再需要获取返回类型,但您必须将类类型发送给方法。

必须为此使用 GCC,因为使用了广泛的扩展,或者,您可以使用 C++ 11,但我不推荐它。

我真的不推荐使用这个,但如果你想要一些很酷的东西,那就试试吧!

于 2012-06-21T23:54:54.457 回答
0

找到了更好的解决方案。诀窍是了解符号是如何拆分的,以正确使用“##”。此外,必须为属性声明添加一个宏来自定义 getter/setter 名称,从而解决属性名称大写的问题。对我来说没什么大不了的,帮助我记住它的特殊属性。我能想到的唯一缩减是由于自定义名称,它们将不符合 KVO。

我重命名了宏以使它们更容易记住(它们分别替换了@property 和@synthesize),并重新排列了参数顺序以反映属性。

#define PROPERTY_OF_IVAR(prop_type,ivar,prop_name) \
    @property (setter=set_##prop_name:, getter=get_##prop_name) prop_type prop_name \

#define SYNTHESIZE_IVAR_PROPERTY(prop_type,ivar,prop_name) \
    - (prop_type)get_##prop_name { return ivar.prop_name;} \
    - (void)set_##prop_name:(prop_type)set_val { ivar.prop_name = set_val; } \

另一个版本,使@property 声明更自然,并具有能够在任何深度的属性的额外好处(而不仅仅是 ivar 属性,您可以深入到 ivar.property.another_property.indefinitely)。您还可以将属性的名称设置为与其在组合对象中的名称不同。

// prop_type - type of property that will be exposed
// prop_name - name of property in composing object
#define PROPERTY_OF_IVAR(prop_type, prop_name) \
    (setter=set_##prop_name:, getter=get_##prop_name) prop_type prop_name \

// prop_type - type of property that will be exposed
// prop_path - path to the property you want
// prop_name - must match PROPERTY_OF_IVAR (above)
#define SYNTHESIZE_IVAR_PROPERTY(prop_type, prop_path,prop_name) \
    - (prop_type)get_##prop_name { return prop_path;} \
    - (void)set_##prop_name:(prop_type)set_val { prop_path = set_val; } \

示例使用:

// foo.h
@interface
@property PROPERTY_OF_IVAR(CGPoint, position);
...

// foo.m
@implementation
SYNTHESIZE_IVAR_PROPERTY(CGPoint, sprite.position, position);
...
于 2012-06-22T00:50:36.080 回答