Objective-C运行时崩溃

问题描述 投票:0回答:1

我是第一次尝试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和描述。当在描述所指向的对象上调用调度程序函数时,UTF8String作为运行它的方法崩溃了

返回CFBridgingRetain(methodIMP(receiver,selector));

现在我知道NSString是一个集群,实际上是一个__NSCFString的对象。我认为这可能是调用CFBridgingRetain时的问题。我需要更好地了解实际导致崩溃的原因。提前致谢。

ios objective-c objective-c-runtime
1个回答
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作为此函数的实现可能是一个更好的主意,因为那样您就不必担心类型和参数等等。

© www.soinside.com 2019 - 2024. All rights reserved.