iOS - 使用宏来转发大量消息?

时间:2011-06-28 01:43:31

标签: iphone ios macros message-forwarding

ForwardInvocation确实存在,但它很慢并且有令人讨厌的编译器警告问题。这让我思考 - 有没有办法使用宏来快速实现一堆getter方法,从另一个对象获取有问题的属性?

例如,如果我有一个Car对象,它可能想要实现以下内容:

Car.h:

@class SparkPlug;
@class Engine;

. . .

-(int) nPistons;
-(float) horsepower;
-(SparkPlug*) sparkPlug;

Car.m:

. . .

-(int) nPistons {
    return self.engine.nPistons;
}

-(float) horsepower {
    return self.engine.horsepower;
}

-(SparkPlug*) sparkPlug {
    return self.engine.sparkPlug;
}

问题 - 是否可以设置一些宏,以便通过在某处进行 one 更改,我可以在头文件和实现文件中添加另一个这样的方法?

e.g。 MagicForwardingMacro(nPistons,int,engine);

理想情况下,如果我后来想要使用类似的策略从他或她的birthCertificate获取Person的firstName,lastName,placeOfBirth和dateOfBirth属性,那么macroes将是可重用的。

3 个答案:

答案 0 :(得分:1)

最简单的方法可能是动态添加方法:

阐述第二步:

对于每种类型,添加类似

的方法
-(int)getEngineInt {
  return (int()(id,SEL))(objc_msgSend)(engine, _cmd);
}

请注意,对于结构,您需要objc_msgSend_stret,对于浮点数/双精度数,您可能需要objc_msgSend_fpret(我认为您只需要在i386上使用它;不​​确定AMD64)。支持模拟器和设备的简单方法就是这样(我忘记了GCC使用的宏名称......)

#if __i386
#define objc_msgSend_fpret objc_msgSend
#endif

现在要实现+resolveInstanceMethod:,您需要提前知道要转发的课程。让我们说它是引擎。

+(BOOL)instancesRespondToSelector:(SEL)name
{
  return [Engine instancesRespondToSelector:name];
}

+(BOOL)resolveInstanceMethod:(SEL)name
{
  // Do we want to super-call first or last? Who knows...
  if ([super resolveInstanceMethod:name]) { return YES; }
  // Find the return type, which determines the "template" IMP we call.
  const char * returntype = [Engine instanceMethodSignatureForSelector:name].methodReturnType;
  if (!returnType) { return NO; }

  // Get the selector corresponding to the "template" by comparing return types...
  SEL template = NULL;
  if (0 == strcmp(returntype,@encode(int))
  {
    sel = @selector(getEngineInt);
  }
  else if (0 == strcmp(Returntype,@encode(float))
  {
    ...
  }
  if (!sel) { return NO; }
  Method m = class_getInstanceMethod(self,template);
  return class_addMethod(self, name, method_getImplementation(m), method_getTypeEncoding(m));
}

或者,有一个稍微未记录的方法-forwardingTargetForSelector:可能足够快,可以满足您的需求。

编辑:或者,您可以动态循环遍历属性/方法。似乎没有明显的方法来内省类别,但您可以在协议中定义它们,执行@interface Engine:NSObject<Engine> ... @interface Car(DynamicEngine)<Engine>之类的操作并使用objc_getProtocol("Engine")然后使用protocol_copyMethodDescriptionList() / protocol_copyPropertyList()获取方法,然后添加getter。我不确定属性是否被添加到“方法描述列表”中。另请注意,“复制”函数不会从超类复制方法/属性,在这种情况下,它就是您想要的。

答案 1 :(得分:0)

可悲的是,我不认为Objective-C 2.0属性对您有用,因为我认为您不能在属性声明中指定任何类型的转发。

您不能拥有一个将在两个不同位置插入文本的宏。但是,您可以使用两个宏,如下所示:

//This could also take the third argument and discard it, if you like
#define FORWARDI(type, prop) - (type)prop;
#define FORWARDM(type, prop, owner) - (type)prop { return owner.prop; }

//In the header...
FORWARDI(float, nPistons)

//In the implementation...
FORWARDM(float, nPistons, self.engine)

如果您不介意未在头文件中显示的方法(例如,如果您只在类的实现本身中使用这些方法),您也可以单独使用实现文件宏。< / p>

这与所有者的类型无关,但它应该适用于任何表达式。

答案 2 :(得分:0)

我正在接近我想要的东西。一些唠叨的细节仍然存在:

ForwardingInclude.h:

// no include guard; we want to be able to include this multiple times
#undef forward
#ifdef IMPLEMENTATION
#define forward(a, b, c)  -(a) b { return [[self c] b]; }
#else
#define forward(a, b, c)  -(a) b;
#endif

CarForwarding.h:

// again, no include guard
#include ForwardingInclude.h
forward(int, nPistons, engine)
forward(SparkPlug* sparkPlug, engine)

Car.h:

@interface Car: SomeSuperclass {
  // some ivars
}

. . .

#include CarForwarding.h

Car.m:

. . .

@implementation Car

#define IMPLEMENTATION
#include CarForwarding.h

唠叨的细节:

1)我不喜欢#define IMPLEMENTATION行。我希望CarForwarding.h以某种方式自动检测它当前是否包含在实现中。

2)如果我能在转发文件中定义的内容以某种方式在标题中以人类可读的形式出现,那将是非常酷的。或者更好 - 将“正向”定义直接写入Car.h文件中,所以我根本不需要CarForwarding.h文件。