修正objc_msgSend 在64bit(arm64)上使用

修正objc_msgSend 在64bit(arm64)上使用

当你的程式从32bit转换到64bit后未将objc_msgSend更改符合64bit的方式,那执行之后可能会发生EXC_BAD_ACCESS问题,造成程式突然当掉或是停止不动,所以你必需要使用Apple提供符合64bit的方式。

这方式在Apple文件的64-Bit Transition Guide for Cocoa Touch中有提到:

An exception to the casting rule described above is when you are calling the objc_msgSend function or any other similar functions in the Objective-C runtime that send messages. Although the prototype for the message functions has a variadic form, the method function that is called by the Objective-C runtime does not share the same prototype. The Objective-C runtime directly dispatches to the function that implements the method, so the calling conventions are mismatched, as described previously. Therefore you must cast the objc_msgSend function to a prototype that matches the method function being called.

Listing 2-14 shows the proper form for dispatching a message to an object using the low-level message functions. In this example, the doSomething: method takes a single parameter and does not have a variadic form. It casts the objc_msgSend function using the prototype of the method function. Note that a method function always takes an id variable and a selector as its first two parameters. After the objc_msgSend function is cast to a function pointer, the call is dispatched through that same function pointer.

也就是你必需要使用先定义原型的使用方式再利用此方式执行它才不会造成错误,所以原型要改成:

int MyFunction(int a, int b, ...);

int (*action)(int, int, int) = (int (*)(int, int, int)) MyFunction;
//            ^^^^^^^^^^^^^^--两方对应----^^^^^^^^^^^^^
action(1,2,3); // Error!

必需要定义所有参数类型,如果无返回值的话将int改成void

void MyFunction(int a, int b, ...);

void (*action)(int, int, int) = (int (*)(int, int, int)) MyFunction;
//            ^^^^^^^^^^^^^^--两方对应----^^^^^^^^^^^^^
action(1,2,3); // Error!

以下举个例子由原先的32bit转换至64bit的做法。

  • 32bit原本程式:
objc_msgSend(THIS->parent, @selector(enableLine:), TRUE);

  • 64bit Apple提供的范例加做修改:
void MyFunction(id, SEL, int);
void (*action)(id, SEL, int) = (void (*)(id, SEL, int)) MyFunction;
action(THIS->parent, @selector(enableLine:), TRUE); // Error!

  • 64bit简洁后的方法
((void(*)(id, SEL,BOOL))objc_msgSend)(THIS->parent, @selector(enableLine:), TRUE);

参考资讯

64-Bit Transition Guide for Cocoa Touch

【iOS7】AsyncImageView が arm64 でクラッシュする件