【问题标题】:Objective-C runtime crashObjective-C 运行时崩溃
【发布时间】:2018-01-01 15:04:06
【问题描述】:

我是第一次尝试 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);
}

该函数适用于 initdescription 类型为 NSObject 的对象实例。 当在 description 指向的对象上调用调度程序函数时,使用 UTF8String 作为运行方法,它会在

上崩溃

return CFBridgingRetain(methodIMP(receiver, selector));

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

【问题讨论】:

    标签: ios objective-c objective-c-runtime


    【解决方案1】:

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

    【讨论】:

    • 更正直到增强位。 Varargs 不是那样工作的。您不能将args 作为参数传递并使其与非可变参数方法或函数一起使用。
    • @Brandon 这很有帮助。没有你的帮助就不会解决它。将 IMP 类型转换为返回 void* 的函数指针至关重要。
    • @bbum 感谢您的想法。我会考虑你关于可变参数的观点。
    猜你喜欢
    • 1970-01-01
    • 2019-10-24
    • 2021-06-09
    • 2015-08-08
    • 1970-01-01
    • 1970-01-01
    • 2012-01-31
    • 2013-02-07
    • 1970-01-01
    相关资源
    最近更新 更多