EXC_BAD_ACCESS using ARC only during testing
Asked Answered
P

2

7

I have an issue where I'm getting bad access exceptions but only when running a testing build (calling the same methods in a debug build doesn't cause the problem to come up). The project has ARC enabled and I'm running this on the iPad 5.1 simulator using Xcode 4.3:

Here's where the problem crops up:

- (void)testChangeFoodNotification {
    Player* p = [[Player alloc] init];
    [p addObserver:self forKeyPath:@"food" options:0 context:0]; // <-EXC_BAD_ACCESS (code=2)
    p.food += 1;
    STAssertTrue(_wasNotifiedOfFoodChange, nil);
}

At the point when the addObserver: method is called it doesn't seem like any of the objects involved should have been released so what could be causing the exception?

EDIT:

Apologies if it wasn't clear but the code above is being executed as part of a test case (using the standard Xcode OCUnit). Also in case it clarifies anything here's the relevant code from the player class (there's other ivars and methods but they don't have any connection to the property or methods being tested):

// Public interface
@interface Player : NSObject

@property (nonatomic, assign) NSInteger food;

@end

// Private interface
@interface Player() {
    NSInteger _food;
}

@end

@implementation Player

@synthesize food = _food;

#pragma mark - Getters/Setters

- (void)setFood:(NSInteger)food {
    [self willChangeValueForKey:@"food"];
    _food = food;
    [self didChangeValueForKey:@"food"];    
}
Photograph answered 20/4, 2012 at 13:2 Comment(0)
K
21

If your class is indeed key-value compliant, ensure that the implementation for the class exhibiting the issue is not included in your test product. This means that the Target Membership panel of the Identity inspector for your .m file should only have your app checked (not YourAppTests).

I experienced the same issue in Xcode 4.3.1 when an implementation was included in both products and I registered observers in both production and test code. The following logs tipped me off:

Class YourClass is implemented in both /Users/yourUser/Library/Application Support/iPhone Simulator/5.1/Applications//YourApp.app/YourApp and /Users/yourUser/Library/Developer/Xcode/DerivedData/YourApp-/Build/Products/Debug-iphonesimulator/YourAppTests.octest/YourAppTests. One of the two will be used. Which one is undefined.

Klein answered 26/4, 2012 at 16:47 Comment(1)
I also encountered this issue with CocoaPods. The class implementation file was included in the pods for both the app and app test target, except there was no duplicate class warning in the logs.Sapphera
O
0

As per the Key-Value Observing Programming Guide, is your Player key-value-compliant? You want to make sure you are Ensuring KVC Compliance. I also assume that you have also implemented your observeValueForKeyPath:ofObject:change:context:? If you think you've done all of this and it's still not working, then perhaps you can share your code.

Also, minor thing, but I assume this is a code snippet to highlight the issue. I only mention it because ARC is going to be releasing your p object at the end of your testChangeFoodNotification and I would have thought that you'd want to remove your observer first.

Otocyst answered 20/4, 2012 at 13:31 Comment(5)
Thanks for the response. I could be missing something but I think the class is KVC compliant (the fact that that same code works outside of a test build makes me think that is compliant but I may be wrong - this is my first time using KVC/KVO). As to the removing the observer. Normally I would but I figured that since this is a test case method and both p and the observer are going to be destroyed before there's any chance to modify p again it would be better to avoid cluttering up the test method with boilerplate. I could be wrong though and if I am I'd love to hear how ;)Photograph
Yeah, I'm not seeing it. Sorry. I don't understand why you're defining your private interface for _food nor why you write your own setter (the @synthesize statement does both of those for you), but doubt that's the source of the problem. More importantly, I don't understand why you get different behavior in test v debug builds. I can only suggest you turn on zombies by modifying your scheme in your test build, if you haven't already, and see if it's more enlightening.Otocyst
Originally I wasn't adding my own setters but I added them to see if that was the problem (I thought maybe the synthesized ones didn't call will/didChangeValueForKey - that doesn't seem to be the case). The reason I declare my own private variable is habit: I like my ivars with underscores and the compiler synthesizes them without the underscore. In any case thanks for taking a look.Photograph
Not to belabor the point, I'm with you on the underscored private var, but when you @synthesize food = _food, I think that gives you your own private variable, and you don't need to also have the @interface Player() { NSInteger _food; }. I believe it does that for you.Otocyst
Ha you learn something new every day. I didn't know you could assign an undeclared variable when synthesizing and the compiler would declare it for you. Was this always the case? Either way thanks for the tip.Photograph

© 2022 - 2024 — McMap. All rights reserved.