Archive for the 'Cocoa' Category

The Cocoa Memory Management Regular Expression

Thursday, January 11th, 2007

Any method whose selector matches this regular expression gives you a reference to its return value, which you must later release.

/^retain$|^(alloc|new)|[cC]opy/

As an added extra bonus, here's the CF equivalent.

/Create|Copy|Retain/

Carbon adds several exceptions, such as GetControlData (which returns a reference to CF things like the string value of a text field). Use Cocoa instead and your memory-management life will be simpler. ;)

How to detect a click

Sunday, January 7th, 2007

If you've ever written something that needs to simulate menu behavior, you've probably noticed that it would be nice to know, portably to future OS versions, how long you should wait before rejecting a mouse-down/mouse-up pair as not a click, as well as how far the user must move the mouse for it to not be a click.

The way to do this is:

EventTime clickTimeout; HISize clickMaxDistance; OSStatus err = HIMouseTrackingGetParameters(kMouseParamsSticky, &clickTimeout, &clickMaxDistance);

There is no Cocoa API for this.

On initializing static variables

Thursday, January 4th, 2007

Did you know that you don't have to initialize static variables? (If you've done any Cocoa programming, you know that statics are commonly treated as class ivars, most commonly to hold singleton instances.)

Quoth C99 (§6.7.8 paragraph 10):

… If an object that has static storage duration is not initialized explicitly, then:

  • if it has pointer type, it is initialized to a null pointer;
  • if it has arithmetic type, it is initialized to (positive or unsigned) zero;
  • if it is an aggregate [array or structure —PRH], every member is initialized (recursively) according to these rules;
  • if it is a union, the first named member is initialized (recursively) according to these rules.

And I've prepared a test app that shows that gcc 4.0.1 on OS X 10.4.8 does seem to comply with this handy part of the standard.

Happy not-initializing!

Compiling EPS files to PDF files using Xcode

Wednesday, December 6th, 2006

You may remember Daniel Jalkut's blog post about resolution independence, with its demonstration of the superiority in quality of vector graphics over raster graphics (using an EPS file that I created for him ☺).

The problem with using an EPS file, as I pointed out to him when I emailed him the file originally, is that EPS files are very slow on OS X. The current (Tiger) implementation of NSEPSImageRep uses Quartz's CGPSConverter to convert the EPS data to PDF data, then instantiates an NSPDFImageRep (kept in a private ivar) that it has do all the real work.

That PostScript-to-PDF conversion is the bottleneck. You don't want that multiple-second delay (yes, really) at runtime. Better to take it before runtime. At the time, I suggested that he use pstopdf to convert to a PDF file, then put the PDF file into his Xcode project to be copied into the app bundle.

But what if you decide later to change the PostScript file? You need to remember to run pstopdf again. EPS files are just PostScript source code, so why not treat them like any other source code file? Or, a better question: How?

The answer is quite simple: Add a custom rule to your target in Xcode that runs pstopdf over EPS files. Then, you add the EPS file — not the PDF file — to the project, and Xcode will exhibit normal build-only-when-necessary behavior on the EPS file and copy the generated PDF file for you. And of course, you can have as many EPS files as you want — they're source code files just like any others.

Screenshot of Xcode's Target Info window, showing the custom rule. The rule matches the glob pattern “*.eps” and runs the shell script “pstopdf ${INPUT_FILE_NAME} -o ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}.pdf”. It has one output file, which is “${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}.pdf”. In the background is an Xcode project window showing a couple of EPS files in the “Other Sources” group.

Wanted for abuse of operators

Saturday, December 2nd, 2006

From the stock app delegate in Apple's Core Data application template:

[managedObjectContext release], managedObjectContext = nil; [persistentStoreCoordinator release], persistentStoreCoordinator = nil; [managedObjectModel release], managedObjectModel = nil;

And in case you were wondering how Apple could possibly ship code that doesn't even compile in the app templates…

… that's legal code.

Yes, there is a comma operator, and that's what's at work there. There is a warning (-release returns void, whereas assignment of nil returns id, so the types are mismatched, which GCC doesn't like), but the code is legal.

Legal doesn't always mean correct, however. The comma operator is supposed to be used for things like this:

printf("%s\n", (-‍-argc, *++argv));

Though in modern usage it is out of favor, replaced on sight with less obfuscative code:

printf("%s\n", *++argv); -‍-argc;

printf("%s\n", argv[idx++]);

It is not for performing a void function call followed by an assignment. A void expression should never appear in a comma expression. The code would be greatly improved by replacing the comma with a semicolon, dividing each statement into the two statements that it should be.

Why you should use -availableData rather than -readDataToEndOfFile

Sunday, October 8th, 2006

(All of this only applies to regular files, not to “communications channels”, which I interpret to mean sockets and pipes. Also, this only matters on a 64-bit Mac; on a 32-bit system, you will run out of memory long before seeing any difference between these two methods.)

-[NSFileHandle availableData]:
“If the receiver is a file, returns the data obtained by reading the file from the file pointer to the end of the file.”
-[NSFileHandle readDataToEndOfFile]:
“The data available through the receiver up to UINT_MAX bytes (the maximum value for unsigned integers) …”

In other words, if there are more than UINT_MAX bytes in the file, -availableData will return all of them, whereas -readDataToEndOfFile will only return the first UINT_MAX of them.

ISO 8601 parser, version 0.4

Thursday, August 31st, 2006

I've released version 0.4 of my ISO 8601 parser and unparser.

Colin noticed that 0.3 didn't work so well for parsing the datestamp in the filename on an Adium log file (those datestamps being in ISO 8601 format). You see, Adium generates the filename with a '.' instead of a ':', since ':' is special on HFS/HFS+. 0.3 expects a ':' and only a ':'.

So I added new methods that take a time-separator character. Now you can pass '.' (or anything else, except NUL) when you need to. The old methods are still around, in case you don't. This is the case, for example, for datestamps inside a chatlog file, as opposed to in its filename.

Report-an-Apple-bug Friday! 46

Friday, August 18th, 2006

This bug is +graphicsContextWithBitmapImageRep: doesn't properly map bitmap format to Quartz. It was filed on 2006-08-17 at 19:07 PDT.


Summary:

+[NSGraphicsContext graphicsContextWithBitmapImageRep:] always returns a RGBA context, even when the image rep is ARGB.

Steps to Reproduce:

  1. Create an NSBitmapImageRep with an RGB color space and a bitmapFormat that contains the NSAlphaFirstBitmapFormat bit and not the NSFloatingPointSamplesBitmapFormat bit.
  2. Call +[NSGraphicsContext graphicsContextWithBitmapImageRep:] with the image rep created in step 1.
  3. Set the current graphics context to the graphics context created in step 2.
  4. Draw.

Expected Results:

WYD (draw) IWYG.

Actual Results:

The components in each pixel come out of the image rep permuted, since you expect ARGB from the image rep, but the graphics context put RGBA into it instead.

Regression:

None known.

Notes:

I did not test with the NSFloatingPointSamplesBitmapFormat bit; therefore, I do not know whether the bug exists with it or only without it.

Workaround:

Reinvent the +graphicsContextWithBitmapImageRep: wheel.


Technorati tags: ,

A vim quickie

Saturday, July 22nd, 2006

:g/^[-+].\+{\s*$/,/^}/fold ←Folds all Obj-C methods in the current file.

Technorati tags:

Image Shadow Adder

Tuesday, June 27th, 2006

My newest application, Image Shadow Adder, adds a shadow to an image. I plan on using this instead of Photoshop to restore shadows to my window screenshots (OS X's window screenshot command, ⇪⇧⌘4, does not preserve the shadow).

Also, the source code (BSD license) demonstrates how to use Bindings (used to insert the original and shadowed images into the document window's image views), use NSShadow, and capture an NSImage to an NSBitmapImageRep. Few comments in the code, I'm sorry to say, as this is just a quick-and-dirty application that I wrote when it dawned on me that I could use NSShadow when drawing an NSImage, rather than emulating my old Photoshop technique.

For reference, that technique was:

  1. Take one regular window screenshot, and one rectangular screenshot of the window with shadow on a white background.
  2. Drag and drop the window screenshot into the rectangular screenshot. Position so that the two window images line up.
  3. Knock out the black corners on the window screenshot. These are masked out by the alpha channel in the PNG file, but Photoshop sucks at alpha channels.
  4. Create a new layer. Fill with black.
  5. Load the window screenshot as a selection.
  6. Add a layer mask (reveal selection) to the black layer.
  7. Copy Merged.
  8. Add an alpha channel. Paste into it, and invert it. The area of the window should be white, with the shadow fading to black.
  9. Return to the Layers list and disable or delete the layer mask. Move the black layer down beneath the window-screenshot layer.
  10. Export using SuperPNG.

The only disadvantage to the new way is that AppKit's PNG-interlacing setting is broken. The generated PNG data is not interlaced. I'll have to use OptiPNG to interlace it.

Technorati tags: , .

Another quickie

Saturday, June 24th, 2006

If you use -[NSAttributedString drawAtPoint:], you may be surprised to find that it doesn't respect NSParagraphStyles in the receiver's attributes. The documentation hints at why:

The width (height for vertical layout) of the rendering area is unlimited, unlike drawInRect:, which uses a bounding rectangle. As a result, this method renders the text in a single line.

Well, it will actually respect any newlines when measuring and drawing the text. You won't just get a single line. What it really means is that you don't get paragraph styles applied.

Solution: Use -[NSAttributedString drawInRect:] instead.

I imagine that the same rule applies to -[NSString drawAtPoint:withAttributes:] and -[NSString drawInRect:withAttributes:] as well, since the former method has the same note in its documentation (and is probably implemented using -[NSAttributedString drawAtPoint:]).

Technorati tags: , , , .

Table view double-click actions

Monday, June 19th, 2006

It used to be that when you wanted to receive a double-click notification from an NSTableView, you had to do this in your -awakeFromNib:

[tableView setTarget:self]; [tableView setDoubleAction:@selector(doMagicThings:)];

Now, with the power of Bindings, you can do this in IB!

  1. Create an NSObjectController. Set its class name to the class of the desired receiver, and its “content” outlet (not binding) to the desired receiver.
  2. Bind the table view's doubleClickTarget binding to the object controller. Controller key: content. Leave the model key path empty.
  3. Put the selector name in the field at the bottom of the binding view.

And a cheesy way that doesn't involve a controller:

  1. Bind the table view's doubleClickTarget binding directly to the receiver. Model key path: self.
  2. Put the selector name in the field at the bottom of the binding view.

UPDATE 2008-11-29: No need to add a -self method to your class or NSObject, as NSObject already has one. I think that the reason I didn't see it before is because it's declared in the NSObject protocol, not the NSObject class.

NSMovieView vs QTMovieView

Friday, June 2nd, 2006

I mentioned in my previous post that you should use QTMovieView rather than NSMovieView because QTMovieView is so much more efficient.

There is, however, one reason to use NSMovieView, and it's the controller thumb:

A window with an NSMovieView, showing off its hourglass-shaped controller thumb. NSMovieView with QuickTime 7.0.2.

A MoviePlayer 2.5.1 window, showing off QuickTime 6.0.3's controller thumb. MoviePlayer 2.5.1 with QuickTime 6.0.3.

A window with a QTMovieView, showing off its plain round controller thumb. QTMovieView with QuickTime 7.0.2.

Does anyone know how I can get QTMovieView to use the same controller thumb that NSMovieView and QuickTime 6 use?

And no, the “Speed” field and progress bar are not part of the QTMovieView.

UPDATE 2006-06-10: wootest says that the above is the editing controller, so you get it by making the movie editable. I've tried it and it works. The code is:

[movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];

Thanks!

Technorati tags: .

Enhancing QuickTime performance

Friday, June 2nd, 2006

I use a 450 MHz G4 Cube, so I tend to look hard at minimizing any large drains on my CPU. Such drains include playback of large video files (such as the 540p version of MacBreak). So I've been experimenting on and off with faster ways of playing back QuickTime movies. Here are my findings.

First, do not use NSMovieView. In my app, the NSMovieView used 50% of my CPU at all times — even when the movie was paused. Bad, bad, bad. And the frame rate sucked, too. (UPDATE 23:50 PDT: It seems like NSMovieView simply draws the entire frame periodically and unconditionally. The CPU usage changed based on the size of the window.) When I replaced it with QTMovieView, the CPU usage went way down, and the frame rate became watchable.

Second, Apple has a document called Enhancing Movie Playback Performance. Read it, learn it, live it.

Some of its information bears expanding-upon, though:

  • These are basic QuickTime functions, not QTKit. Both NSMovie and QTMovie allow you to get the raw QuickTime Movie, which you pass to these functions.

  • Prerolling actually involves two steps. First, call PrePrerollMovie. (No, I am not making this up.) Then, call PrerollMovie.

  • Remember MoviePlayer? It had two checkboxes for enhancing speed:

    Screenshot of MoviePlayer's Info window for a MacBreak episode, showing the “video_main” track, Preload options.

    Preload is obvious; it corresponds to LoadMovieIntoRam. Cache hint is not so obvious; what it does is keep movie data in RAM after it has been played (for looping). This corresponds to the keepInRam option to LoadMovieIntoRam.

    If you use keepInRam, be sure to call LoadMovieIntoRam with the unkeepInRam and flushFromRam options when you're done with the movie, so that the memory used will be freed.

    Note that in MoviePlayer, you could only set preload options per-track (there was a “Movie” category, but you could not set preload options for it). So this actually corresponds to LoadTrackIntoRam. I find LoadMovieIntoRam more useful for my situation; obviously, you should use whichever one of LoadMovieIntoRam, LoadTrackIntoRam, or LoadMediaIntoRam is appropriate for yours.

  • Preloading all of a movie is very expensive, especially if the movie is large (MacBreak takes about 20 seconds to load — that's forever in user time). Use it only if you have to, and show a progress indicator if you do. If you can, preload sections at a time — I might, for example, load 30 seconds every 1 second starting from file open.

Third, I suggest making sure that your QTMovieView is set to preserve aspect ratio. I don't know whether aspect ratio distortion has a negative effect on QT performance — especially if the movie is being scaled anyway — but having this turned on can't hurt.

Technorati tags: , .

Beware the nib

Sunday, May 7th, 2006

Say you have an object, X, and a nib containing two other objects, Y and Z. X has two outlets, y and z, with typical accessors for each.

X, or another object on behalf of X, loads the nib with X as its owner. NSNib, naturally, sets the y and z properties of X to the two objects in the nib, using the accessors since they exist.

Now, when an object is instantiated, it has a retain count of 1; it is implicitly owned by the object that created it. If you release that object, the retain count goes down to 0 and the object dies.

Such is the case of the objects Y and Z when they are unarchived from the nib: After they come out of deep freeze, they have that starting retain count of 1. But then they are passed to the accessors of X, which, if written in the usual way, retain Y and Z. Now the retain counts are 2 each.

When X is finished with Y and Z, it releases them, of course (perhaps by calling its accessors with nil). But the implicit retains still exist. Therefore, so do Y and Z.

This isn't a nib bug, though; the bug is in X. Outlets aren't supposed to be retained. So, if you do implement accessors, they should be non-retaining. Or, if you can, omit the accessors; NSNib will do the Right Thing for you.

Example of the bug: NibLeak (source code with Xcode 2.2.1 project).

Technorati tags: .

Making vim grok Obj-C

Monday, April 24th, 2006

Like many, I use the popular open-source text editor vim. Earlier, I set about making it more usable through syntax coloring. This included tweaking its support for Objective-C.

First, mkdir ~/.vim, and cd ~/.vim. Now mkdir syntax. cp /usr/share/vim/vim62/filetype.vim . and cp /usr/share/vim/vim62/syntax/objc.vim syntax/. Then select and copy this patch:

Index: filetype.vim
===================================================================
--- filetype.vim 2005-03-21 02:12:47.000000000 -0800
+++ filetype.vim 2006-04-24 03:33:27.000000000 -0700
@@ -262,7 +262,7 @@
 
 " .h files can be C or C++, set c_syntax_for_h if you want C
 au BufNewFile,BufRead *.h
- \ if exists("c_syntax_for_h") | setf c | else | setf cpp | endif
+ \ if exists("c_syntax_for_h") | setf c | else | if exists("objc_syntax_for_h") | setf objc | else | setf cpp | endif | endif
 
 " TLH files are C++ headers generated by Visual C++'s #import from typelibs
 au BufNewFile,BufRead *.tlh   setf cpp
Index: syntax/objc.vim
===================================================================
--- syntax/objc.vim 2005-03-21 02:12:49.000000000 -0800
+++ syntax/objc.vim 2006-04-24 03:19:25.000000000 -0700
@@ -43,6 +43,8 @@
 syn match  objcDirective "@class\|@end\|@defs"
 syn match  objcDirective "@encode\|@protocol\|@selector"
 
+syn keyword     objcStorageClass   in out inout byref bycopy
+
 " Match the ObjC method types
 "
 " NOTE: here I match only the indicators, this looks
@@ -73,6 +75,7 @@
   HiLink objcFactMethod Function
   HiLink objcStatement Statement
   HiLink objcDirective Statement
+  HiLink objcStorageClass StorageClass
 
   delcommand HiLink
 endif

Now type pbpaste | patch -p0.

OK, now you have modified copies of the file type detector and the Obj-C syntax coloring. Now you need a .vimrc. If you already have one, add this to it; otherwise, create it with this:

let c_gnu=1
let c_no_bracket_error=1
let objc_syntax_for_h=1
syntax enable

This makes the following changes:

c_gnu
Enables detection of some GCC extensions to C.
c_no_bracket_error
Allows {} inside of []. This lets you do compound literals inside messages; for example: [self setFrame:(NSRect){ NSZeroPoint, (NSSize){ 128.0f, 128.0f } }];
objc_syntax_for_h
Tells vim (with my modifications, that you did above) that a .h file is an Obj-C header, not a C++ header.
syntax enable
Enables syntax-highlighting.

Technorati tags: , .

Report-an-Apple-bug Friday! 34

Friday, April 21st, 2006

This bug is Xcode: Cocoa Document-Based Application template uses old-style document methods. It was filed on 2006-04-21 at 21:19 PDT.


Summary:

NSDocument documentation recommends using -readFromData:ofType:error: and -dataOfType:error:, but the project template uses the older methods.

Steps to Reproduce:

  1. Create a new project.
  2. Use the Cocoa Document-Based Application template.

Expected Results:

MyDocument, from the new document, implements -readFromData:ofType:error: and -dataOfType:error:.

Actual Results:

MyDocument implements the deprecated methods -loadDataRepresentation:ofType: and -dataRepresentationOfType:.

Regression:

Prior to Mac OS X 10.4, this wasn't a problem, because -loadDataRepresentation:ofType: and -dataRepresentationOfType: were not deprecated.

Notes:

One could make the argument that the deprecated methods should still be implemented so that the new application is compatible with 10.3.x and earlier. But a new application is not likely to require such compatibility. Also, if this argument is indeed the motivation, then the project should target the 10.3 (or an earlier) SDK.


Technorati tags: , .

Report-an-Apple-bug Friday! 29

Friday, April 14th, 2006

This bug is NSWorkspace documentation contains two split sentences. It was filed on 2006-04-14 at 23:30 PDT.


Summary:

Documentation of several instance methods of NSWorkspace contains two sentences that have each been split in two, with the method signature between.

Steps to Reproduce:

  1. Go to the NSWorkspace documentation.
  2. Visit -activeApplication or -launchedApplications.

Expected Results:

Both sentences are not split.

Actual Results:

Both sentences are split.

Regression:

None.

Notes:

None.


Technorati tags: , .

Report-an-Apple-bug Friday! 28

Friday, April 14th, 2006

This bug is NSAnimation documented as inheriting from itself. It was filed on 2006-04-14 at 23:25 PDT.


Summary:

The infobox at the top of the NSAnimation reference gives a cyclic inheritance path.

Steps to Reproduce:

  1. Go to the NSAnimation reference.

Expected Results:

The infobox at the top says that NSAnimation inherits from NSObject.

Actual Results:

The infobox at the top says that NSAnimation inherits from NSAnimation inherits from NSObject.

Regression:

None known.

Notes:

As documented, NSAnimation inherits from both itself and NSObject. This is both cyclic inheritance and multiple inheritance, and both are illegal in Objective-C.

Similar to "NSViewAnimation documentation documented as inheriting from itself.".


Technorati tags: ,

Report-an-Apple-bug Friday! 27

Friday, April 14th, 2006

This bug is NSViewAnimation documented as inheriting from itself. It was filed on 2006-04-14 at 23:21 PDT.


Summary:

The infobox at the top of the NSViewAnimation reference gives a cyclic inheritance path.

Steps to Reproduce:

  1. Go to the NSViewAnimation reference.

Expected Results:

The infobox at the top says that NSViewAnimation inherits from NSAnimation inherits from NSObject.

Actual Results:

The infobox at the top says that NSViewAnimation inherits from NSViewAnimation inherits from NSAnimation.

Regression:

None known.

Notes:

As documented, NSViewAnimation inherits from both itself and NSAnimation. This is both cyclic inheritance and multiple inheritance, and both are illegal in Objective-C.

Also, NSAnimation is the last (highest) in the list, rather than NSObject, which means that NSAnimation is a root class.


Technorati tags: , .