Cocoa terse backtrace using NSRegularExpression

I recently wanted to improve some logging code to print out the callers to some methods (I’m tracking down errant retains / releases). [NSThread callStackSymbols] is excellent and easy, but I found that simply logging a chunk of the call stack was too verbose. I wanted to include callers from just my app and also parse it to include just the method names, ie. to turn this output from callStackSymbols:

0   MyApp                               0x0005da3a -[UnsavedPhoto retain] + 136,
1   CoreFoundation                      0x02b75fac CFRetain + 92,
2   CoreFoundation                      0x02b7ea52 __CFBasicHashAddValue + 98,
3   CoreFoundation                      0x02b86219 CFDictionarySetValue + 105,
4   CoreFoundation                      0x02c620d5 -[__NSCFDictionary setObject:forKey:] + 117,
5   MyApp                               0x000d631e -[OrderedDictionary setObject:forKey:] + 165,
6   MyApp                               0x0005c668 -[PhotoSaver addPhoto:] + 224,

into just this: "-[OrderedDictionary removeObjectForKey:] < -[PhotoSaver discardPhoto:]".

I made a method in NSString category (um, because it generates a string), which I call like this:

logthis = [NSString appBacktraceOfDepth:2 fromStackSymbols:[NSThread callStackSymbols]];

Here it is, fairly concise. Note that it does something reasonable if pre-iOS4 and NSRegularExpression is not present (namely just returns a chunk of the line from the top stack frame), fires an assertion if the regular expression fails (because it shouldn’t), and restarts without the restriction of matching only app calls if there are none in the stack before main.

I mostly decided to blog this because I didn’t initially spot any good examples of using NSRegularExpression to extract capture groups. The answer was to use firstMatchInString:options:range: to get a NSTextCheckingResult and then replacementStringForResult:inString:offset:template:.

@interface NSString (backtraceOfDepth_fromStackSymbols)
+ (NSString *)appBacktraceOfDepth:(int)depth fromStackSymbols:(NSArray *)frames;
+ (NSString *)backtraceOfDepth:(int)depth fromStackSymbols:(NSArray *)frames;
+ (NSString *)backtraceOfDepth:(int)depth fromStackSymbols:(NSArray *)frames matching:(NSString *)from;
@implementation NSString (backtraceOfDepth_fromStackSymbols)
+ (NSString *)appBacktraceOfDepth:(int)depth fromStackSymbols:(NSArray *)frames { return [self backtraceOfDepth:depth fromStackSymbols:frames matching:[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]]; }
+ (NSString *)backtraceOfDepth:(int)depth fromStackSymbols:(NSArray *)frames { return [self backtraceOfDepth:depth fromStackSymbols:frames matching:nil]; }
+ (NSString *)backtraceOfDepth:(int)depth fromStackSymbols:(NSArray *)frames matching:(NSString *)from {
  NSRegularExpression *regex = nil;
  if (!NSClassFromString(@"NSRegularExpression") || !(regex = [NSRegularExpression regularExpressionWithPattern:@"[0-9]+ +(.+[^ ]) +0x[0-9a-f]+ (.+) \\+ [0-9a-f]+" options:0 error:nil]))
    return [[frames objectAtIndex:1] substringFromIndex:51]; // no regex, be lame and rely on column counts
  for (int goodframes=0, framenum=1; goodframes < depth && framenum < [frames count]; ++framenum) {
    NSString *frame = [frames objectAtIndex:framenum];
    NSTextCheckingResult *match = [regex firstMatchInString:frame options:0 range:NSMakeRange(0, [frame length])];
    if (!match)
      NSAssert1(NO, @"unparsed stack frame: %@", frame);
    if (from && ![from isEqualToString:[regex replacementStringForResult:match inString:frame offset:0 template:@"$1"]])
    NSString *caller = [regex replacementStringForResult:match inString:frame offset:0 template:@"$2"];
    if (from && goodframes == 1 && [caller isEqualToString:@"main"])
      return [self backtraceOfDepth:depth fromStackSymbols:frames matching:nil]; // no useful calls from us, take ones from anyone instead
    result = !result ? caller : [result stringByAppendingFormat:@" < %@", caller];
  return result ? result : @"?";

Found my TTPhotoView bug

TTPhotoView bug ExampleThis is a tip for anyone hacking TTPhotoViewController, and specifically TTPhotoView. If you’re finding swiping only rotating between three photos, and if there’s a mix of landscape and portrait then it gets a redraw bug that looks like this:

then you’ve probably disabled updateLayer (perhaps because it wasn’t working for photos with orientations other than Up) but didn’t replace it with [self setNeedsDisplay]. You’re welcome.
Also, for anyone else trying to untie TTPhotoView from TTURLCache and instead work with images you can’t load asynchronously: don’t. Instead make it work with fake URLs, force-feeding the cache as you go. Hey ALAssetLibrary, thanks for being async in all the wrong places.

A multitouch UIPanGestureRecognizer trick

Even though I wanted our app to still support iPhone OS 3.1, I realized that since our extra gesture-based features are iOS4 only, I could use UIGestureRecognizer for those. I already had a UIView subclass I was using as a transparent overlay where I implemented my custom tap detection and drag tracking. What I did was bite the bullet and replaced it completely with a plain UIView with gesture recognizers attached. In a few minutes I was able to duplicate all the behaviour that took lots of trial and error to get right. Kudos to everyone at Apple who worked on UIGestureRecognizer.

One part of the new behaviour I wanted to try out was a drag that started with two fingers. It looked like UIPanGestureRecognizer was working but it was ending the gesture when one finger was lifted. I wanted to support starting with a two-finger tap but then still track if the user lifts one finger.

I thought I’d have to implement a custom recognizer subclass when I came upon a little hack that seems to work: In my action method, I change the minimumNumberOfTouches property from 2 to 1 on the fly when called in the Began state, then set it back to 2 when called with Ended or Cancelled.

- (void)drag2Fingers:(UIPanGestureRecognizer *)sender {
    if (sender.state == UIGestureRecognizerStateBegan) {
        sender.minimumNumberOfTouches = 1;
    } else if (sender.state == UIGestureRecognizerStateEnded ||
               sender.state == UIGestureRecognizerStateCancelled) {
        sender.minimumNumberOfTouches = 2;

This seems to work but maybe it’s presumptuous to think it’s always going to. I think I’ll be doing that custom recognizer subclass in a future update.

Intercepting UINavigationController back button

I was looking for a simple way to add a UINavigationBar to my a modal view controller that presents a normal (pointy) Back button as if there was a root view controller to go back to. It would be as if my outer view was the root view controller.

Now I know one legitimate way of doing this would be to wrap a UINavigationController around my existing outer view an hiding the navigation bar, but that would be an intrusive change to my view class that I didn’t want to change. Another way would be to create UINavigationBar and have my view controller manage it, but this seemed like extra work I was hoping to avoid. I was looking for a self-contained, sneaky way of using a UINavigationController within my modal view controller class’ code.

The two issues were: giving the navigation bar a pointy Back button, and getting called when the user presses the button.

Since it seemed impossible to create a left-pointing button on-demand, I figured the first issue could most easily be solved by making a fake root view controller then pushing my modal view controller onto the UINavigationController’s stack. To keep this detail private to my modal view controller, I made a public method containerViewController that assembles this UINavigationController & fake root view. My caller uses it like:

modalViewController = [[MyModalTableViewController alloc] init];
modalViewController.delegate = whatever;
// and other setup, notification observers added, and such
[myViewController presentModalViewController:
    [modalViewController containerViewController] animated:YES];
// rather than presenting modalViewController itself

To intercept the back button, I searched the net to find a solution. One of the top hits suggests subclassing UINavigationController and overriding - [UIViewController popViewControllerAnimated:]. This worked but I was saw that the UINavigationController’s pop animation back to the fake root controller was still happening, which was interfering with my delegate’s for dismissing the modal view. I needed to find another override that could prevent the pop animation.

I noticed that UINavigationController is documented to be the delegate of UINavigationBar, even though it isn’t declared to be in the header file. I figured this meant UINavigationController implemented the UINavigationBarDelegate methods, one of which is - [UINavigationBarDelegate navigationBar:shouldPopItem:] and I found that indeed my sneaky UINavigationController subclass did indeed get a call to this method.

Here the code I made to do what I wanted:

@interface UINavigationController (UndocumentedUINavigationBarDelegateMethod)
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
@interface SneakyNavigationController : UINavigationController

@implementation MyModalTableViewController
. . .
- (UIViewController *)containerViewController {
  UIViewController *fakeRootView = [[[UIViewController alloc] init] autorelease];
  fakeRootView.navigationItem.title = NSLocalizedString(@"Back", @"Modal TableView Back button");
  MyModalTableViewController *navController = [[[MyModalTableViewController alloc] initWithRootViewController:fakeRootView] autorelease];
  navController.navigationBar.barStyle = UIBarStyleBlack;
  [navController pushViewController:self animated:NO];
  return navController;

@implementation SneakyNavigationController
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
  UIViewController *viewController = nil;
  for (viewController in self.viewControllers)
    if (viewController.navigationItem == item)
  if ([viewController isKindOfClass:[MyModalTableViewController class]]) {
    [(ManageSocialAccountsViewController *)viewController close];
    return NO;
  return [super navigationBar:navigationBar shouldPopItem:item];

Mysterious twitpic authentication problem fixed

Error 401 from twitpic “Could not authenticate you (header rejected by twitter).”

Thanks to and i found that the OAuth header must be created with “GET” even though the request to twitpic is sent as a “POST”. That was unexpected and it probably would have taken me quite a while to find this just going by the documentation.

Improvement to Trevor Harmon’s UIImage+Resize.m

from the “Resize a UIImage the right way” post on Trevor’s Bike Shed
Trevor says:
December 24, 2009 at 9:18 pm
True; I didn’t bother with handling the imageRotation setting in croppedImage. I changed the method to include a source code comment explaining this.
If anyone would like to contribute an improved croppedImage function that fixes this oversight, I’d be happy to include it in the distribution.
I found a good post on Robert Clark’s Niftybean blog, “Selecting regions from rotated EXIF images on iPhone” with code for adapting the a crop rect based on a UIImage’s imageRotation. Adding this to croppedImage I then only needed to finally rotate the resulting CGImageRef, for I only needed to slightly generalize the private resizeImage:transform:… helper method and then use it to rotate the resulting cropped image to match imageRotation. It would be much better if the original UIImage’s metadata, imageRotation included, could be retained so the final rotation step could be avoided.
// Returns a copy of this image that is cropped to the given bounds.
// The bounds will be adjusted using CGRectIntegral.
// JPMH-This method no long ignores the image's imageOrientation setting.
- (UIImage *)croppedImage:(CGRect)bounds {
    CGAffineTransform txTranslate;
    CGAffineTransform txCompound;
    CGRect adjustedBounds;
    BOOL drawTransposed;
    switch (self.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            txTranslate = CGAffineTransformMakeTranslation(self.size.width, self.size.height);
            txCompound = CGAffineTransformRotate(txTranslate, M_PI);
            adjustedBounds = CGRectApplyAffineTransform(bounds, txCompound);
            drawTransposed = NO;
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            txTranslate = CGAffineTransformMakeTranslation(self.size.height, 0.0);
            txCompound = CGAffineTransformRotate(txTranslate, M_PI_2);
            adjustedBounds = CGRectApplyAffineTransform(bounds, txCompound);
            drawTransposed = YES;
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            txTranslate = CGAffineTransformMakeTranslation(0.0, self.size.width);
            txCompound = CGAffineTransformRotate(txTranslate, M_PI + M_PI_2);
            adjustedBounds = CGRectApplyAffineTransform(bounds, txCompound);
            drawTransposed = YES;
            adjustedBounds = bounds;
            drawTransposed = NO;
    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], adjustedBounds);
    UIImage *croppedImage;
    if (CGRectEqualToRect(adjustedBounds, bounds))
        croppedImage = [UIImage imageWithCGImage:imageRef];
        croppedImage = [self resizedImage:imageRef
                                transform:[self transformForOrientation:bounds.size]
    return croppedImage;
Where the resizeImage: utility method was change to take a CGImageRef as a parameter instead of always using self.CGImage:
- (UIImage *)resizedImage:(CGImageRef)imageRef
- (CGAffineTransform)transformForOrientation:(CGSize)newSize;