我是第一次尝试Objective-C运行时方法。我一直在阅读关于iOS 7编程推动极限的第24章。根据本书中的示例,我实现了一个消息调度程序功能,如下所示:
static const void *myMsgSend(id receiver, const char *name) {
SEL selector = sel_registerName(name);
Class receiverClass = object_getClass(receiver);
IMP methodIMP = class_getMethodImplementation(receiverClass, selector);
return CFBridgingRetain(methodIMP(receiver, selector));
}
我在下面的函数中测试了这个调度程序:
void runMyMsgSend() {
// NSObject *object = [[NSObject alloc] init];
Class class = (Class)objc_getClass("NSObject");
id object = class_createInstance(class, 0);
myMsgSend(object, "init");
// id description = [object description];
id description = (__bridge id)myMsgSend(object, "description");
// const char *cstr = [description UTF8String];
const char *cstr = myMsgSend(description, "UTF8String");
printf("---------------------");
printf("%s\n", cstr);
}
对于 NSObject 类型的对象实例,该函数适用于 init 和 description 。 当使用描述指向的对象调用调度程序函数时, UTF8String 作为运行它的方法崩溃
返回CFBridgingRetain(methodIMP(receiver,selector));
现在我知道NSString是一个集群,实际上是__NSCFString的一个对象。我认为这可能是调用 CFBridgingRetain 时的问题。我需要更好地了解实际导致崩溃的原因。提前谢谢。
答案 0 :(得分:2)
您正试图在所有返回类型上调用CFBridgingRetain
..
文档明确指出,如果IMP
返回任何非void
的内容,则必须将其明确地转换为正确的函数指针类型。
您的职能是:
static const void *myMsgSend(id receiver, const char *name) {
SEL selector = sel_registerName(name);
Class receiverClass = object_getClass(receiver);
IMP methodIMP = class_getMethodImplementation(receiverClass, selector);
id (*imp)(id, SEL) = (id (*)(id, SEL))methodIMP;
return CFBridgingRetain(imp(receiver, selector));
}
当您致电UTF8String
时,它会返回您要保留的const char*
。不仅如此,你要说IMP
返回一个id
而你保留它......你只能保留Objective-C类对象。你永远不会转换为正确的函数指针类型,所以它崩溃试图隐式转换const char*
到id
..
因此,您的方法必须是:
static const void *myMsgSend(id receiver, const char *name) {
SEL selector = sel_registerName(name);
Class receiverClass = object_getClass(receiver);
IMP methodIMP = class_getMethodImplementation(receiverClass, selector);
void* (*imp)(id, SEL) = (void* (*)(id, SEL))methodIMP;
return imp(receiver, selector, args);
}
返回一个简单的void*
,它可以是NULL或BOOL或int或w / e类型(原始)。如果需要返回一个对象,则该对象被隐式桥接,因此不需要保留(只需将结果转换为正确的类型。)
但是,使用NSInvocation
作为此函数的实现可能更好一点,因为那样你就不必担心类型和参数等等。