C宏在objective-c中暴露ivar的属性

时间:2012-06-21 23:00:18

标签: objective-c macros

在我的项目中,我通常有一个复合对象(GameObject)需要将一些ivar属性暴露给GameObject的界面。例如,GameObject有一个带有'position'属性的Sprite,我想使用Sprite的位置作为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; } \

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

2 个答案:

答案 0 :(得分: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,但我不推荐它。

我不会真的推荐使用它,但是如果你想要一些很酷的东西,请继续尝试吧!

答案 1 :(得分: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);
...