My app is working perfectly on the simulator but when I test the app on the device I eventually get a random EXC_BAD_ACCESS and the app crashes.
After a few days of testing I think I have found the code that is causing this error. At some point I need to add some subviews to a controller's main view, then the user interacts with the app and those subviews are removed from their superview and new subviews are added. If I don't ever remove the subviews the app does not crash but if I remove them, the app eventually gets an EXC_BAD_ACCESS and crashes.
It seems like the subviews removed get a release msg when they already are completely released or something like that... This the first app where I'm using ARC so I'm probably missing something...
Here's the code involved:
#define kWordXXX 101
#define kWordYYY 102
...
// This is called after the user interaction, it removes the
// old subviews (if they exist) and add new ones
- (void)updateWords
{
[self removeWords];
if (self.game.move.wordXXX) {
WordView *wordXXX = [self wordViewForTypeXXX];
wordXXX.tag = kWordXXX;
// self.wordsView is the view where the subviews are added
[self.wordsView addSubview:wordXXX];
}
if (self.game.move.wordYYY) {
WordView *wordYYY = [self wordViewForTypeYYY];
wordYYY.tag = kWordYYY;
[self.wordsView addSubview:wordYYY];
}
}
// Remove the old words if they exist
- (void)removeWords
{
WordView *wordXXX = (WordView *)[self.wordsView viewWithTag:kWordXXX];
WordView *wordYYY = (WordView *)[self.wordsView viewWithTag:kWordYYY];
if (wordXXX) {
[wordXXX removeFromSuperview];
}
if (wordYYY) {
[wordYYY removeFromSuperview];
}
}
Here is how the subviews are created. I'm not particularly proud of this code and it needs refactoring but I need to understand why is not working before:
- (WordView *)wordViewWithFrame:(CGRect)frame andType:(WordType)type
{
WordView *wordView = nil;
if (type == SystemWord) {
frame.origin.y += 15;
wordView = [[SystemWordView alloc] initWithFrame:frame];
} else if (type == StartWord) {
wordView = [[StartWordView alloc] initWithFrame:frame];
} else if (type == UserWord) {
wordView = [[UserWordView alloc] initWithFrame:frame];
} else {
wordView = [[RivalWordView alloc] initWithFrame:frame];
}
return wordView;
}
- (WordView *)wordViewForTypeXXX
{
WordType type = self.game.move.wordType;
WordView *wordView = nil;
CGRect wordViewFrame = CGRectMake(0,
0,
self.scoreView.frame.size.width,
35);
wordView = [self wordViewWithFrame:wordViewFrame andType:type];
wordView.word = self.game.move.word;
return wordView;
}
- (WordView *)wordViewForTypeYYY
{
WordType type = self.game.move.wordType;
CGFloat y = self.game.move.word ? 35 : 0;
WordView *wordView = nil;
CGRect wordViewFrame = CGRectMake(0,
y,
self.scoreView.frame.size.width,
35);
wordView = [self wordViewWithFrame:wordViewFrame andType:type];
wordView.word = self.game.move.word;
if (self.game.move.word && [wordView isKindOfClass:[PlayerWordView class]]) {
((PlayerWordView *)wordView).points = [NSNumber numberWithInteger:self.game.move.points];
}
return wordView;
}
This is working for a while and then crashes. I mean, the views are removed and added a few times and it seems like everything is OK but after a while the app gets the EXC_BAD_ACCESS.
Any help will be eternally appreciated!
PS: Sorry for my English
EDIT: I can't use Zombies on the device and I can't see the stacktrace.
This is what I get if I type "bt" on the lldb after I get the EXC_BAD_ACCESS:
* thread #1: tid = 0x2503, 0x3bb735b0 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x11d52465)
frame #0: 0x3bb735b0 libobjc.A.dylib`objc_msgSend + 16
frame #1: 0x3473f6fe Foundation`probeGC + 62
frame #2: 0x34745706 Foundation`-[NSConcreteMapTable removeObjectForKey:] + 34
frame #3: 0x360b3d5c UIKit`-[_UIImageViewPretiledImageWrapper dealloc] + 80
frame #4: 0x3bb75488 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 168
frame #5: 0x33e16440 CoreFoundation`_CFAutoreleasePoolPop + 16
frame #6: 0x347ea184 Foundation`__NSThreadPerformPerform + 604
frame #7: 0x33ea8682 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
frame #8: 0x33ea7ee8 CoreFoundation`__CFRunLoopDoSources0 + 212
frame #9: 0x33ea6cb6 CoreFoundation`__CFRunLoopRun + 646
frame #10: 0x33e19ebc CoreFoundation`CFRunLoopRunSpecific + 356
frame #11: 0x33e19d48 CoreFoundation`CFRunLoopRunInMode + 104
frame #12: 0x379dd2ea GraphicsServices`GSEventRunModal + 74
frame #13: 0x35d2f300 UIKit`UIApplicationMain + 1120
frame #14: 0x0005bd40 Dr. Cuaicap`main(argc=1, argv=0x2fda8d10) + 116 at main.m:16
frame #15: 0x3bfafb20 libdyld.dylib`start + 4
Discussion from comments -
You're right, zombies will work only in simulator - though I suspect the crash will reproduce in simulator as well. Very few cases where app will crash on device, but not on simulator. Maybe give it another try ensuring you use simulator of the same iOS version. I couldn't find any info on _UIImageViewPretiledImageWrapper, but that will be the key to solving this.
This answer suggests _UIImageViewPretiledImageWrapper
is related to -[UIImage resizableImageWithCapInsets:]
.
Comments from OP:
At some point I was creating a UIImageView based on a resizable UIImage but I was setting the image before setting the final size of the UIImageView. This seems to somehow end up corrupting memory due to the resizing of the UIImage