Archive for the ‘Objective-C’ Category

NSDates and Fractional Seconds

Wednesday, July 18th, 2012

A recent overhaul to Wooly Tasks allowed me to manage and query for Task records quicker, more easily, and more reliably. However, I quickly ran into a silly snap when ordering lists of Tasks based on their due dates.

Would you care for another date?…

The recommended method for adding time to a NSDate is by setting up a NSDateComponents object and adding the components to the date object that you want to change. Behind the scenes, the OS will handle the cases of Daylight Savings time changes and Leap Years correctly. Simply adding a NSTimeInterval does not.

Incorrect:

NSTimeInterval oneHour = 3600; // magic number! 60 seconds * 60 minutes
NSDate *newDate = [date dateByAddingTimeInterval:oneHour];

Correct:

NSDateComponents *components = [NSDateComponents new];
[dateComponents setHour:1];
NSDate *newDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:date options:0];

The NSDateComponents class interface for setting the hour uses a NSInteger, and not a float or double. This is true of all the other components:

...
- (void)setDay:(NSInteger)v;
- (void)setHour:(NSInteger)v;
- (void)setMinute:(NSInteger)v;
...

Dates gone bad…

OK, now we know the correct way to add time to a date, let’s look at something that could bite us in the ass. In the case of Wooly Tasks, we limit due dates to have granularity of every 5 minutes. We also prepopulate a new task with a date that falls on the hour, and is at least 30 minutes from the moment the task was created. So if the current time is 12:34 when we create the task, then we’ll choose 2:00 instead of 1:00 as the due date time. We call this normalizing the due date.

The problem exists, if we create a couple of tasks and normalize the date something like:

NSDate *date = [NSDate date];
NSUInteger unitFlags = NSMinuteCalendarUnit+NSSecondCalenderUnit;
NSDateComponents *components = [[NSCalendar currentCalender] components:unitFlags fromDate:date];
NSInteger hour = ([components minute]<30) ? 1 : 2;
[components setHour:hour];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
date = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:date options:0];

This will wind the minutes and seconds back to hh:00:00, and the hour ahead by one (two if the date was less than 30 minutes from the next whole hour). Unless you examine the actual NSTimeInterval of these dates, then several created within a short period of time (for our example, within 5.5 seconds of each other) that might appear to be the same time:

Date 1:

July 18, 2012 4:31:39 PM PDT
364347099.942335
July 18, 2012 6:00:00 PM PDT
364352400.942335

Date 2:

July 18, 2012 4:31:45 PM PDT
364347105.456080
July 18, 2012 6:00:00 PM PDT
364352400.456080

Each date above is shown with four values: the raw date, the raw date in seconds*, the normalized date, and the normalized date in seconds*. (* number of seconds since January 1st, 2001 GMT). What you notice is that the seconds display have a fractional part that doesn’t get reflected by the user readable display.

This becomes problematic in applications that want to sort records by dates as the primary sort key, and another criteria for a secondary sort key. Particularly so when the coarseness of the dates is less than at the seconds level. For instance, if we sorted the above normalized dates, then record with Date 2 would appear before record with Date 1, even though to the user they would appear to be the same. In cases where the secondary sort criteria would have put a record with Date 1 before Date 2, this sorting would have failed to do so.

A Good Date…

There is no way to remove these fractional seconds by using -dateByAddingComponents:toDate:options: because -setMinute:, as noted above, accepts a NSInteger and not a floating point value type. We can easily modify our code above to handle that using our handy-dandy function, trunc():

NSDate *date = [NSDate date];
NSTimeInterval seconds = trunc([date timeIntervalSinceReferenceDate]);
date = [NSDate dateWithTimeIntervalSinceReferenceDate:seconds];
NSUInteger unitFlags = NSMinuteCalendarUnit+NSSecondCalenderUnit;
NSDateComponents *components = [[NSCalendar currentCalender] components:unitFlags fromDate:date];
NSInteger hour = ([components minute]<30) ? 1 : 2;
[components setHour:hour];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
date = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:date options:0];

An ARC Transition Tale

Tuesday, July 10th, 2012

If you are not a software developer, this post will probably of less interest to you. Otherwise, please read on…

Recently, I updated Wooly Tasks to use Automated Reference Counting (ARC). The transition from a non-ARC project to an ARC project is fairly straight forward, and mostly automated by Xcode 4. ARC allows the developer to worry less about Cocoa memory management rules, and instead focus on their object graphs and creating great software. ARC is a handy tool, but you now a have some new rules to learn (although, in truth they are probably less confusing as a whole than the old rules). One that is important is object lifespans. Essentially, this means the LLVM compiler can decide to release an object that it deems unused at any moment within the body of the code. We’ll see what kind of ramifications that presents soon.

It’s all fun and games until you shoot your eye out…

Once I had successfully transition (i.e. it compiled and linked), I tested Wooly Tasks using the simulator. To my delight, everything still worked as expected. OK, time to build and install on a device (in this case my iPhone 4S running iOS 5.1).

Launch.

Boom!

Crash. What the #^$#!!

Check with the Simulator again. Fine.

Back to the iPhone. Crash. Gah.

Unfortunately, it crashes due to an autorelease object, so there is no stack dump to figure out what the object is that is causing the issue (this is not entirely true, you can look at the crash log in Xcode, and this would give you plenty of clues…I didn’t think to look there first, unfortunately — lesson learned). All I get is a cryptic console log exception message stating:

“[Not A Type isEqual:] sent to deallocated object 0x….”

Well, huh.

Debugger spelunking time.

So, I started running it in the debugger and setting breakpoints starting from the first UIApplication delegate call and on down the line until I narrow it down to the setting up of the first view controller and displaying its view. Eventually I found the offending code (for brevity, I have left out the values, they were irrelevant to the issue, as is the particular color creation method):

CGColorRef color = [[UIColor colorWithHue:saturation:brightness:alpha:] CGColor];
self.layer.shadowColor = color;

Do you see the problem? Clang’s document on Automatic Reference Counting and lifetime semantics states:

By default, local variables of automatic storage duration do not have precise lifetime semantics. Such objects are simply strong references which hold values of retainable object pointer type, and these values are still fully subject to the optimizations on values under local control.

We have done the equivalent of:

UIColor *uiColor = [UIColor colorWithHue:saturation:brightness:alpha:];
CGColorRef cgColor = [uiColor CGColor];
self.layer.shadowColor = cgColor;

LLVM has every right to deallocate the UIColor object immediately after obtaining the CGColorRef from it, since by all accounts, it is no longer be used. This would then invalidate the data referenced by the cgColor local variable. When we set the shadowColor for the layer, we are potentially (in this case, we definitely were) stuffing an invalidated (but not nil’d) object value into it.

The Fix

The simplest solution was to rewrite the code like:

UIColor *color = [UIColor colorWithHue:saturation:brightness:alpha:];
self.layer.shadowColor = [color CGColor];
LLVM compiler will not be able to insert a release between accessing the CGColor and assigning it to the shadowColor.
And here you thought you could forget all those pesky memory rules.

iOS: UIMenuController

Tuesday, June 12th, 2012

File this one under: For my own future reference.

UIMenuController was introduced as public API in iOS 3.0 (along with UIPasteboard and Copy and Paste functionality). This is now a technology we take for granted in the iOS world, but it really wasn’t that long ago that Apple hadn’t provided this seemingly basic service.

Recently I was using this API in DeepDish GigBook to update some fairly crufty workflow. I am not going to explain how to use UIMenuController, as the documentation is fairly straight forward. What I will mention is a few “gotchas”.

1. There is no target for an UIMenuItem.

It is whomever is the First Responder. In other words, your controller class probably (in most cases) is what will handle the actions, so you want to make sure it can become the first responder. Add the following to your controller implementation:

- (BOOL)canBecomeFirstReponder
{
    return YES;
}

IF you don’t do this, then passing the message -setMenuVisible:animated: to the UIMenuController will have no effect.

2. Implement - (BOOL)canPerformAction:(SEL) sender:(id) in your controller:

There is little reason not to, and many reason why you should. This will be implemented in the same controller that also implemented -canBecomeFirstResponder. This will allow you to enable or disable (effectively hiding) any menu options you may or may not want at any particular time. It avoids having to recreate the menu items list every time the user invokes your menu through whatever gesture.

- (BOOL)canPerformAction:(SEL)action sender:(id)sender
{
    if ( action == @selector(myMenuItemMethod:) ) {
        return YES;
    }
    else if (...) {
        return ...;
    }
    else {
        return [super canPerformAction:action sender:sender];
    }
}

Of course, there might be situations where the same view might support different menu configurations depending on some context, in which case you probably do need to rebuild the menu list each time. But in case where you don’t…

3. Remember that your menu items are persistent:

Whatever menu items you add will remain there until they are set again. If you think about it, this makes sense since UIMenuController is a singleton.

This one bit me due to two reasons. 1) I didn’t have -canPerformAction:sender: implemented, 2) I assumed that menu items list must be rebuilt prior to each invocation of the menu. What happened was a different subview was presented within the main view of the controller, and the subview contained an UITextView. When the user double tapped the text view, and had some text on their pasteboard, the Paste menu option was displayed, along with the other menu options already installed by an earlier menu displayed in the same view. Oops.

The first solution was to nil out the menuItems when the menu was dismissed. This seemed a bit of hack. The better solution was to follow item 2 above since my menu items never changed as long as I was in this particular view. In this case, since a subview only required the basic Copy, Cut, Paste, Select, Select All, etc. the controller would return NO when the controller itself was no longer the first responder. Revisiting our implementation of -canPerformAction:sender: from above:

- (BOOL)canPerformAction:(SEL)action sender:(id)sender
{
    if ( action == @selector(myMenuItemMethod:) ) {
        return [self isFirstResponder];
    }
    else if (...) {
        return ...;
    }
    else {
        return [super canPerformAction:action sender:sender];
    }
}

Cheers!

Static Libraries woes with categories and iOS

Friday, May 20th, 2011

Like most developers, I assume, I have a lot of utility functions and category methods that I rely on from project to project. Importing all those files into each project, while allows customization, does tend to be tedious, error-prone, and a bit amateurish (IMO). So, I created several stand-alone libraries for Core Foundation, Core Data, and UIKit “extensions”. Simple, no big deal. Just don’t forget to add -ObjC via the Other Linker Flags to your Build Settings of the project that is linking to the static libraries. (See Apple’s Technical Q&A QA1490).

Unfortunately, there exists an issue with the linker where this linker flag doesn’t work as advertised in certain environments (iOS and 64-bit Mac applications) when you have only categories defined, but no classes. When you build your application, you not get any errors about unrecognized categories (assuming you have defined and exposed them via #import header correctly).

When you merrily go about running the application and it tries accessing one of these categories defined in a static library, you get an exception thrown that reads a bit like:

2011-06-04 08:34:50.823 <App Name> [24476:207] +[<Class> <method>:]: unrecognized selector sent to class 0x68e00

In this case, I was calling a class method, but instance methods will be affected as well. Also, if it isn’t obvious, the class address will be different from session to session.

So, the solution for now is to replace -ObjC with either -all_load or -force_load.

Using -force_load requires a path to the archive (static library), whereas -all_load is a shot-gun approach for all archives linked into the project. Each forces the linker to pull in all object files from the archive(s). This is not perfect in that it would be nice to take advantage of the linkers ability to strip out unused symbols, among other potential issues (such as forcing loads of other symbols from other, possibly unknown, archives).

As far as I can tell, there is no harm in defining in Other Linker Flags both -ObjC and either -all_load or -force_load, nor do I see an advantage to it. -all_load or -force_load pretty much override -ObjC entirely.