问题
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?
回答1:
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.
回答2:
Could it be that your value is 8, an Integer?
Thanks to arc, value is attempted to be retained, i.e. retain at adress 0x8, which would explain that EXC_BAD_ACCESS
, 0x8 being a protected address.
If that is your problem, just wrap your value as a NSNumber
回答3:
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);
来源:https://stackoverflow.com/questions/24232434/why-is-objc-msgsend-causing-an-exc-bad-access