1

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;
}

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

例如 MagicForwardingMacro (nPistons, int, engine);

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

4

3 回答 3

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。我不确定是否将属性添加到“方法描述列表”中。另请注意,“复制”功能不会从超类复制方法/属性,这(在这种情况下)是您想要的。

于 2011-06-28T02:38:45.550 回答
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)

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

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

于 2011-06-28T01:54:43.173 回答
0

我越来越接近我想要的了。一些令人讨厌的细节仍然存在:

转发包括.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)

汽车.h:

@interface Car: SomeSuperclass {
  // some ivars
}

. . .

#include CarForwarding.h

汽车.m:

. . .

@implementation Car

#define IMPLEMENTATION
#include CarForwarding.h

唠叨的细节:

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

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

于 2011-06-28T03:50:44.353 回答