iOS——使用宏来转发一堆消息?

发布于 2024-11-17 16:49:24 字数 702 浏览 1 评论 0原文

ForwardIncation 确实存在,但速度很慢,并且存在令人烦恼的编译器警告问题。所以这让我开始思考——有没有一种方法可以使用宏来快速实现一堆 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 中获取该人的firstName、lastName、placeOfBirth 和dateOfBirth 属性,那么宏将是可重用的。

ForwardInvocation does exist, but it is slow and has the annoying problem of compiler warnings. So that got me to thinking -- is there a way to use macroes to quickly implement a bunch of getter methods that get the property in question from another object?

For example, if I have a Car object, it might want to implement the following:

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

Question -- would it be possible to set up some macroes so that by making one change somewhere, I could add another such method to both the header and implementation files?

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

Ideally, in such a way that the macroes would be reusable if I later wanted to later use a similar strategy to get the firstName, lastName, placeOfBirth, and dateOfBirth properties of a Person from his or her birthCertificate.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浅黛梨妆こ 2024-11-24 16:49:24

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

详细说明第二步:

对于每种类型,添加一个方法,例如

-(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的操作... @interface Car(DynamicEngine) 并使用 objc_getProtocol("Engine") 然后使用 protocol_copyMethodDescriptionList()/protocol_copyPropertyList() 获取方法,然后添加吸气剂。我不确定属性是否已添加到“方法描述列表”中。另请注意,“复制”函数不会从超类复制方法/属性,这(在本例中)正是您想要的。

The easiest way is probably to add the methods dynamically:

Elaborating on the second step:

For each type, add a method like

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

Note that for structs you need objc_msgSend_stret and for floats/doubles you might need objc_msgSend_fpret (I think you only need it on i386; not sure about AMD64). The easy hack to support both the simulator and device is something like (I forget the macro name GCC uses...)

#if __i386
#define objc_msgSend_fpret objc_msgSend
#endif

Now to implement +resolveInstanceMethod:, you need to know the class you're forwarding to ahead of time. Let's say it's Engine.

+(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));
}

Alternatively, there's a slightly undocumented method -forwardingTargetForSelector: which may be fast enough for your needs.

EDIT: Alternatively, you can loop over the properties/methods dynamically. There doesn't appear to be an obvious way to introspect categories, but you can define them in a protocol, do something like @interface Engine:NSObject<Engine> ... @interface Car(DynamicEngine)<Engine> and use objc_getProtocol("Engine") and then protocol_copyMethodDescriptionList()/protocol_copyPropertyList() to get the methods, and then add the getters. I'm not sure if properties are added to the "method description list". Also note that the "copy" functions do not copy methods/properties from superclasses, which (in this case) is what you want.

古镇旧梦 2024-11-24 16:49:24

遗憾的是,我认为 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)

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

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

Sadly, I don't think Objective-C 2.0 properties will work for you because I don't think you can specify any kind of forwarding in the property declaration.

You can't have one macro that will insert text in two different places. However, you can use two macros like so:

//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)

If you don't mind the methods not showing up in the header file (for example, if you will only use these methods inside the class's implementation itself), you can just as well use the implementation file macro by itself.

This is agnostic to the type of the owner, but it should work with any expression.

无声无音无过去 2024-11-24 16:49:24

我正在接近我想要的。仍然存在一些烦人的细节:

ForwardingIninclude.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 IMPLMENTATION 行。我希望 CarForwarding.h 以某种方式自动检测它当前是否包含在实现中。

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

I'm getting close to what I want. Some nagging details remain:

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

The nagging details:

1) I don't like that #define IMPLEMENTATION line. I want CarForwarding.h to somehow automatically detect whether or not it is currently being included inside an implementation.

2) It would be waaaaaay cool if I could have the stuff defined in the forwarding file somehow also appear in human-readable form in the header. Or better yet -- write the "forward" definitions directly into the Car.h file somehow, so I don't need the CarForwarding.h file at all.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文