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.