Why shouldn't you use objc_msgSend() in Objective C?

谁说胖子不能爱 提交于 2019-11-28 07:27:52

Reason #1: Bad style - it's redundant and unreadable.

The compiler automatically generates calls to objc_msgSend() (or some variant thereof) when it encounters Objective-C messaging expressions. If you know the class and the selector to be sent at compile-time, there's no reason to write

id obj = objc_msgSend(objc_msgSend([NSObject class], @selector(alloc)), @selector(init));

instead of

id obj = [[NSObject alloc] init];

Even if you don't know the class or the selector (or even both), it's still safer (at least the compiler has a chance to warn you if you are doing something potentially nasty/wrong) to obtain a correctly typed function pointer to the implementation itself and use that function pointer instead:

const char *(*fptr)(NSString *, SEL) = [NSString instanceMethodForSelector:@selector(UTF8String)];
const char *cstr = fptr(@"Foo");

This is especially true when the types of the arguments of a method are sensitive to default promotions - if they are, then you don't want to pass them through the variadic arguments objc_msgSend() takes, because your program will quickly invoke undefined behavior.

Reason #2: dangerous and error-prone.

Notice the "or some variant thereof" part in #1. Not all message sends use the objc_msgSend() function itself. Due to complications and requirements in the ABI (in the calling convention of functions, in particular), there are separate functions for returning, for example, floating-point values or structures. For example, in the case of a method that performs some sort of searching (substrings, etc.), and it returns an NSRange structure, depending on the platform, it may be necessary to use the structure-returning version of the messenger function:

NSRange retval;
objc_msgSend_stret(&retval, @"FooBar", @selector(rangeOfString:), @"Bar");

And if you get this wrong (e. g. you use the inappropriate messenger function, you mix up the pointers to the return value and to self, etc.), your program will likely behave incorrectly and/or crash. (And you will most probably get it wrong, because it's not even that simple - not all methods returning a struct use this variant, since small structures will fit into one or two processor registers, eliminating the need for using the stack as the place of the return value. That's why - unless you are a hardcore ABI hacker - you rather want to let the compiler do its job, or there be dragons.)

CRD

You ask "what are the dangers?" and @H2CO3 has listed some ending with "unless you are a hardcore ABI hacker"...

As with many rules there are exceptions (and possibly a few more under ARC). So your reasoning for using msgSend should go something along the lines of:

[ 1] I think I should use msgSend - don't.

[2] But I've a case here... - you probably haven't, keep looking for another solution.

...

[10] I really think I should use it here - think again.

...

[100] Really, this looks like a case for msgSend, I can't see any other solution! OK, go read Document.m in the TextEdit code sample from Apple. Do you know why they used msgSend? Are you sure... think again...

...

[1000] I understand why Apple used it, and my case is similar... You've found and understood the exception that proves the rule and your case matches, use it!

HTH

I can make a case. We use msgSend in one of our C++ files that's in a cross-platform project (Windows, Mac and Linux). We use it to ref count a reference in the backed (the shared code) that's used later to go from frontend to backend and vice versa. Very special case, admittedly.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!