Archive for the 'Cocoa' Category

A simple way to make your NSLogs and NSAsserts more informative

Wednesday, June 13th, 2007

OK, so I'm not totally radio-silent. I learned about this in a WWDC session, but since it's already public API in Tiger (actually, it's a GCC extension), I can talk about it.

It's a built-in macro called __PRETTY_FUNCTION__. This is a fully-qualified human-readable signature of the function you're in. The GCC docs don't mention this part, but it even works in Objective-C, in addition to C++ and plain C. Here's a test app, containing this code:

@implementation Blah(blah)
- (void)blah {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}
@end

And the output:

2007-06-14 07:50:37.733 printmethod[1800] -[Blah(blah) blah]

Notice that it includes the class name, category name (if any), and method selector.

Note that that's a C-string, not an NSCFString. Be sure to set up your format string accordingly.

What if we had a language keyword for ownership?

Tuesday, May 29th, 2007

For example:

retained NSImage *myImage;

mutablyCopied NSTextStorage *myTextStorage;
NSLayoutManager *layoutManager; //Owned by myTextStorage
NSTextContainer *textContainer; //Owned by layoutManager

Or instead of retained, we could have a keyword for non-ownership, with retain being the default for instance variables:

NSImage *myImage; //Retained by default

mutablyCopied NSTextStorage *myTextStorage;
borrowed NSLayoutManager *layoutManager; //Owned by myTextStorage
borrowed NSTextContainer *textContainer; //Owned by layoutManager

(Hopefully, this is already common, but using comments rather than language keywords.)

Perhaps these could be implemented using something like Python's decorator syntax:

#define retained @ownership(retain)
#define copied @ownership(copy)
#define mutablyCopied @ownership(mutableCopy)

//What the code looks like after macro-expansion, or without using macros at all:
@ownership(copy) NSString *myTitle;

Either way, the correct retain/copy/mutableCopy/release magic would happen automatically on any assignment. The only manual work still involved would be:

- (void)dealloc {
	[myTimer invalidate];
	myTimer = nil; //Implicitly releases myTimer

	myTextStorage = nil; //Implicitly releases myTS (and on death, it releases its LM, and that releases its TC)

	[super dealloc];
}

This would make it totally impossible to forget to retain something (forgetting to retain or copy things leads to double-releases or zombie objects), and make it much harder to retain when you should copy or vice-versa. It would also make your header much richer in documentation, and the more you can learn from the header, the easier reading the implementation will be.

Now that’s what I call a speed-up

Tuesday, May 29th, 2007

CPU Usage 0.4 creates and throws away an NSImage every time a view (one per processor) updates. This is incredibly wasteful: The application uses about 0.7% of a CPU on my system.

But I decided that I can make that faster. Profiling (thanks, Shark!) revealed that the hot spot was creating, measuring, drawing, and throwing away the attributed string that goes into the NSImage. That's not hard to optimize away: as the old doctor joke goes, “stop doing that”.

So I created a couple of branches, and went in two different directions:

  • Direction A uses NSLayoutManager—specifically, one NSTextStorage, one NSLayoutManager, and one NSTextContainer for every (CPU, percentage) pair. A view can display any of 101 percentages (0–100%), so on my four-core system, this branch creates 404 storages, 404 managers, and 404 containers.
  • Direction B uses an NSImage, just like 0.4 does, but keeps it around.

Both branches are written to create the (NSImage|NSTS, NSLM, and NSTC) lazily, but for testing, I added this code to force the creation of all the cached objects up front so that my time-trials would represent normal usage (that is, usage after all the objects have been created and cached):

- (void)drawRect:(NSRect)rect {
    //BEGIN TEMP
    if(!(percentageImages[0U])) {
        static BOOL isInDrawRect;
        if(!isInDrawRect) {
            isInDrawRect = YES;

            NSLog(@"Preloading percentage images for CPU %u", CPUNumber);
            NSRect tempImageBounds = [self bounds];
            NSImage *tempImage = [[NSImage alloc] initWithSize:tempImageBounds.size];
            [tempImage lockFocus];
            for(float usage = 0.0f, maxUsage = 1.01f; usage < maxUsage; usage += 0.01f) {
                [self setCPUUsage:usage];
                [self drawRect:tempImageBounds];
            }
            [tempImage unlockFocus];
            [tempImage release];
            NSLog(@"Done preloading percentage images for CPU %u", CPUNumber);

            isInDrawRect = NO;
        }
    }
    //END TEMP

    ⋮
    (The rest of -drawRect: is here)
}

That's from the NSImage branch, but the code in the NSLayoutManager branch is basically the same. (Note: tempImage is not the cached image; it's just a throwaway destination for the drawing done by -drawRect: in the inner call.)

Once I had finished this, and fully optimized both branches using Shark, the next step was to try them out and see how they fare.

I launched all three versions of CPU Usage, and then did the following:

# Watch the CPU usage of the CPU Usage processes for five minutes (300 seconds)
top -l 300 | fgrep 'CPU Usage' > top-CPU_Usage.txt      %/Volumes/RAM Disk(130)
___
# Fourth column (as determined by whitespace) is CPU usage
fgrep 221 < top-CPU_Usage.txt  | awk '{ print $4; }' > top-CPU_Usage-0.4.txt
___
fgrep 1121 < top-CPU_Usage.txt | awk '{ print $4; }' > top-CPU_Usage-NSLM.txt
___
fgrep 1118 < top-CPU_Usage.txt | awk '{ print $4; }' > top-CPU_Usage-NSImage.txt
___
# The results, in percent of one CPU
~/Python/avg.py < top-CPU_Usage-0.4.txt                   %/Volumes/RAM Disk(0)
0.671
___
~/Python/avg.py < top-CPU_Usage-NSLM.txt                  %/Volumes/RAM Disk(0)
0.667
___
~/Python/avg.py < top-CPU_Usage-NSImage.txt               %/Volumes/RAM Disk(0)
0.737

Note: For timing purposes (since top's CPU-usage display is in tenths of a second), I divided CPU Usage's sample interval by ten. Normally, it samples every 0.5 sec; the two prototypes above sample every 0.05 second (that is, ¹⁄₂₀ sec instead of ¹⁄₂ sec).

This means that to compare them to 0.4, you must divide the results by ten to adjust them back to the half-second interval that a release would have. Here are the *real* results:

0.40.671%
NSImage0.0737%
NSLayoutManager0.0667%

So CPU Usage 0.5, with the cached-NSLayoutManager behavior, will use ¹⁄₁₀ as much CPU as 0.4 does. And here's what that looks like:

All four processors show 0% usage.

Sweet!

Apple Bug Friday! 60

Friday, May 25th, 2007

This bug is NSImageInterlaced has no effect. It was filed on 2007-05-12 at 00:50 PDT.

(more...)

Apple Bug Friday! 59

Friday, May 25th, 2007

This bug is The NSImageInterlaced, it does nothing!. It was filed on 2007-05-12 at 00:29 PDT.

(more...)

Report-an-Apple-Bug Friday! 58

Saturday, May 12th, 2007

Slightly late because I had to devise a way to determine whether a GIF file is interlaced. (I settled on GifBuilder, in case you're curious.) This ties in with the next two bugs; I'll blog both at once next week.

This bug is NSImageInterlaced documented as working on half of known interlaceable types. It was filed on 2007-05-12 at 00:27 PDT.

(more...)

How do I swap thy bytes? Let me count the ways

Saturday, April 28th, 2007
  1. swab

    swab(3) is a function that copies some bytes from one location to another, swapping each pair of bytes during the copy. Handy for structures.

    It has a feature that isn't mentioned in the Darwin manpage for swab: If you pass a negative size, it does not swap. I have no idea why this magic behavior was added; if you want a swab that doesn't swap bytes, just use bcopy. I shake my head at this use of a magic argument.

  2. ntohs, htons, ntohl, htonl

    These four functions swap the bytes of a 16-bit (‘s’) or 32-bit (‘l’, in ignorance of LP64) integer and return the transformed value.

    They are mainly used in network-I/O contexts, as they transform between network byte order (big-endian) and host byte order (whatever you're running). But there's nothing stopping you from using them for any other 16-bit/32-bit integral byte-swapping.

  3. OSByteOrder (Darwin)

    The Darwin kernel provides a number of handy-dandy macros for byte-swapping:

    • OSSwap{Const}?Int{16,32,64}
    • OSSwap{Host,Big,Little}To{Host,Big,Little}{Const}?Int{16,32,64}

    The {Host,Big,Little}To{Host,Big,Little} functions swap conditionally; the others always swap.

    According to the Universal Binary Programming Guidelines, it is safe to use these in applications.

  4. Core Foundation

    CF's Byte-Order Utilities provide the same facilities as OSByteOrder, with a couple of twists:

    • The implementation uses assembly language when the environment is GCC on either PowerPC or x86. This is theoretically faster than OSByteOrder's pure-C implementation. (CF falls back on pure C in all other environments.)
    • CF adds support for byte-swapping 32-bit and 64-bit floating-point numbers.
  5. Foundation

    Foundation's byte-order functions bear all the same capabilities as the CF Byte-Order Utilities. In fact, they are implemented with them.

  6. NeXT byte-order utilities

    These utilities are equivalent to the Foundation functions, except that they are implemented using the OSByteOrder utilities. They are declared in <architecture/byte_order.h>.

  7. Core Endian

    Core Endian logo that I made up.

    I think that the “Core Endian” name itself is new in Panther. Three functions in the API have a “CoreEndian” prefix, and are marked as new in Panther, whereas the others have simply “Endian”, and are marked as having existed since 10.0. This suggests to me that the entire API was branded “Core Endian” in 10.3, with the older functions subsumed by it.

    The new functions have to do with “flipper” callbacks, which you can install so that things like Apple Event Manager can DTRT with your custom data types. The older functions are plain byte-swapping utilities, just like all the other APIs described here, and exist mainly for the benefit of QuickTime users (they exist on Windows, too, through QuickTime).

Report-an-Apple-Bug Friday! 57

Friday, April 27th, 2007

This bug is NSFrameRectWithWidth uses the current color, not the stroke color. It was filed on 2007-04-27 at 16:17 PDT.

(more...)

New service: Make Obj-C Accessors

Thursday, April 19th, 2007

A pair of services, actually: One to generate declarations, and the other to generate definitions. Simply select a run of instance variable declarations (ideally copied and pasted into the area where you want the methods to go), then invoke the service. Couldn't be simpler—at least until Obj-C 2.0 arrives.

Why Mac programmers should learn PostScript

Saturday, April 7th, 2007

I'll follow this up with a tutorial called “PostScript for Cocoa programmers”, but today brings my list of reasons why you should care in the first place.

(more...)

Mac app checklist

Sunday, March 18th, 2007

A list of things that you must do for every app. Most of these should be done up front; some can be put off a little bit; some can be put off a long time, but ought to be done at some point rather than put off into the indefinite future.

Also available in TaskPaper format, in case you're a TaskPaper user.

  1. Version control is mandatory. I recommend Subversion (which is free and open-source). The advantage of version-control is time travel: You can easily go back to any previous version of your code, or even certain files. This lets you delete with confidence. Be sure to follow the trunk+branches+tags layout for your repository; you can create branches for experimentation and tags for released versions. Both these operations are cheap in Subversion. The easy way to install Subversion is to use Martin Ott's Subversion Installer package.

  2. Resolution independence. In particular, shun raster images (except for your Finder icons). Use vector graphics, either created in an app like Lineform or written in PostScript or Cocoa.

  3. Contextual menus. Much like AS support (listed below), effective contextual-menu support is a great way to make your app more efficient for its power users. In IB, create a top-level menu by dragging it from the palette into your nib, then ctrl-dragging from the view to the top-level menu and connecting to the menu outlet. If you create your views programmatically, you'll need to override -menu (either returning a programmatically-created menu, or adding support for a -setMenu: method that you provide).

  4. Localize everything. Localize every string you show to the user, even if it's only in English for now. If your app achieves any popularity (and you should hope it does), people will volunteer to localize it into their preferred language. If you use NSLocalizedString on every hard-coded string as you write it, your localizers will be able to localize your entire app, without having to ask you for app-side support because it will already be there.

  5. Maintain a bug- and feature-tracking system. Ideally, something like a locally-hosted Trac (don't open it to the users, or you will be fighting lots of duplicates), but even a simple OmniOutliner or Opal document will save you having to remember all those things you need to fix/add.

    Its benefits aren't as immediate as version-control: Version-control pays off the moment you want to bring back something you deleted, whereas a bug-tracker pays off months down the road, when you're trying to remember what that last feature you wanted for 1.0 was, or what that one guy reported with the bug that only happened when he clicked on one of the windows with his left hand. But it's the same kind of time-saver.

  6. Sell your shares in __MyCompanyName__. Especially important for open-source projects, since other people will be reading your code. Do this now, so you don't need to do it after you create the project.

  7. Burninate NewApplication. The template nibs (including those in a new project) use “NewApplication” throughout the menus, where your application name is supposed to go. Make sure you replace “NewApplication” with your actual application name. (Even better, use Daniel Jalkut's app template instead of Apple's.) Take it from me, it's embarrassing to ship an app whose menus still use the name “NewApplication”. ☺

  8. Thoroughly test everything in the stock menus. Especially the application menu (edit: used to say ; thanks Jesper) and Help menu. Services should work in any text view or text field, and ideally should work in any view that has data that your users may want to run a Service over. The Preferences menu item should be hooked up or deleted. Same for “Application Name Help”.

  9. Edit the credits. (No pun intended.) These are stored in the “Credits” file in Contents/Resources. The default Credits file is an RTF file; TN2179 says that the About panel looks for Credits.{html,rtf,rtfd}, in that order.

  10. An application icon. If you can't draw a good icon, hire someone to draw one for you. (If yours is an open-source project and it becomes popular, somebody may volunteer.)

  11. Document icons. (For doc-based apps only, obviously.) One document icon for each document type you can write out, with different classes of type (e.g. movie vs. audio vs. text) being depicted in significantly different ways (e.g. QuickTime Player's icons having sprocket-holes for video files vs. a waveform for audio files).

  12. Pick a creator code and register it. This still matters, especially for associating a specific document file with your app. (Say you're writing a multi-track video editing app—please—and you want to have movie files that you save be owned by your app rather than QTP. That's what a creator code is for.) Your creator code goes in the Properties tab of Target Info in Xcode. Keep in mind that creator codes are limited to MacRoman characters.

  13. Make sure your bundle ID, version, short version, and Get Info string are correct.

    • Your bundle ID should be something unique to you, and should use your domain—not Apple's, not anyone else's, yours. If you don't have one, make one up. Be sure to use the domain that you'll eventually actually own; when you finally register that domain (even if you don't do it for years, but you should do it as soon as you can, IMO), you won't have to change all your bundle IDs.
    • Dave Dribin discusses what to put in CFBundleVersion.
    • The short version string is the version that appears in Finder's List View; it should be a simple major.minor.maint version-number string (e.g., 1.0), and nothing else.
    • For Get Info strings, include a copyright statement and your version—for awhile, Apple said to leave out the version, but they've since reverted that, as users (well, at least former Mac OS users like me) expect to see a version number there. The version number you have here should be the same one you have in the short version string.
  14. Run it with no prefs. The goal here is to see how your app looks and works the very first time it is run. In particular, anything fed by NSUserDefaultsController needs to handle nil values correctly if you didn't put some default values into it in main.

  15. Do at least one private beta or release-candidate. This is your final attempt to discover any bugs that you haven't found yourself. Maybe it's a byte-order bug, or something that only happens in certain network situations, or a crash that doesn't happen on your machine because xyz. Regardless, you'll find the bug only when somebody else runs your program, and hopefully you'll do that in a private beta or release-candidate phase rather than in your actual public release. (You're welcome to do a public beta as well, but don't stall—get to release quickly.)

  16. Include a license and a ReadMe. For open-source projects, picking a license is easy: Use new-BSD, MIT, LGPL, or GPL. I don't know what you would do for a closed-source project—I suggest consulting an intellectual-property lawyer. And definitely include a ReadMe. Your ReadMe should cover the following points:

    • What does the app do? (This is in case it was delivered on a CD or other compilation. In that case, they didn't read the description on your webpage.)
    • How would I use the app in its most basic operation?
    • Who owns this app? (Copyright statement.)
    • Where can I get a newer version of this app? (Link to your website for the product.)

    Also, if you present your ReadMe as an RTF file, make sure you choose “Prevent Editing” from TextEdit's Format menu, so that your ReadMe file is read-only. This prevents your users from accidentally deleting important swaths of the file.

  17. Maintain a detailed version history. Include the version number, release date, and list of changes. Version control will help you here: It's tedious, but not hard, to build a complete list of changes from an svn log. The version history should be on a page of your website.

  18. AppleScript support. This isn't something you need right away, but it's a definite plus, especially if you'll be selling your app. Daniel Jalkut has good info on adding AppleScript support. Even just being able to script the preferences is a good start.

  19. Check for leaks. The leaks(1) utility will scan your app's heap looking for memory that isn't pointed-to anymore. It's very easy to leak objects, especially when your autorelease-fu is not strong. Be sure to use MallocStackLogging, so you can see what allocated the leaked memory; this may give you a hint as to where the leak lies. You may also find heap(1) useful.

  20. Get a real website. No, your GeoCities website won't cut it. Expect lots of traffic, and be on a server that can handle it. No such server is free. (Also, don't host it yourself—most ISPs won't allow you to run a server on a consumer account, you don't have the bandwidth anyway, and professional hosting is cheaper than an ISP server-grade account.) I recommend TextDrive, though others[1] [2] [3] have their own recommendations.

Feel free to suggest more items in the comments.

UPDATE 2007-04-02 15:15 PDT: The old #5 was controversial, and I concede that it's too early to give that advice yet. I've replaced it with one that I posted in a comment. For posterity, here's the old #5:

Test on both architectures. If you can't do this, make your app Intel-only. Testers with ICBMs are easy to come by. Testers with PowerPC Macs aren't so much, and they get harder to find every day. So if you have only a PowerPC, you can recruit somebody with an ICBM to test your UB, but if you have only an ICBM, it's easiest to just make it Intel-only.

UPDATE 2007-04-02 20:17 PDT: Added a link to Martin Ott's Subversion Installer package.

UPDATE 2007-07-23 20:15 PDT: Added discussion of the short version string to point #13.

UPDATE 2007-07-23 20:27 PDT: Formatted point #13 and clarified its discussion of Get Info strings.

UPDATE 2007-08-06 04:30 PDT: Added links to OmniOutliner and Opal.

UPDATE 2007-08-08 17:15 PDT: Added TaskPaper version.

UPDATE 2007-08-08 21:51 PDT: Added suggestion to Prevent Editing on your ReadMe.rtf file.

It works for sets, too

Saturday, February 17th, 2007

You probably know that NSArray supports KVC as a sort of pseudo-HOM:

NSArray *words = …; NSArray *lengths = [words valueForKey:@"length"]; //Invokes -[NSString length] for every element, assuming that each element is a kind of NSString

What you may not know is that this works for NSSet as well. As -[NSArray valueForKey:] returns an NSArray, so does NSSet return an NSSet.

Apple Bug Friday! 47

Friday, February 16th, 2007

You thought it was dead! But no! Apple Bug Friday is back!

This bug is Drawing text changes the fill color. It was filed on 2006-08-18 at 20:35 PDT.

(more...)

What’s the resolution of your screen?

Sunday, February 4th, 2007

A few weeks ago, I installed Adobe Reader to view a particular PDF, and noticed something interesting in its Preferences:

Its Resolution setting is set by default to “System setting: 98 dpi”.

“Wow”, I thought, “I wonder how it knows that.” So I went looking through the Quartz Display Services documentation, and found it.

The function is CGDisplayScreenSize. It returns a struct CGSize containing the number of millimeters in each dimension of the physical size of the screen. Convert to inches and divide the number of pixels by it, and you've got DPI.

Not all displays support EDID (which is what the docs for CGDisplayScreenSize say it uses); if yours doesn't, CGDisplayScreenSize will return CGSizeZero. Watch for this; failure to account for this possibility will lead to division-by-zero errors.

Here's an app to demonstrate this technique:

ShowAllResolutions' main window: “Resolution from Quartz Display Services: 98.52×96.33 dpi. Resolution from NSScreen: 72 dpi.”

ShowAllResolutions will show one of these windows on each display on your computer, and it should update if your display configuration changes (e.g. you change resolution or plug/unplug a display). If CGDisplayScreenSize comes back with CGZeroSize, ShowAllResolutions will state its resolution as 0 dpi both ways.

The practical usage of this is for things like Adobe Reader and Preview (note: Preview doesn't do this), and their photographic equivalents. If you're writing an image editor of any kind, you should consider using the screen resolution to correct the magnification factor so that a 8.5×11″ image takes up exactly 8.5″ across (and 11″ down, if possible).

“Ah,”, you say, “but what about Resolution Independence?”.

The theory of Resolution Independence is that in some future version of Mac OS X (possibly Leopard), the OS will automatically set the UI scale factor so that the interface objects will be some fixed number of (meters|inches) in size, rather than some absolute number of pixels. So in my case, it would set the UI scale factor to roughly 98/72, or about 1+⅓.

This is a great idea, but it screws up the Adobe Reader theory of automatic magnification. With its setting that asks you what resolution your display is, it inherently assumes that your virtual display is 72 dpi—that is, that your UI is not scaled. Multiplying by 98/72 is not appropriate when the entire UI has already been multiplied by this same factor; you would essentially be doing the multiplication twice (the OS does it once, and then you do it again).

The solution to that is in the bottom half of that window. While I was working on ShowAllResolutions, I noticed that NSScreen also has a means to ascertain the screen's resolution: [[[myScreen deviceDescription] objectForKey:NSDeviceResolution] sizeValue]. It's not the same as the Quartz Display Services function, as you can see; it seemingly returns { 72, 72 } constantly.

Except it doesn't.

In fact, the size that it returns is premultiplied by the UI scale factor; if you set your scale factor to 2 in Quartz Debug and launch ShowAllResolutions, you'll see that NSScreen now returns { 144, 144 }.

The Resolution-Independent version of Mac OS X will probably use CGDisplayScreenSize to set the scale factor automatically, so that on that version of Mac OS X, NSScreen will probably return { 98.52, 98.52 }, { 96.33, 96.33 }, or { 98.52, 96.33 } for me. At that point, dividing the resolution you derived from CGDisplayScreenSize by the resolution you got from NSScreen will be a no-op, and the PDF view will not be doubly-magnified after all. It will be magnified by 133+⅓% by the UI scale factor, and then magnified again by 100% (CGDisplayScreenSize divided by NSDeviceResolution) by the app.

Obviously, that's assuming that the app actually uses NSScreen to get the virtual resolution, or corrects for HIGetScaleFactor() itself. Adobe Reader doesn't do that, unfortunately, so it suffers the double-multiplication problem.

So, the summary:

  • To scale your drawing so that its size matches up to real-world measurements, scale by NSDeviceResolution divided by { 72.0f, 72.0f }. For example, in my case, you would scale by { 98.52, 96.33 } / { 72.0, 72.0 } (that is, the x-axis by 98.52/72 and the y-axis by 96.33/72). The correct screen to ask for its resolution is generally [[self window] screen] (where self is a kind of NSView).
  • You do not need to worry about HIGetScaleFactor most of the time. It is only useful for things like -[NSStatusBar thickness], which return a number of pixels rather than points (which is inconvenient in, say, your status item's content view).

Two ways to strengthen your Cocoa-fu

Friday, February 2nd, 2007
  1. Chris Forsythe's Weekly Cocoa App Challenge, wherein you must clone an existing app with no source. “Week 3” is on now, since over two weeks ago. At the end of each logical week, Chris reveals the source, so you can check your answer (and his).

  2. Colin Barrett's Koan. The Koan that Colin poses is a verbal challenge, and is much harder than anything that Chris has put up so far. You basically need to already have strong Cocoa-fu in order to find the answer to this one within you.

Weekly Cocoa app challenge #3 solution

Thursday, January 18th, 2007

HERE THERE BE SPOILERS. If you intend to participate in the challenge, read no further because my solution follows.

DudeMenu-PRH-1.0-source.tbz

Source and executable are both included. It took me about an hour and 19 minutes to complete. Source-control-wise, this is revision 7 of its SVN repository.

Things used therein:

  • Acquisition of and intake from a plist
  • Definition of custom symbols for the auto-documentation of dictionary keys
  • Dynamically building a menu
  • Validation of input, both with alerts and assertions
  • Many, many comments
  • String localization
  • String localization using a custom table
  • Primitive token-replacement (primitive because there is no provision for escaping)
  • Inspection of the current locale
  • A status item
  • Menu items representing other objects
  • An app without a main menu (as in NSApp's mainMenu)
  • LSUIElement
  • A menu item that messages NSApp directly
  • Retrieval of images from the search path by name
  • Walking arrays
  • Auto-incrementation

A Core-Image-less Image Unit

Wednesday, January 17th, 2007

Can you imagine an Image Unit that didn't actually use Core Image?

I just wrote one.

Well, OK, so I did use CIFilter and CIImage — you can't get away without those. But I did not use a CIKernel. That's right: This simple filter does its work without a kernel.

For the uninitiated, a kernel is what QuartzCore compiles to either a pixel shader or a series of vector (AltiVec or SSE) instructions. All Image Units (as far as I know) use one — not only because it's faster than any other way, but because that's all you see in the documentation.

But I was curious. Could an Image Unit be written that didn't use a kernel? I saw nothing to prevent it, and indeed, it does work just fine.

The image unit that I wrote simply scales the image by a multiplier, using AppKit. I call it the AppKit-scaling Image Unit. Feel free to try it out or peek at the source code; my usual BSD license applies.

Obviously, this Image Unit shouldn't require a Core Image-capable GPU.

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!