Iphone iOS——使用宏转发一堆消息?
ForwardInvocation确实存在,但速度很慢,并且存在编译器警告的恼人问题。这让我思考——有没有一种方法可以使用宏来快速实现一系列getter方法,从另一个对象获取有问题的属性 例如,如果我有一个Car对象,它可能希望实现以下功能:Iphone iOS——使用宏转发一堆消息?,iphone,ios,macros,message-forwarding,Iphone,Ios,Macros,Message Forwarding,ForwardInvocation确实存在,但速度很慢,并且存在编译器警告的恼人问题。这让我思考——有没有一种方法可以使用宏来快速实现一系列getter方法,从另一个对象获取有问题的属性 例如,如果我有一个Car对象,它可能希望实现以下功能: Car.h: @class SparkPlug; @class Engine; . . . -(int) nPistons; -(float) horsepower; -(SparkPlug*) sparkPlug; Car.m: . . .
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;
}
问题——是否可以设置一些宏,这样通过在某个地方进行一个更改,我就可以向头文件和实现文件中添加另一个这样的方法
e、 g.MagicForwardingMacro(nPistons、int、engine)
理想情况下,如果我以后想使用类似的策略从一个人的出生证中获取他的名字、姓氏、出生地点和出生日期属性,那么Macroe可以重复使用。遗憾的是,我认为Objective-C2.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)
如果不介意头文件中没有显示这些方法(例如,如果只在类的实现本身中使用这些方法),那么也可以单独使用实现文件宏
这与所有者的类型无关,但它应该适用于任何表达式。遗憾的是,我认为Objective-C2.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)
如果不介意头文件中没有显示这些方法(例如,如果只在类的实现本身中使用这些方法),那么也可以单独使用实现文件宏
这与所有者的类型无关,但它应该适用于任何表达式。最简单的方法可能是动态添加方法:
- 将属性添加到类别中,这样编译器就不会抱怨太多
- 克隆一个合适的小鬼。你需要拨开窗户
-(int)getEngineInt {
return (int()(id,SEL))(objc_msgSend)(engine, _cmd);
}
请注意,对于结构,您需要objc_msgSend_stret;对于浮点/双精度,您可能需要objc_msgSend_fpret(我认为您只需要在i386上使用它;对于AMD64不确定)。支持模拟器和设备的简单方法是(我忘了GCC使用的宏名…)
现在要实现+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));
}
或者,有一个稍微没有文档化的方法,它可能足够快,可以满足您的需要
编辑:或者,您可以动态循环属性/方法。似乎没有一种明显的方式来内省类别,但您可以在协议中定义它们,执行类似于
@interface Engine:NSObject的操作@接口Car(DynamicEngine)
并使用objc\u getProtocol(“引擎”)
和protocol\u copyMethodDescriptionList()/protocol\u copyPropertyList()获取方法,然后添加getter。我不确定是否将属性添加到“方法描述列表”中。还请注意,“复制”函数不会从超类复制方法/属性,这(在本例中)正是您想要的。最简单的方法可能是动态添加方法:
- 将属性添加到类别中,这样编译器就不会抱怨太多
- 克隆一个合适的小鬼。你需要拨开窗户
-(int)getEngineInt {
return (int()(id,SEL))(objc_msgSend)(engine, _cmd);
}
请注意,对于结构,您需要objc_msgSend_stret;对于浮点/双精度,您可能需要objc_msgSend_fpret(我认为您只需要在i386上使用它;对于AMD64不确定)。支持模拟器和设备的简单方法是(我忘了GCC使用的宏名…)
现在要实现+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));
}
或者,有一个稍微没有文档化的方法,它可能足够快,可以满足您的需要
编辑:或者,您可以动态循环属性/方法。似乎没有一种明显的方式来内省类别,但您可以在协议中定义它们,执行类似于
@interface Engine:NSObject的操作@接口Car(DynamicEngine)
并使用objc\u getProtocol(“引擎”)
和protocol\u copyMethodDescriptionList()/protocol\u copyPropertyList()获取方法,然后添加getter。我不确定是否将属性添加到“方法描述列表”中。还请注意,“复制”函数不会从超类复制方法/属性,这(在本例中)正是您想要的。我正在接近我想要的。一些棘手的细节仍然存在:
转寄包括。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
m:
. . .
@implementation Car
#define IMPLEMENTATION
#include CarForwarding.h
烦人的细节:
1) 我不喜欢这样定义实现路线。我希望CarForwarding.h以某种方式自动检测当前是否包含在实现中
2) 如果我能让转发文件中定义的内容以某种方式以人类可读的形式出现在标题中,那就太酷了。或者更好——以某种方式将“forward”定义直接写入Car.h文件,这样我就根本不需要CarForwarding.h文件了。我已经接近我想要的了。一些棘手的细节仍然存在: 转寄包括。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
m:
. . .
@implementation Car
#define IMPLEMENTATION
#include CarForwarding.h
烦人的细节:
1) 我不喜欢这样定义实现路线。我想把Carh转寄到au