Corbin Dunn and Louise Lovelle
Treehouse
Woodworking
About
Search

Archive for the 'Cocoa' Category

Cocoa programmers: avoid writing to the user defaults when you don’t need to

Tuesday, August 26th, 2008

[Edit: ecto ate this post, so I’m typing it in again!]

I discovered that a lot of applications will unnecessarily write to NSUserDefaults. This causes your app to hit the disk when it shouldn’t, and is a slight performance penalty. AppKit is also susceptible to this problem; if you hit cmd-O to bring up the open panel in any application, you will see it writing things to the user defaults, when it probably doesn’t need to do so. I’m working on fixing that, and you should to!

So, how do you do it? It is easy — just add a breakpoint on -[NSUserDefaults(NSUserDefaults) setObject:forKey:]. You can do this with gdb:

b -[NSUserDefaults(NSUserDefaults) setObject:forKey:]

Or you can use the breakpoints window in Xcode (my preferred way):

Picture4 - breaking on user defaults.png

Then, reproduce whatever action might cause it to happen (ie: starting your application, or in my test case, cmd-o to bring up the open panel). Look at the backtrace in Xcode and figure out why you are doing too much work:

Picture5 - the callstack for a user default.png

Getting rid of the undo warning in Xcode after saving

Friday, August 22nd, 2008

One of the most annoying dialogs in Xcode is the undo warning dialog you get when attempting to undo after a save. I do this all the time, and I hate the warning. Luckily, there is a user default to turn it off:

defaults write com.apple.Xcode XCShowUndoPastSaveWarning NO

Wake up and smell the cocoa

Thursday, August 21st, 2008

Your most important breakpoint in Cocoa

Wednesday, August 20th, 2008

…..drumroll please…and it is…is: objc_exception_throw. You should always have this breakpoint setup in any Cocoa or Cocoa Touch app that you are building.

How do you do it? In Xcode, Run -> Show -> Breakpoints and double click on a new breakpoint. Type it in, ie:

Picture 3 objc exception throw.png

Exceptions in cocoa are, well, exceptional. If your app is throwing them, then you should fix them.

Xcode code completion and your code

Thursday, August 14th, 2008

How can you become a faster Cocoa programmer? One way is to adequately name your variables, enums and classes.

Let’s start with enums and take an example from something new to NSTableView in Leopard. This is copied from NSTableView.h with the comments stripped out for clarity.

enum {

NSTableViewSelectionHighlightStyleRegular = 0,

NSTableViewSelectionHighlightStyleSourceList = 1,

};

typedef NSInteger NSTableViewSelectionHighlightStyle;

- (NSTableViewSelectionHighlightStyle)selectionHighlightStyle;

- (void)setSelectionHighlightStyle:(NSTableViewSelectionHighlightStyle)selectionHighlightStyle;

There are several things to notice here, some of which are important to you. The most important thing (in my opinion) is the common prefix. Notice that the enum values fully contain the enum type name. Why? The answer is code completion, which you should be using. It is much easier to remember one key portion of the name than to remember all values. In this case, the key thing to remember is “selection”.
As a programmer working with NSTableView you know you want to change the selection highlight style, but you don’t remember the option for the specific style you want. You know the Cocoa convention is setFoo, so you type:

[tableView set

And hit escape (or whatever key combo invokes code completion for you. For me, I remapped the key to ctrl-space, since I was used to Delphi and Visual Studio. But, I also use escape).
You see this result:

TableViewSet_CodeComplete.png

and start typing “sel” to see the result you want:

TableViewSetSelResult.png
Which inserts this template:

TableViewSetTemplate.png
Now, I’m surprised, but most people don’t realize that they can type ctrl-/ (or maybe alt-/ depending on your key bindings) to select the placeholder and type over it. Memorize that keystroke, and use it.
Now, the common prefix name comes in really handy with code completion — just start typing in the type that the placeholder tells you and you’ll see what options you have:

TableViewSetOptions.png
In essence, you only have to remember “sel”, and from there you can derive exactly what option you want using code completion. Less memorization, and faster programming.
Unfortunately, a lot of Cocoa came along before code completion, and doesn’t follow this convention. But if you look at a new UI framework (ala: UIKit for the iPhone), you’ll find this pattern throughout it. It makes programming very fast with fewer trips to the header to find out what you need.

The bottom line: use a common prefix, wherever you have a list of options. Also note that the NSTableViewSelectionHighlightStyle has the prefix NSTableView, since it only applies to NSTableView. But, the property name is “selectionHighlightStyle”, since it doesn’t make sense to replicate the type name there.

WWDC 2008

Monday, June 9th, 2008

WWDC 2008! Howdy to my fellow Cocoa Developers. Take a look at the conference schedule: http://developer.apple.com/wwdc/schedules/ and be sure to come to my talk! Tuesday at 10:30 AM, iPhone for Mac Developers.

If you have *any* Cocoa questions, come to the Cocoa labs! I’ll be working the labs this week, so find me (or any of the other great apple engineers) and ask questions!

Edit: if you go to: http://developer.apple.com/wwdc/students/ you can see a picture of me from last year — I have the Leopard print hair:

Picture 1.png

Cocoa: willDisplayCell delegate method of NSTableView, [NSCell setTextColor], and “source lists”

Wednesday, January 9th, 2008

Mac OS 10.5 added a “source list” highlighting style to NSTableView, with the API below for your reference:

enum {

NSTableViewSelectionHighlightStyleRegular = 0,

NSTableViewSelectionHighlightStyleSourceList = 1,

};

typedef NSInteger NSTableViewSelectionHighlightStyle;

- (NSTableViewSelectionHighlightStyle)selectionHighlightStyle;

- (void)setSelectionHighlightStyle:(NSTableViewSelectionHighlightStyle)selectionHighlightStyle;


Source lists should have bold text when the item is selected, and NSTableView attempts to auto-format the cell’s contents to automatically do this for you, as seen in this screen shot for the selected item in the open panel source list:

However, the code that does this formatting does so by converting the ’stringValue’ of the cell to an attributedStringValue that has the bold text. This is done *before* calling the delegate with “willDisplayCell”. The delegate gets the final say of how it looks in “willDisplayCell”, but this can cause unexpected results if you want to do something like this:

- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {

if(row == 0) {

[cell setTextColor: [NSColor redColor]];

} else {

[cell setTextColor: [NSColor blackColor]];

}

}

The problem with the above code is that it is *too late* for the coloring to be correctly applied to the cell if it is selected. The work around is easy; you need to color the cell text earlier, and a perfect place to do that is in the new 10.5 delegate method “dataCellForTableColumn:”

- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {

NSTextFieldCell *cell = [tableColumn dataCell];

if(row == 0) {

[cell setTextColor: [NSColor redColor]];

} else {

[cell setTextColor: [NSColor blackColor]];

}

return cell;

}

Cool. Have fun.

Overriding shortcut keys in the NSOpenPanel or NSSavePanel

Tuesday, December 11th, 2007

It recently came up where someone needed to override some of the default shortcut keys in the NSOpenPanel or NSSavePanel. This is quite trivial to do. First off, the “easy way” would be to add a hidden NSPopUpButton in an accessory view that has the appropriate shortcuts. However, that won’t override the defaults (ie: cmd-r == reveal in finder, cmd-i == finder info window, cmd-a == select all, etc). To override these, you would subclass the appropriate panel:

@interface MyOpenPanel : NSOpenPanel

@end

@implementation MyOpenPanel

- (BOOL)performKeyEquivalent:(NSEvent *)theEvent {

NSLog(@”…test”); // an example of where you would do your key testing

return [super performKeyEquivalent:theEvent];

}

@end

You would then use it via something like:

[MyOpenPanel openPanel]


Easy to do…

Leopard: PhotoSearch demo app now live

Monday, November 5th, 2007

PhotoSearch, a demo app I wrote for WWDC a few years ago, is now live:

http://developer.apple.com/samplecode/PhotoSearch/

It demonstrates some cool custom cell stuff that is only available on Leopard.

Instruments on Leopard: How to debug those random crashes in your Cocoa app

Wednesday, October 31st, 2007

Mac OS 10.5 Leopard has a great new developer app called Instruments.

It can easily be used to debug those “random crashers” in your application caused by too many -release or -autorelease calls. Let’s see how.

If you want to follow along, download this project: CrashTestDummy.zip

Open it up in Xcode.

The nib has two outlets in it: a basic NSObject called testObject and button setup to invoke the following code:

- (IBAction)buttonCrashTestAction:(id)sender {

// We are going to autorelease the testObject too many times..

[testObject autorelease];

}

So, we will hit the button a few times and we should get a crash. Compile the app with the Debug target. Start Instruments, and select the Object Allocations template:



Hit the purple “i” next to the instruments to bring up the inspector for it, and be sure to check “Record reference counts”, as seen below:





Cool. Click “Launch Executable” and select “Choose Executable”, and find CrashTestDummy.app from the built directory.





While you have the Open Panel up, add the environment variable NSZombieEnabled and set it to YES (sort of an optional step):




Hit Open, if you haven’t already done so.


Hit “Record” in instruments, which will start the app, and click the “Crash Test” button a few times until the app crashes.


Alright! Fire up Console.app and look for a line like this:



The important part is the address, 0×150d40, in this case.


In Instruments, move the mouse to the right of “All Allocations” and a little triangle appears:




Click on it to see all the allocations.

In the lower right hand corner of instruments click on the magnifying glass in the NSSearchField and select “Address”:



Type in your address, and you should see all allocations at that address:



Now, why so many? Well, addresses are reused. Chances are, the last allocation is your object, and sure enough, it is, since we know we are over releasing an NSObject instance. Click on that item, and you’ll see the history for it. Scroll to the bottom to see just the history for that NSObject:



Now! You should be able to figure out which release probably shouldn’t be there. We know it is #19 from our demo, but sometimes it isn’t that easy to figure out. Select #19’s row and then click the little gray arrow to next to its address (or whatever yours is), and then click on this button at the bottom of instruments to show the stack trace / extended detail:



You can then see when that “bad release” happened. Click on the thumb below for a full size screen shot of it.




Ahh! Way cool. Of course, this was a simple and easy test case.


Happy debugging :)


corbin


(c) 2008 Corbin Dunn

Corbin’s Treehouse is powered by WordPress. Made on a Mac

Entries (RSS) and Comments (RSS).

26 queries. 0.533 seconds.

Bad Behavior has blocked 445 access attempts in the last 7 days.