Vincent Gable’s Blog

March 29, 2009

How To Write Cocoa Object Setters

There are several ways to write setters for Objective-C/Cocoa objects that work. But here are the practices I follow; to the best of my knowledge they produce the safest code.

Principle 0: Don’t Write a Setter

When possible, it’s best to write immutable objects. Generally they are safer, and easier to optimize, especially when it comes to concurrency.

By definition immutable objects have no setters, so always ask yourself if you really need a setter, before you write one, and whenever revisiting code.

I’ve removed many of my setters by making the thing they set an argument to the class’s -initWith: constructor. For example,

CustomWidget *widget = [[CustomWidget alloc] init];
[widget setController:self];

becomes,

CustomWidget *widget = [[CustomWidget alloc] initWithController:self];

This is less code, and now, widget is never in a partially-ready state with no controller.

It’s not always practical to do without setters. If an object looks like it needs a settable property, it probably does. But in my experience, questioning the assumption that a property needs to be changeable pays off consistently, if infrequently.

Principle 1: Use @synthesize

This should go without saying, but as long as I’m enumerating best-practices: if you are using Objective-C 2.0 (iPhone or Mac OS X 10.5 & up) you should use @synthesize-ed properties to implement your setters.

The obvious benefits are less code, and setters that are guaranteed to work by the compiler. A less obvious benefit is a clean, abstracted way to expose the state values of an object. Also, using properties can simplify you dealloc method.

But watch out for the a gotcha if you are using copy-assignment for an NSMutable object!

(Note: Some Cocoa programmers strongly dislike the dot-syntax that was introduced with properties and lets you say x.foo = 3; instead of [x setFoo:3];. But, you can use properties without using the dot-syntax. For the record, I think the dot syntax is an improvement. But don’t let a hatred of it it keep you from using properties.)

Principle 2: Prefer copy over retain

I covered this in detail here. In summary, use copy over retain whenever possible: copy is safer, and with most basic Foundation objects, copy is just as fast and efficient as retain.

The Preferred Pattern

When properties are unavailable, this is my “go-to” pattern:

- (void) setX:(TypeOfX*)newX;
{
  [memberVariableThatHoldsX autorelease];
  memberVariableThatHoldsX = [newX copy];
}

Sometimes I use use retain, or very rarely mutableCopy, instead of copy. But if autorelease won’t work, then I use a different pattern. I have a few reasons for writing setters this way.

Reason: Less Code

This pattern is only two lines of code, and has no conditionals. There is very little that can I can screw up when writing it. It always does the same thing, which simplifies debugging.

Reason: autorelease Defers Destruction

Using autorelease instead of release is just a little bit safer, because it does not immediately destroy the old value.

If the old value is immediately released in the setter then this code will sometimes crash,

NSString* oldName = [x name];
[x setName:@"Alice"];
NSLog(@"%@ has changed their name to Alice", oldName);

If -setName: immediately releasees the value that -name returned, oldName will be invalid when it’s used in NSLog.

But if If -setName: autorelease-ed the old value instead, this wouldn’t be a problem; oldName would still be valid until the current autorelease pool was drained.

Reason: Precedent

This is the pattern that google recommends.

When assigning a new object to a variable, one must first release the old object to avoid a memory leak. There are several “correct” ways to handle this. We’ve chosen the “autorelease then retain” approach because it’s less prone to error. Be aware in tight loops it can fill up the autorelease pool, and may be slightly less efficient, but we feel the tradeoffs are acceptable.

- (void)setFoo:(GMFoo *)aFoo {
  [foo_ autorelease];  // Won't dealloc if |foo_| == |aFoo|
  foo_ = [aFoo retain]; 
}

Backup Pattern (No autorelease)

When autorelease won’t work, my Plan-B is:

- (void) setX:(TypeOfX*)newX;
{
  id old = memberVariableThatHoldsX;
  memberVariableThatHoldsX = [newX copy];
  [old release];
}

Reason: Simple

Again, there are no conditionals in this pattern. There’s no if(oldX != newX) test for me to screw up. (Yes, I have done this. It wasn’t a hard bug to discover and fix, but it was a bug nonetheless.) When I’m debugging a problem, I know exactly what setX: did to it’s inputs, without having to know what they are.

On id old

I like naming my temporary old-value id old, because that name & type always works, and it’s short. It’s less to type, and less to think about than TypeOfX* oldX.

But I don’t think it’s necessarily the best choice for doing more to old than sending it release.

To be honest I’m still evaluating that naming practice. But so far I’ve been happy with it.

Principle 3: Only Optimize After You Measure

This is an old maxim of Computer Science, but it bears repeating.

The most common pattern for a setter feels like premature optimization:

- (void) setX:(TypeOfX*)newX;
{
  if(newX != memberVariableThatHoldsX){
    [memberVariableThatHoldsX release];
    memberVariableThatHoldsX = [newX copy];
  }
}

Testing if(newX != memberVariableThatHoldsX) can avoid an expensive copy.

But it also slows every call to setX:. if statements are more code, that takes time to execute. When the processor guesses wrong while loading instructions after the branch, if‘s become quite expensive.

To know what way is faster, you have to measure real-world conditions. Even if a copy is very slow, the conditional approach isn’t necessarily faster, unless there is code that sets a property to it’s current value. Which is kind of silly really. How often do you write code like,

[a setX:x1];
[a setX:x1]; //just to be sure!

or

[a setX:[a x]];

Does that look like code you want to optimize? (Trick question! You don’t know until you test.)

Hypocrisy!

I constantly break Principle 3 by declaring properties in iPhone code as nonatomic, since it’s the pattern Apple uses in their libraries. I assume Apple has good reason for it; and since I will need to write synchronization-code to safely use their libraries, I figure it’s not much more work to reuse the same code to protect access to my objects.

I can’t shake the feeling I’m wrong to do this. But it seems more wrong to not follow Apple’s example; they wrote the iPhone OS in the first place.

If you know a better best practice, say so!

There isn’t a way to write a setter that works optimally all the time, but there is a setter-pattern that works optimally more often then other patterns. With your help I can find it.

UPDATE 03-30-2009:

Wil Shiply disagrees. Essentially his argument is that setters are called a lot, so if they aren’t aggressive about freeing memory, you can have thousands of dead objects rotting in an autorelease pool. Plus, setters often do things like registering with the undo manager, and that’s expensive, so it’s a good idea to have conditional code that only does that when necessary.

My rebuttal is that you should optimize big programs by draining autorelease pools early anyway; and that mitigates the dead-object problem.

With complex setters I can see why it makes sense to check if you need to do something before doing it. I still prefer safer, unconditional, code as a simple first implementation. That’s why it’s my go-to pattern. But if most setters you write end up being more complex, it might be the wrong pattern.

Really you should read what Wil says, and decide for yourself. He’s got much more experience with Objective-C development then I do.

March 13, 2009

(Always) Showing a Window on Application Switch

Filed under: Cocoa,MacOSX,Objective-C,Programming | , ,
― Vincent Gable on March 13, 2009

Keith Lang said all Mac applications should show a window when you switch to them via command-tab or gestures. I agree. I do think it is best to show nothing when the last window is closed; otherwise closing a window might mean more stuff in your face, not less. But when context-switching back into an application I can’t think of a good reason why applications shouldn’t have something up that you can interact with right now.1

This is the behavior you get when switching between applications by clicking their icons in the dock. It’s unfortunate that you have to do more work for your application to behave consistently no matter how it’s switched-to. Here’s how I’ve done this in IMLocation,

In my NSApplication delegate I implemented - (void)applicationDidBecomeActive:(NSNotification *)aNotification to check if no windows were visible, and if so, put something up. Here’s the exact code I use, replace editCurrentLocation: with what’s right for you:

- (void)applicationDidBecomeActive:(NSNotification *)aNotification
{
	[self displayLocationWindowIfNoWindowIsOpen];
}
- (void) displayLocationWindowIfNoWindowIsOpen
{
	NSArray *visibleWindows = [[NSApp windows] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"isVisible == YES"]];
	if(IsEmpty(visibleWindows))
		[self editCurrentLocation:nil];
}


1Xcode, doesn’t put up anything when you click on it’s icon and you have turned off the “Getting Started” splash screen. This makes a bit of sense, because Xcode is a project editor, not a document editor. Projects are collections of many files, and having an “untitled” default one does not make sense. But by default Xcode shows a window, the splash screen. And there is an argument that it should show an empty file, like an Emacs “scratch buffer”.

February 11, 2009

Black on White, White on Black

Filed under: Announcement,MacOSX,Sample Code,Tips,Usability | , , , ,
― Vincent Gable on February 11, 2009

Command-Option-Control-8 will invert your screen. It’s a cool looking effect (and quite a prank if you do it to someone else’s machine), but most importantly it makes tiny-white-text-on-black webpages easier to read. Command Plus/Minus makes text larger/smaller, which helps too.

I’ve known for some time that dark text on a white background is most readable. But it until recently it was just “book learnin”. I’m young, my eyes are healthy, and I can read both color schemes just fine. I didn’t have proof I could see.

But I have trouble sleeping sometimes. A few days ago I had an “accident” with a 2L bottle of Mountain Dew and a late-night dinner of salty pizza. Look, the details of blame aren’t important here, the point is I didn’t get to sleep that night. Now, when you are very tired, it’s harder to focus your eyes — and having to focus them on a computer screen doesn’t help. About 3 in the afternoon it got downright painful to read trendy looking webpages with midnight backgrounds and petite white text. Remembering the color theory behind contrast, I gave Command-Option-Control-8 a shot, and holy shit, it worked! My screen looked like an adventure in black-lighting gone horribly wrong. But I could focus on those webpage’s text more clearly. Degraded vision from eye-fatigue gave me proof that I could see.

Now please don’t take this as anything but a biased anecdote. Trust the science, not me! But it was a neat (and painful) experience. I can see why Command-Option-Control-8 is there now. Give it a try sometime, and see if it helps for you. The most you have to lose is impressing any shoulder surfers with your computer wizardry. (Honestly though Command-Plus — make text bigger — will probably do more to enhance readability.)

Just in case you want to inver the screen programatically, this Apple Script will do the job:
tell application "System Events" to tell application processes to key code 28 using {command down, option down, control down}

February 9, 2009

Resolution Independent Screenshots

Filed under: Announcement,MacOSX,Programming | , , , ,
― Vincent Gable on February 9, 2009

Leopard includes technology that generates (mostly) resolution independent screenshots. That means when you enlarge the pictures, they won’t get pixelated, and more importantly, they will stay sharp when printed.

I don’t know if you’ve ever seen a printout of text mixed with a screenshot of text, but it looks like ass. That’s because even a very cheap printer is much higher resolution then your screen. It prints text very sharply. But when it prints the screen shot, it reproduces the low resolution display in high-fedelity — which actually makes it look worse. Plus, computers use tricks (eg sub pixel antialiasing) to make text look sharper on LCD screens — but those tricks can backfire on other media. A screenshot grabs exactly the pixels shown on the screen. And those pixels are optimized to be shown on a screen, not paper.

Example

Here’s an example screenshot (PDF). It looks like this:
Preview.png

If you open it, and zoom in, you will see that the text stays sharp, while some (but not all) of the interface gets pixelated.

PreviewBlownUp.png

How it Was Made

When Automator.app (click to open) saves a workflow, it puts a (mostly) resolution-independent screenshot of the workflow’s UI inside it. The screenshot is at SomeWorkflow.workflow/Contents/QuickLook/Preview.pdf. (In Finder, right-click a .workflow file, and choose “Show Package Contents” to look inside it).

If you print a workflow to a PDF file, it has the same limited resolution-independence. So I suspect Automator.app generates this PDF in much the same way files are printed. I have not investigated why the gray border is vectorized as well as the text. If anyone has an insight there, I’d love to hear it.

In the future, I expect text, and most UI elements, to be represented as vectors at every level of the OS. Screenshots will capture those vector-elements, as as they capture pixel-elements (pixels) today.

Color Blindness

Filed under: Accessibility,Design,MacOSX,Programming,Usability | , , , ,
― Vincent Gable on February 9, 2009

Roughly 10% of men are color blind to some degree. You need to be sure your interfaces are accessible to them. (Unless you are designing exclusively for women I suppose, since women are about 20x less likely to be color blind.)

Sim Daltonism is the best way to test an interface on Mac OS X I’ve seen.

Here is a web-based colorblindness simulator. Here is another. Personally I prefer a native program though. It’s faster and more versatile.

If you are curious, you can test yourself for colorblindness. I have no idea how accurate that test is, but since different displays and operating systems usually show colors differently I’d be a little skeptical.

ADDITION 2009-10-11: WeAreColorBlind.com is a website dedicated to design patterns for the colorblind.

February 5, 2009

Reverse Engineering Inter-Process Communication

Filed under: Interface Builder,MacOSX,Reverse Engineering | ,
― Vincent Gable on February 5, 2009

Matt Gallagher tells how he reverse engineered the link between Xcode and Interface Builder. Very interesting, I learned a lot. I’ve done essentially the same thing with iChat. (And in retrospect it might have been a bad idea, because it’s broken on Snow Leopard).

January 19, 2009

Setting iChat Status With Automator

Filed under: Announcement,MacOSX | , ,
― Vincent Gable on January 19, 2009

Set iChat Status is an Automator action that sets your status message, and availability in iChat. Amazingly, this action did not ship with Mac OS X.

Preview.png

I wrote it, because I wanted a user-friendly way for people to control iChat in IMLocation workflows.

I Need Your Help, Tiger

This action should run on Mac OS X 10.4. But since I don’t have a second computer running Tiger, I’m not sure. If someone would let me know if this works on Tiger I would really appreciate it! It should work just fine, but you know what they say about “should”…

Download.

January 18, 2009

iChat AppleScript / Apple Event Gotcha With “set status”

Filed under: Bug Bite,MacOSX,Programming | , ,
― Vincent Gable on January 18, 2009

If you run the AppleScript:


tell application "iChat"
   set status message to "testing"
   set status to away
end tell

You might expect to have the away message “testing”. But what you get is an empty away message. That’s because set status destroys your status message. It behaves as if you had selected “Away”, “Available”, etc. as your status from iChat’s status menu.

If you want to set a custom away message, call set status first, so it won’t over-write your message.


tell application "iChat"
   set status to away
   set status message to "testing"
end tell

Filled as radar://6505882, but Apple considers it expected behavior.

January 13, 2009

Automatically Closing NSFW Content (Beta)

Filed under: Announcement,MacOSX | , , ,
― Vincent Gable on January 13, 2009

I’ve finally implemented a neat feature for IMLocation that I’ve wanted to do for a while now, when you arrive at work, it can automagically close NSFW webpages. NSFW content is detected using the same technology behind Mac OS X’s Parental Controls.

I recommend downloading IMLocation to get the full effect, but you can also get just the automator action. Leopard is required, and it only works with Safari right now.

As you guessed, there is a trade-off between accidentally closing important webpages and letting questionable content slip through. I’m not yet sure what the best way to expose tweaking this tradeoff is. In the mean time I’ve chosen the more-confusing-but-powerful road, because I think it’s best to start with something that can do the job, and then refine and simplify it.

Firefox?

I want this to work with Firefox, but I am hamstrung by Firefox’s poor-to-nonexistant AppleScript support. (In the meantime you can try the worksafer Firefox plugin, but it’s not a true substitute).

Firefox is geared towards extension through plugins, while Safari has a less-rich plugin architecture, but good scripting support. Arguably, the Firefox way is better for a web-browser. I’ve seen some really cool Firefox plugins that extend the web-browsing experience in ways a script just can’t. Unfortunately, what I’m trying to do — have NSFW content automatically closed for you when you get to work — is the sort of thing a script does well, and a plugin does cumbersomely, if at all.

If anyone has some advice on how to hack around Firefox’s limitations please drop me a line. Right now my prognosis is “a lot of work for a clunky payoff”, so right now I’m focusing on more pressing concerns.

Running in the Background

Filed under: Bug Bite,MacOSX,Programming |
― Vincent Gable on January 13, 2009

If you want your bundled application to run in the background — not show up in the dock — here’s what you do. Add

<key>LSUIElement</key>
<string>1</string>

(exclusive) or

<key>LSBackgroundOnly</key>
<string>1</string>

To your info.plist file. Use only one or the other.

LSUIElement will let you put up windows and dialogs. They’ll appear in the context of whatever application is frontmost at the time. If your process might ever need to put up an alert, this is what you should use.

If you choose LSBackgroundOnly, then windows and dialogs won’t work. Note that if you are writing a daemon, there are frameworks that you can not use!

« Newer PostsOlder Posts »

Powered by WordPress