Core Animation Warning: "uncommitted CATransaction" - iphone

I am getting the following warning with Device only:
CoreAnimation: warning, deleted thread with uncommitted CATransaction; created by:
0 QuartzCore 0x3763c65d <redacted> + 220
1 QuartzCore 0x3763c541 <redacted> + 224
2 QuartzCore 0x3763fe2f <redacted> + 30
3 QuartzCore 0x3763b9bf <redacted> + 318
4 QuartzCore 0x3763b87b <redacted> + 50
5 QuartzCore 0x3763b80b <redacted> + 538
6 MyApp 0x000df597 -[CommonClass orangeGradient:] + 382
7 MyApp 0x000f70e1 -[HomeViewController updateStatusBar] + 1700
8 MyApp 0x000f61bd -[HomeViewController statusRecieved] + 224
9 MyApp 0x000cd323 -[CommonClass statusReceivedFromServer] + 142
10 MyApp 0x000d833d -[CommonClass accountStatus] + 7416
11 Foundation 0x35a3767d <redacted> + 972
12 libsystem_c.dylib 0x35c9a311 <redacted> + 308
13 libsystem_c.dylib 0x35c9a1d8 thread_start + 8
My Method that is on top of the stack is as follows:
- (void)orangeGradient: (UILabel *)fillLabel {
#synchronized([CommonClass class]) {
CAGradientLayer * gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = fillLabel.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[UIColorFromRGB(0xfbb250) CGColor],(id)[UIColorFromRGB(0xf47c2a) CGColor], nil];
[fillLabel.layer addSublayer:gradientLayer];
}
}
Any idea on why is this coming, and how I can fix this?

It looks like orangeGradient: is called from a background thread. Core Animation groups all changes into CATransactions. Usually this is done automatically from the run loop. On background threads there is (usually) no run loop, so you have to create the transaction yourself:
- (void)orangeGradient: (UILabel *)fillLabel {
#synchronized([CommonClass class]) {
[CATransaction begin];
CAGradientLayer * gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = fillLabel.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[UIColorFromRGB(0xfbb250) CGColor],(id)[UIColorFromRGB(0xf47c2a) CGColor], nil];
[fillLabel.layer addSublayer:gradientLayer];
[CATransaction commit];
}
}
There's another issue: UIKit is not thread safe. You can't call bounds on a UILabel on a background thread.

Check anywhere in your code there is an uncommitted CATransaction. You will need to perform this operation following way;
[CATransaction begin];
[CATransaction setDisableActions:YES];
[commonClassObject orangeGradient:lblFill];
[CATransaction commit];
[EDIT]
Basically this problem happens when NSOperation completes before CoreAnimation is done performing. There must be some relation between these two class in somewhere in your code you need to figure that out.

I'll just add that I had the same problem with initWithFrame from a background threads.
Seems that since iOS 7 and upwards, some calls to UIKit must be done under the main thread, and cannot be called from a background thread.
Also note that after several warnings like this, my app stopped responding completely. So better not ignore such warning.

Related

Exception / Crash When Merging CoreData Managed Contexts

Im getting the following exception when attempting to merge a managed context (running on a background thread) with my main managed context (on mainthread). I cant seem to catch the exception in my own #try expression. Does anyone have any insight into this issue?
I'm using the default merge policy but im not sure this is correct - this issue is very intermittent - happens rarely but is causing my app to crash.
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x00000000, 0x00000000
Crashed Thread: 0
Last Exception Backtrace:
0 CoreFoundation 0x37e3b8bf __exceptionPreprocess + 163
1 libobjc.A.dylib 0x319211e5 objc_exception_throw + 33
2 CoreData 0x344b7ea5 -[NSSQLiteStatement cachedSQLiteStatement] + 1
3 CoreData 0x344b774f -[NSSQLiteConnection prepareSQLStatement:] + 55
4 CoreData 0x3455b049 -[NSSQLChannel selectRowsWithCachedStatement:] + 61
5 CoreData 0x34586d63 newFetchedRowsForFetchPlan_MT + 783
6 CoreData 0x344bfb07 -[NSSQLCore newRowsForFetchPlan:] + 351
7 CoreData 0x34565011 -[NSSQLCore fetchRowForObjectID:] + 1005
8 CoreData 0x344d1a57 -[NSSQLCore newValuesForObjectWithID:withContext:error:] + 195
9 CoreData 0x344d0f83 _PFFaultHandlerLookupRow + 423
10 CoreData 0x3450e111 -[NSFaultHandler fulfillFault:withContext:] + 25
11 CoreData 0x34518999 -[NSManagedObject(_NSInternalMethods) _newPropertiesForRetainedTypes:andCopiedTypes:preserveFaults:] + 77
12 CoreData 0x345178ef -[NSManagedObject(_NSInternalMethods) _newAllPropertiesWithRelationshipFaultsIntact__] + 79
13 CoreData 0x345284db -[NSManagedObjectContext(_NSInternalChangeProcessing) _establishEventSnapshotsForObject:] + 47
14 CoreData 0x3452694b -[NSManagedObjectContext deleteObject:] + 155
15 CoreData 0x345238a1 -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] + 813
16 CoreData 0x34522c35 -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] + 189
17 myapp 0x0008f0e9 0x8d000 + 8425
18 CoreFoundation 0x37d9a22b -[NSObject performSelector:withObject:] + 43
19 Foundation 0x31d75757 __NSThreadPerformPerform + 351
20 CoreFoundation 0x37e0fb03 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
21 CoreFoundation 0x37e0f2cf __CFRunLoopDoSources0 + 215
22 CoreFoundation 0x37e0e075 __CFRunLoopRun + 653
23 CoreFoundation 0x37d914dd CFRunLoopRunSpecific + 301
24 CoreFoundation 0x37d913a5 CFRunLoopRunInMode + 105
25 GraphicsServices 0x3790ffcd GSEventRunModal + 157
26 UIKit 0x35221743 UIApplicationMain + 1091
I init the background context in start() of a nsoperation like so:
AppDelegate *appController = [[UIApplication sharedApplication] delegate];
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[appController setPersistentStore:_managedObjectContext];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedDeletedObjects:) name:NSManagedObjectContextDidSaveNotification object:_managedObjectContext];
I also set up a notification event which is called when objects are deleted on the background managed context, the callback then does:
-(void)receivedDeletedObjects:(NSNotification *)note
{
AppDelegate *appController = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [self managedObjectContext];
[mainContext performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:) withObject:note waitUntilDone:NO];
}
Thats pretty much the code. I have 4 different background threads each with its own managed context doing the same thing merging with the main context in this manner. Im wondering multiple threads are getting into mergeChangesFromContextDidSaveNotification at the same time but this shouldnt be the case as it is always called on the mainthread.
How about this:
(all in AppDelegate, call freshContextForBackgroundTask from a background Thread and use save: to trigger the merge.) Note syncObj is a plain NSObject instance needed for synchronization within the app delegate when using many background threads (or just have bad luck with scheduling)
-(NSManagedObjectContext*) freshContextForBackgroundTask {
#synchronized(syncObj) {
NSManagedObjectContext* r = [[NSManagedObjectContext alloc] init];
[r setPersistentStoreCoordinator:self.managedObjectContext.persistentStoreCoordinator];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:r];
return r;
}
}
- (void)backgroundContextDidSave:(NSNotification *)notification {
/* Make sure we're on the main thread when updating the main context */
//NSLog(#"merging change: %#",notification);
dispatch_async(dispatch_get_main_queue(), ^{
NSManagedObjectContext *context = [self managedObjectContext];
// this for loop may not be needed for your purpose.
for(NSManagedObject *object in [[notification userInfo] objectForKey:NSUpdatedObjectsKey]) {
[[context objectWithID:[object objectID]] willAccessValueForKey:nil];
}
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}
Your approach seems strange. Especially when you set your app delegate's persistent store to the (nil) one of the freshly init'ed context.

iPhone: SIGSEGV occurs seemingly randomly (ViewDidLoad, setFont, etc.) on device, never in simulator

First, thanks in advance for taking the time to read through this. I have been searching for an answer to this issue for a few days but I cannot seem to find what I am looking for. I know that SIGSEGV errors (or EXEC_BAD_ACCESS faults) are usually caused by memory management issues but after hours of looking at the code, I can't seem to find an error. The static analyzer hasn't found anything either.
As the title says, this error only occurs on the device (never in the simulator) and doesn't happen every time the given view loads. Obviously this means that I can't use NSZombie to debug what is going wrong.
Let's start with the crash dump log:
0 myApp 0x00036659 +[TFCrashHandler backtrace] + 428
1 myApp 0x00036a6f TFSignalHandler + 54
2 libsystem_c.dylib 0x32f48539 _sigtramp + 48
3 UIKit 0x365701df -[UIView(Rendering) setNeedsDisplay] + 86
4 UIKit 0x36598269 -[UILabel setFont:] + 232
5 myApp 0x000190eb +[BruUISettings applyLabelDetailStyle:] (BruUISettings.m:130)
6 myApp 0x0001aa57 -[ActiveChallengeViewController viewDidLoad] (ActiveChallengeViewController.m:115)
7 UIKit 0x365a57ff -[UIViewController view] + 166
8 UIKit 0x365b1c39 -[UIViewController contentScrollView] + 24
9 UIKit 0x365b1aa9 -[UINavigationController _computeAndApplyScrollContentInsetDeltaForViewController:] + 36
10 UIKit 0x365b198f -[UINavigationController _layoutViewController:] + 34
11 UIKit 0x365b115b -[UINavigationController _startTransition:fromViewController:toViewController:] + 318
12 UIKit 0x365b0f53 -[UINavigationController _startDeferredTransitionIfNeeded] + 250
13 UIKit 0x365a5673 -[UINavigationController pushViewController:transition:forceImmediate:] + 806
14 UIKit 0x365a5349 -[UINavigationController pushViewController:animated:] + 36
15 myApp 0x00015dc7 -[ChallengesHomeViewController tableView:didSelectRowAtIndexPath:] (ChallengesHomeViewController.m:325)
16 UIKit 0x3661f565 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 944
17 UIKit 0x36697ce7 -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 158
18 Foundation 0x31f7a943 __NSFireDelayedPerform + 414
19 CoreFoundation 0x34ab1a63 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 14
20 CoreFoundation 0x34ab16c9 __CFRunLoopDoTimer + 364
21 CoreFoundation 0x34ab029f __CFRunLoopRun + 1206
22 CoreFoundation 0x34a334dd CFRunLoopRunSpecific + 300
23 CoreFoundation 0x34a333a5 CFRunLoopRunInMode + 104
24 GraphicsServices 0x351a7fed GSEventRunModal + 156
25 UIKit 0x3659a743 UIApplicationMain + 1090
26 myApp 0x000026c5 main (main.m:13)
27 myApp 0x0000265c start + 52
Note, I am using TestFlight from testflightapp.com and it is there exception handler at the top of the stack.
Here is the code that is creating and pushing the view controller in question:
CDAcceptedChallenge *challengeAtIndex = [self.activeChallenges objectAtIndex:[indexPath row]];
ActiveChallengeViewController *view = [[ActiveChallengeViewController alloc] initWithNibName:#"ActiveChallengeViewController"
bundle:nil
activeChallenge:challengeAtIndex];
view.leaveChallengeTarget = self;
[self.navigationController pushViewController:view animated:YES];
[view release];
This code is running inside of a didSelectRowAtIndex method, if that matters at all. This view controller has a bunch of member variables and IBOutlets which reference objects in the nib but here are the ones that correlate to the object mentioned in this error:
in .h:
UILabel *helpLabel;
...
#property (nonatomic, retain) IBOutlet UILabel *helpLabel;
in .m:
#synthesize helpLabel;
I am positive that helpLabel is bound to the correct object in the nib because it does get the correct styling when this doesn't blow up. Next, here is the init method for this view controller:
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
self.activeChallenge = inActiveChallenge;
}
return self;
Here is the viewDidLoad method where we will sometimes see the crash (bolded). Note, there is a lot going on here but nothing touches the self.helpLabel which is what eventually triggers the crash.
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:FALSE];
self.navigationItem.title = #"Details";
// overlay disclosure creation
self.overlayDisclosure = [CustomColoredDisclosureIndicator accessoryWithColor:self.challengeNameLabel.textColor];
self.overlayDisclosure.highlightedColor = [UIColor whiteColor];
[self.overlayDisclosure addTarget:self action:#selector(overlayViewButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.overlayDisclosure];
[[BruNotificationManager instance] registerForTabUpdated:[[[BruCallback alloc] initWithTarget:self
andCallback:#selector(tabUpdated:)] autorelease]];
[[BruNotificationManager instance] registerForActiveChallengesUpdated:[BruCallback buildFromObject:self
andCallback:#selector(challengesUpdatedLocally)]];
[[BruNotificationManager instance] registerForEarnedRewardsUpdated:[BruCallback buildFromObject:self
andCallback:#selector(rewardsUpdatedLocally:)]];
[super refreshEarnedRewardsForChallenge:self.activeChallenge.challenge.identifier];
[super refreshTabForChallenge:self.activeChallenge.challenge.identifier];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(addBeer)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
// Request that the object download its image
BruCallback *imageCallback = [BruCallback buildFromObject:self andCallback:#selector(imageRequestCompleted:withData:)];
self.challengeImageView.image = [self.activeChallenge.challenge retrieveImage:self.bruConnection withCallback:imageCallback];
// Create our help label
**[BruUISettings applyLabelDetailStyle:self.helpLabel];**
self.helpLabel.textColor = AccentSecondaryColor;
self.overlayView.alpha = [BruUISettings standardOverlayViewAlpha];
[BruUISettings applyTableStyle:self.rewardsTable];
[BruUISettings applyScrollViewStyle:self.scrollView];
self.needToUpdateTab = YES;
self.needToUpdateRewards = YES;
self.needToUpdateChallenge = NO;
Here is a trimmed down version of the viewDidUnload method, though I don't see why it would matter in this case:
...
self.helpLabel = nil;
...
[super viewDidUnload];
Finally, my [BruUISettings applyLabelDetailStyle] is a method that applies a centralized style to my label. There are a bunch of places in my code that use this function so that I can change all of the styles for those labels by just changing the one function.
+ (void) applyLabelDetailStyle:(UILabel *)inLabel
{
inLabel.font = [UIFont fontWithName:#"Helvetica Neue" size:12.0];
inLabel.textColor = [UIColor darkGrayColor];
inLabel.backgroundColor = [UIColor clearColor];
}
Any thoughts as to what could be happening here would be greatly appreciated. Thanks again.
After a lot of debugging I found that this was actually due to a memory corruption being caused by the QSStrings library which I was using to generate base64 encoded strings. The crashes seemed to happen randomly but only after this library was invoked. When I switched to a different MD5 library, the issue went away. If you are using this library, I would suggest finding a new one to do base64 encoding. I moved to the one found here: http://www.cocoadev.com/index.pl?BaseSixtyFour
I think the problem happens because you have something wrong in your view that owns your label (view of your ActiveChallengeViewController).
When you set the font of your label, that invokes the setNeedsDisplay of your parent view and because something is wrong, setNeedsDisplay crashs.
Your line :
self.challengeImageView.image = [self.activeChallenge.challenge retrieveImage:self.bruConnection withCallback:imageCallback];
Is it a synchronous connection to retrieve the image ?
Maybe the problem happen here when sometimes the image isn't retrieved from your connection and so the image property of your image view is bad.
But i am not sure because setNeedsDisplay doesn't directly invoke the redraw of the view so i don't know if the debugger catches the error just the after setNeedsDisplay call.
Edit :
I think this answer is bad, if image is not ok, the crash must happen when the imageView image property retains the image but i let the answer in the case of it could help you to point to the real problem.

How to debug iPhone app crash log. Crash only in App Store purchased version, not in development

I received the first crash report but I am not able to get what is the problem.
Fortunately, since this crash came from a colleague of mine, I can test the error directly on his iPhone and install a development version on his phone.
However, the strange thing is that when I install and run the development version on his device from my computer there is no crash; the crash appear only when the application is downloaded and installed from the App Store.
Until now I could not reproduce the error on any other device (the affected device is an iPhone 3G firmware 4.2.1 and all the operating system run very slowly).
The crash happen after that a row is selected from a UITableViewController (ItemsListTableViewController), at this point a detail view should appear, but the application crash during the construction of the detail view (ItemDetailViewController) in viewDidLoad.
In particular the problem seem to be localized within the class LocationPathView (a subview of ItemDetailViewController's view).
ItemDetailViewController is a subclass of UITableViewController and load its tableHeaderView from nib in viewDidLoad with:
[[NSBundle mainBundle] loadNibNamed:#"ItemDetailHeaderView" owner:self options:nil];
(LocationPathView is part of the tableHeaderView)
When the nib file is loaded, LocationPathView::awakeFromNib is called and then LocationPathView::layoutSubviews is called. The crash seem to be originated from layoutSubviews.
Within layoutSubviews I perform an animation:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
Could this animation create for some reasons the crash on slow device? Is correct to animate within layoutSubviews?
I would be grateful for any help,
Marco
Date/Time: 2011-04-30 12:50:36.972 +0200
OS Version: iPhone OS 4.2.1 (8C148)
Report Version: 104
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x00000000, 0x00000000
Crashed Thread: 0
Thread 0 Crashed:
0 libSystem.B.dylib 0x35de3ad0 __kill + 8
1 libSystem.B.dylib 0x35de3abe kill + 4
2 libSystem.B.dylib 0x35de3ab2 raise + 10
3 libSystem.B.dylib 0x35dfad5e abort + 54
4 libstdc++.6.dylib 0x374f2a00 __gnu_cxx::__verbose_terminate_handler() + 588
5 libobjc.A.dylib 0x32d9d8d8 _objc_terminate + 160
6 libstdc++.6.dylib 0x374f0100 __cxxabiv1::__terminate(void (*)()) + 76
7 libstdc++.6.dylib 0x374f0178 std::terminate() + 16
8 libstdc++.6.dylib 0x374f02a0 __cxa_throw + 100
9 libobjc.A.dylib 0x32d9bf28 objc_exception_throw + 104
10 CoreFoundation 0x3759dabc +[NSException raise:format:arguments:] + 64
11 CoreFoundation 0x3759daf0 +[NSException raise:format:] + 24
12 QuartzCore 0x33d9409c CALayerSetPosition(CALayer*, CA::Vec2<double> const&, bool) + 180
13 QuartzCore 0x33d93fd8 -[CALayer setPosition:] + 40
14 QuartzCore 0x33d93efc -[CALayer setFrame:] + 444
15 UIKit 0x358d92c8 -[UIView(Geometry) setFrame:] + 248
16 UIKit 0x3592dde0 -[UIButton setFrame:] + 120
17 MyApp 0x0003432a -[LocationPathView layoutSubviews] (LocationPathView.m:101) <<<<<<<<<<<<<<<<<<<<<<<<<<<
18 UIKit 0x358ec704 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 32
19 CoreFoundation 0x37538f72 -[NSObject(NSObject) performSelector:withObject:] + 18
20 QuartzCore 0x33d9a128 -[CALayer layoutSublayers] + 176
21 QuartzCore 0x33d99db0 CALayerLayoutIfNeeded + 192
22 QuartzCore 0x33d99cd8 -[CALayer layoutIfNeeded] + 108
23 UIKit 0x3598ee38 -[UIView(Hierarchy) layoutIfNeeded] + 24
24 UIKit 0x359fbabc -[UIButton titleLabel] + 76
25 MyApp 0x0001fee8 -[ItemDetailViewController viewDidLoad] (ItemDetailViewController.m:148) <<<<<<<<<<<<<<<<<<<<<<<<<<<
26 UIKit 0x35926e58 -[UIViewController view] + 152
27 UIKit 0x35937f2c -[UIViewController contentScrollView] + 24
28 UIKit 0x35937d4c -[UINavigationController _computeAndApplyScrollContentInsetDeltaForViewController:] + 36
29 UIKit 0x35937bf8 -[UINavigationController _layoutViewController:] + 28
30 UIKit 0x35937474 -[UINavigationController _startTransition:fromViewController:toViewController:] + 336
31 UIKit 0x35937288 -[UINavigationController _startDeferredTransitionIfNeeded] + 256
32 UIKit 0x35926c44 -[UINavigationController pushViewController:transition:forceImmediate:] + 904
33 UIKit 0x359268a8 -[UINavigationController pushViewController:animated:] + 36
34 MyApp 0x00020a9a -[ItemsListTableViewController viewItemDetail:startEditable:] (ItemsListTableViewController.m:717)
35 MyApp 0x00022110 -[ItemsListTableViewController tableView:didSelectRowAtIndexPath:] (ItemsListTableViewController.m:241) <<<<<<<<<<<<<<<<<<<<<<<<<<<
36 UIKit 0x3595bf4c -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 884
37 UIKit 0x35a5da9c -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 196
38 Foundation 0x351724d4 __NSFireDelayedPerform + 360
39 CoreFoundation 0x375522fe __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 10
40 CoreFoundation 0x37551cd2 __CFRunLoopDoTimer + 982
41 CoreFoundation 0x37521a8a __CFRunLoopRun + 1178
42 CoreFoundation 0x37521504 CFRunLoopRunSpecific + 220
43 CoreFoundation 0x37521412 CFRunLoopRunInMode + 54
44 GraphicsServices 0x33e76d1c GSEventRunModal + 188
45 UIKit 0x3591d574 -[UIApplication _run] + 580
46 UIKit 0x3591a550 UIApplicationMain + 964
47 MyApp 0x00002fce main (main.m:14)
48 MyApp 0x00002f98 0x1000 + 8088
ItemsListTableViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Item *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self viewItemDetail:item startEditable:NO]; <<<<<<<<<<<<<<<<<<<
}
- (void)viewItemDetail:(Item *)item startEditable:(BOOL)editable {
ItemDetailViewController *controller = [[ItemDetailViewController alloc] initWithStyle:UITableViewStyleGrouped showItem:item inItemsList:[self.fetchedResultsController fetchedObjects]];
if (editable)
controller.editing = YES;
[self.navigationController pushViewController:controller animated:YES]; <<<<<<<<<<<<<<<<<<<
[controller release];
}
ItemDetailViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = NSLocalizedString(#"Item details", #"Item details");
self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];
// Configure the tableview
self.tableView.sectionFooterHeight = 5.0f;
self.tableView.allowsSelectionDuringEditing = YES;
// Create and set the tableview header
if (headerView == nil) {
[[NSBundle mainBundle] loadNibNamed:#"ItemDetailHeaderView" owner:self options:nil]; <<<<<<<<<<<<<<<<<<<<<<<<<<<
// Item title button
itemTitleButton.titleLabel.adjustsFontSizeToFitWidth = YES;
itemTitleButton.titleLabel.minimumFontSize = 11.0f;
[itemTitleButton setImageEdgeInsets:UIEdgeInsetsMake(0.0f, itemTitleButton.bounds.size.width-29.0f, 0.0f, 0.0f)];
[itemTitleButton setTitleEdgeInsets:UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 24.0f)];
UIImage *normalImage = [[UIImage imageNamed:#"objectNameHighlighedButton.png"] stretchableImageWithLeftCapWidth:11 topCapHeight:0];
[self.itemTitleButton setBackgroundImage:normalImage forState:UIControlStateHighlighted];
[pathView addTarget:self action:#selector(moveButtonTouchUp:) forControlEvents:UILocationPathViewMoveControlEvent];
// Check if the item's path should be recalculated
if (pathView.pathList == nil) {
pathView.pathList = pathNames;
}
self.tableView.tableHeaderView = headerView;
// Save initial header height so that it can be restore navigating between items with different flags count
tableHeaderFrameHeight = headerView.frame.size.height;
}
// Enable the UIToolbar at the bottom of the view controller and create its buttons
[self.navigationController setToolbarHidden:NO animated:YES];
self.navigationController.toolbar.barStyle = UIBarStyleBlack;
self.navigationController.toolbar.translucent = YES;
// [omitted part]: create toolbar items …
// Restore editing status if the view was unloaded
if (viewUnloaded && self.editing) {
viewUnloaded = NO;
// this will force setEditing:YES to be called on all tableView's subviews! (categorySectionHeaderView...)
[self.tableView setEditing:YES];
// ...not for tableheader!
editableImageView.editing = YES;
pathView.editing = YES;
}
// Recreate item flags
CGRect headerFrame = headerView.frame;
for (FlaggedItem *flagObject in self.itemBookmarkRibbons) {
headerFrame.size.height += ITEM_SHEET_FLAG_BUTTON_HEIGHT + 8.0f;
headerView.frame = headerFrame;
UIButton *bookmarkRibbon = [self createBookmarkRibbon:flagObject];
bookmarkRibbon.frame = CGRectMake(0.0f, headerFrame.size.height-ITEM_SHEET_FLAG_BUTTON_HEIGHT-8.0f,
headerFrame.size.width, ITEM_SHEET_FLAG_BUTTON_HEIGHT);
[headerView addSubview:bookmarkRibbon];
[headerView sendSubviewToBack:bookmarkRibbon];
}
// Update the table header view height
self.tableView.tableHeaderView = headerView;
}
LocationPathView.m:
- (void)awakeFromNib {
[super awakeFromNib];
UIColor *color = [[UIColor alloc] initWithWhite:0.0f alpha:0.0f];
self.backgroundColor = color;
[color release];
[self sendSubviewToBack:backgroundView];
// create imageview rounded rect
CALayer *l = [backgroundView layer];
[l setMasksToBounds:YES];
[l setCornerRadius:10.0];
[l setBorderWidth:2.0];
UIColor *borderColor = [[UIColor alloc] initWithWhite:0.65 alpha:1.0];
[l setBorderColor:[borderColor CGColor]];
[borderColor release];
expanded = NO;
buttons = [[NSMutableArray alloc] initWithCapacity:2];
[buttons addObject:[self createButtonOfType:PVButtonTypeStart]];
[buttons addObject:[self createButtonOfType:PVButtonTypeEnd]];
}
- (void)layoutSubviews {
#define MOVE_BUTTON_WIDTH 90.0f
#define STANDARD_BUTTON_HEIGHT 22.0f
[super layoutSubviews];
CGRect contentRect = self.frame;
if (CGRectIsEmpty(contentRect))
return;
if ([buttons count] != [pathList count] && [pathList count] > 0) {
[self prepareButtons];
}
[UIView beginAnimations:nil context:nil]; <<<<<<<<<<<<<<<<<<<
[UIView setAnimationDuration:0.2];
CGFloat x = 0.0f, y = 0.0f;
CGFloat availableHSpace = 0.0f;
UIButton *button;
for (NSUInteger i=0; i<[self.buttons count]; i++) {
button = [buttons objectAtIndex:i];
if (self.isExpanded == NO && i > 0 && i <= [self.buttons count]-2)
// hide all middle buttons
button.alpha = 0.0f;
else {
button.alpha = 1.0f;
NSString *title = [pathList objectAtIndex:i];
CGSize size = [title sizeWithFont:button.titleLabel.font];
CGFloat buttonWidth = size.width + 36.0f;
// check if the button width is greater than available space, if yes set the button width equal to the available space.
// The available space is reduced in editing move to make room for the move button.
if (self.editing && moveButton)
availableHSpace = contentRect.size.width - MOVE_BUTTON_WIDTH - 3.0f - 10.0f; // 10.0=space from move button
else
availableHSpace = contentRect.size.width;
if (x + buttonWidth > availableHSpace)
buttonWidth = availableHSpace - x;
button.frame = CGRectMake(x, y, buttonWidth, STANDARD_BUTTON_HEIGHT);
[button setTitle:title forState:UIControlStateNormal];
y += STANDARD_BUTTON_HEIGHT + 3.0f; // vertical distance between buttons
}
if (self.isExpanded)
x += 14.0f; // indentation
}
// setup cyan background
backgroundView.alpha = (self.isExpanded ? 0.80f : 0.0f);
CGRect backgroundViewFrame = backgroundView.frame;
if (self.isExpanded) {
// 10.0 = 5.0=y gap between backgroundView and self + 5.0=distance between edge
backgroundViewFrame.size.height = button.frame.origin.y + button.frame.size.height + 12.0f;
}
else
backgroundViewFrame.size.height = self.frame.size.height;
backgroundView.frame = backgroundViewFrame;
[UIView commitAnimations];
// set moveButton position and make it visible (put it out of animation block to avoid frame changing animation,
// fade animation already set from caller)
if (self.editing && moveButton) {
moveButton.frame = CGRectMake(0.0f, 0.0f, MOVE_BUTTON_WIDTH, STANDARD_BUTTON_HEIGHT*2 + 3.0f); // 3.0=vertical space between buttons
moveButton.center = CGPointMake(contentRect.size.width - MOVE_BUTTON_WIDTH/2 - 3.0f, contentRect.size.height/2);
moveButton.hidden = NO;
}
}
Being an error only the app-store version makes me suspicious of a project or target setting used to build the submission binary.
Are you using a project configuration to build the submission binary? If so, the first thing I would look for is a C macro is defined incorrectly for a release version. Or perhaps it is correct at the project-level, but incorrect at the target-level. Perhaps the end of your animation block is not correctly signalled?
Building an ad-hoc version of the app with the exact same settings as your app store version would be a good test. (Code signing would be the only difference.) Delete the old app from the device and deploy it the hard way through iTunes to be sure.
It might be your animation block blocking the main transition running behind. Try to avoid these kind of situation. For example popAviewController and just after push another this kind of situation lead to crash because you are doing another transition before previous one finished. Hope you understand.

iPhone Instruments Leaks is confusing me. UIView fade out animation causes a lot of tiny leaks

I'm trying to kill all memory leaks in my iPhone 3.0 application. CLANG builds with all lights green so now I'm analyzing with Instruments.
This is easier said then done, since it is indicating hundreds of 16 byte leaks after just a few minutes of using the app. It seams to be mainly in UIKit, and the common part is that the end of the stack trace always calls [NSObject respondsToSelector]
Is this something I can ignore or what can be the reason for all these leaks?
I I can ignore them, is there a way to filter them out in Instruments, so I can detect the real leaks?
*EDIT
I managed to find the part of my code that caused the problem, but I still don't understand why.
I have a custom UIView with some text and a spinner that is visible during an async http request. When the request is done I call this method on the view:
- (void)fadeOut{
spinner.hidden = YES;
loadingLabel.hidden = YES;
messageLabel.hidden = YES;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(fadeComplete)];
[UIView setAnimationDuration:0.40];
self.alpha = 0.0;
[UIView commitAnimations];
}
- (void)fadeComplete{
[self removeFromSuperview];
}
If I instead simply do
[self removeFromSuperView] without the alpha animation, there is no leaks reported.
See the screenshot below for Instruments details.
Instruments Screenshot
A sample stack trace:
0 libobjc.A.dylib _malloc_internal
1 libobjc.A.dylib _cache_addForwardEntry
2 libobjc.A.dylib lookUpMethod
3 libobjc.A.dylib class_respondsToSelector
4 CoreFoundation -[NSObject respondsToSelector:]
5 UIKit -[UINavigationTransitionView transition:fromView:toView:]
6 UIKit -[UINavigationTransitionView transition:toView:]
7 UIKit -[UINavigationController _startTransition:fromViewController:toViewController:]
8 UIKit -[UINavigationController _startDeferredTransitionIfNeeded]
9 UIKit -[UINavigationController viewWillLayoutSubviews]
10 UIKit -[UILayoutContainerView layoutSubviews]
11 UIKit -[UIView(CALayerDelegate) _layoutSublayersOfLayer:]
12 QuartzCore -[CALayer layoutSublayers]
13 QuartzCore CALayerLayoutIfNeeded
14 QuartzCore CA::Context::commit_transaction(CA::Transaction*)
15 QuartzCore CA::Transaction::commit()
16 QuartzCore CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*)
17 CoreFoundation __CFRunLoopDoObservers
18 CoreFoundation CFRunLoopRunSpecific
19 CoreFoundation CFRunLoopRunInMode
20 GraphicsServices GSEventRunModal
21 GraphicsServices GSEventRun
22 UIKit -[UIApplication _run]
23 UIKit UIApplicationMain
24 Client main **/main.m:14
25 Client start
And another one:
0 libobjc.A.dylib _malloc_internal
1 libobjc.A.dylib _cache_addForwardEntry
2 libobjc.A.dylib lookUpMethod
3 libobjc.A.dylib class_respondsToSelector
4 CoreFoundation -[NSObject respondsToSelector:]
5 UIKit -[UIViewAnimationState animationDidStart:]
6 QuartzCore run_animation_callbacks(double, void*)
7 QuartzCore CA::timer_callback(__CFRunLoopTimer*, void*)
8 CoreFoundation CFRunLoopRunSpecific
9 CoreFoundation CFRunLoopRunInMode
10 GraphicsServices GSEventRunModal
11 GraphicsServices GSEventRun
12 UIKit -[UIApplication _run]
13 UIKit UIApplicationMain
14 Client main ***/main.m:14
15 Client start
You didn't specify if you did your checks on the simulator on an actual device, but in my experience running Leaks in the simulator is not very reliable and will report many tiny leaks in the the SDK, while running it on the device will not report any leaks.
Are you sure your delegate method is being called? I think the problem is the animation delegate method, which, according to Apple's documentation, must have this signature:
[UIView setAnimationDidStopSelector:#selector(animationFinished:finished:context:)];
And then you have a method like this in your class:
#pragma mark -
#pragma mark UIView animation delegate method
- (void)animationFinished:(NSString *)animationID
finished:(BOOL)finished
context:(void *)context
{
[self removeFromSuperview];
}
Probably your animation delegate is not being called, thus, the object is not released.
From the documentation:
setAnimationDidStopSelector:
Sets the message to send to the animation delegate when animation stops.
(void)setAnimationDidStopSelector:(SEL)selector
Parameters
selector
The message sent to the animation delegate after animations end. The default value is NULL. The selector should be of the form: - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context. Your method must take the following arguments:
animationID
An NSString containing an optional application-supplied identifier. This is the identifier that is passed to the beginAnimations:context: method. This argument can be nil.
finished
An NSNumber object containing a Boolean value. The value is YES if the animation ran to completion before it stopped or NO if it did not.
context
An optional application-supplied context. This is the context data passed to the beginAnimations:context: method. This argument can be nil.
Hope this helps!

How to ensure that UIImage is never released?

I grabbed the crash log from the iPhone:
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000c
Crashed Thread: 0
Thread 0 Crashed:
0 libobjc.A.dylib 0x30011940 objc_msgSend + 20
1 CoreFoundation 0x30235f1e CFRelease + 98
2 UIKit 0x308f4974 -[UIImage dealloc] + 36
3 CoreFoundation 0x30236b72 -[NSObject release] + 28
4 UIKit 0x30a00298 FlushNamedImage + 64
5 CoreFoundation 0x30250a20 CFDictionaryApplyFunction + 124
6 UIKit 0x30a0019c _UISharedImageFlushAll + 196
7 UIKit 0x30a00730 +[UIImage(UIImageInternal) _flushCacheOnMemoryWarning:] + 8
8 Foundation 0x3054dc7a _nsnote_callback + 178
9 CoreFoundation 0x3024ea52 _CFXNotificationPostNotification + 298
10 Foundation 0x3054b854 -[NSNotificationCenter postNotificationName:object:userInfo:] + 64
11 Foundation 0x3054dbba -[NSNotificationCenter postNotificationName:object:] + 14
12 UIKit 0x30a00708 -[UIApplication _performMemoryWarning] + 60
13 UIKit 0x30a006a0 -[UIApplication _receivedMemoryNotification] + 128
14 UIKit 0x30a005d0 _memoryStatusChanged + 56
15 CoreFoundation 0x30217410 __CFNotificationCenterDarwinCallBack + 20
16 CoreFoundation 0x3020d0aa __CFMachPortPerform + 72
17 CoreFoundation 0x30254a70 CFRunLoopRunSpecific + 2296
18 CoreFoundation 0x30254164 CFRunLoopRunInMode + 44
19 GraphicsServices 0x3204529c GSEventRunModal + 188
20 UIKit 0x308f0374 -[UIApplication _run] + 552
21 UIKit 0x308eea8c UIApplicationMain + 960
...
...
From my previous question, Can somebody give me a hand about this stacktrace in iPhone app?, I have changed my codes mainly around UIImage part. I now use [[UIImage alloc] initWithContentsOfFile ... ]. No more [UIImage imageNamed: ... ] or the like. The portion is below.
//this is a method of a subclass of UIImageView.
- (void) reviewImage: (bool) review{
NSString* st;
if (review){
NSString* origin = [NSString stringWithString: [[ReviewCardManager getInstance] getCardImageString:chosenIndex]];
NSString* stt = [origin substringToIndex: [origin length]-4];
st = [[NSString alloc] initWithString: stt];
if (myImageFlipped == nil)
myImageFlipped = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource:st ofType:#"png"]];
[self setImage:myImageFlipped];
if (notRotated){
self.transform = CGAffineTransformRotate(self.transform, [MyMath radf:rotate]);
notRotated = false;
}
}else{
st = [[NSString alloc] initWithFormat:#"sc%d", chosenNumber];
if (myImage == nil)
myImage = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource:st ofType:#"png"]];
[self setImage:myImage];
if (notRotated){
self.transform = CGAffineTransformRotate(self.transform, [MyMath radf:rotate]);
notRotated = false;
}
}
[st release];
}
I also have the UIImage already retained in the property.
#property (nonatomic, retain) UIImage* myImage, *myImageFlipped;
Memory Leaks have also been taken cared of. These variables are release in dealloc method.
I thought that I have successfully killed the bug, but it seems that I still have a rare occuring bug problem.
Based on the crash log, my application yells out "performMemoryWarning". I am just "alloc"-ing 13 .png images with the size 156 x 272. I'm confused. Those images shouldn't take that much memory to the point that it exceeds iPhone's RAM. Or is there something I am overlooking? Please advise.
To help you with memory issues and UIImages, you might want to use the imageNamed convience method of UIImage, from the docs:
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.
Alternatively, you might want to go this route if you still run into memory issues after switching to UIImage imageNamed, because there are some downsides to using the convinience method.
The problem is solved. I forgot to change UIImage at one place. Now, all UIImages are truly "alloc", no more autorelease.
FYI, if you are using [UIImage imageNamed: ... ], use "Simulate Memory Warning" on iPhone Simulator to see whether you are having a problem with it when the real device is low on memory.