Search code examples
objective-cselectorexc-bad-accessobjc-message-send

Why is objc_msgSend causing an EXC_BAD_ACCESS?


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!

Screenshot

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?


Solution

  • 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 selectors return data of the same type. Alternatively, make sure to always return an object (or nil), which can be retained by ARC.