I'm making a class, that given an object
target, a selector
to watch for, and a displayTitle
will output a string in this format: @"displayTitle: object.selector"
. It then registers itself through KVO so that anytime the value
of object.selector
changes, it can notify a view controller to update the view. I am using this as an abstract and reusable way to show a description of various properties of an object to a user.
When I try to get the value of object.selector
, I can't do [object performSelector:selector]
because LLVM gives errors when you use performSelector with a dynamic selector.
So, I did exactly what this answer suggested: I used objc_msgSend(object, selector)
.
- (instancetype)initWithSelector:(SEL)selector onObject:(NSObject*)object displayTitle:(NSString*)displayTitle {
self = [super init];
if (self) {
id value;
if ([object respondsToSelector:selector) {
// Used objc_msgSend instead of performSelector to suppress a LLVM warning which was caused by using a dynamic selector.
value = objc_msgSend(object, selector);
} else {
return nil;
}
[self setItemDescription:[NSString stringWithFormat:@"%@: %@", displayTitle, value]];
}
return self;
}
And I got an EXC_BAD_ACCESS
!
As you can see in the screenshot, I made sure that
doing [object selector]
works.
What is going on, and how can I fix it?
You assign the result of your objc_msgSend
call to a variable of type id
so ARC kicks in and tries to retain the resulting object (crash is in objc_retain
as you can see in the stack to the left). However, the result isn’t an object but an integer of value 8, which objc_retain
takes to be a pointer. But there are no valid pointers this low, so you get the EXC_BAD_ACCESS
.
Just change the type of value
to be NSUInteger
(or any other non-object type). But make sure all potential selector
s return data of the same type. Alternatively, make sure to always return an object (or nil
), which can be retained by ARC.
Just like with calling a function in C, in order to call a method in Objective-C, you need to know the exact type signature of the method.
When you use objc_msgSend
and related functions, you need to cast it to the correct function pointer type before calling it. From the other answers and comments, it appears that your method takes no parameters and has return type NSInteger
. In that case, in order to use it, you must do something like:
NSInteger value;
NSInteger (*f)(id, SEL) = (NSInteger (*)(id, SEL))objc_msgSend;
value = f(object, selector);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With