ios - Objective-C 运行时崩溃

标签 ios objective-c objective-c-runtime

我是第一次尝试 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 类型的对象实例的initdescription 效果很好。 当使用 UTF8String 作为运行方法在 description 指向的对象上调用调度程序函数时,它在

上崩溃

return CFBridgingRetain(methodIMP(receiver, selector));

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

最佳答案

您正在尝试对所有返回类型调用 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 作为此函数的实现可能是一个更好的主意,因为这样您就不必担心类型和参数等问题。

关于ios - Objective-C 运行时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48050693/

相关文章:

ios - 如何在所有 View Storyboard iOS 上添加视觉效果 View 模糊?

ios - 检测所有 UIWebViews 加载完成 : Objective-C

ios - 避免项目中使用我的 iOS 框架和依赖项之一的重复符号的最佳方法是什么?

ios - GKTurnBasedMatch 接收数据

iphone - 为 UIView 层设置 anchor

objective-c - 在 objc_SetAssociatedObject 中使用非 id 指针作为值是否可以?

c++ - 在 Objective-C block 中泄漏 C++ shared_ptr

ios - 使用子类化 UIActivityItemProvider 时没有可用的共享操作

objective-c - 如何立即停止 UIWebView 加载

ios - 在 iOS 7 中更改条形按钮的颜色