(((1 << 3) & (~0xF | (3 >> 1) & (~0xFFED << 4))) | ~(7 + 0xDEADBEEF << 7) >> 5) >> 5 ^ (0xDE ^ (~0xF32 >> (5 | 74 >> 2)))

NSCopying and Mutability

Devin Lane | Cocoa Yups and Nopes | Friday, April 17th, 2009

In the Cocoa frameworks, it is common to find mutable and immutable versions of classes that store data, such as strings, dictionaries, and arrays. Most of these classes also implement the NSCopying informal protocol, and those with mutable varients implement the NSMutableCopying protocol. These protocols specify methods for obtaining immutable and mutable copies of an object. These copying methods should be implemented such that the copy can return an instance of a subclass, to allow for subclasses to copy their specific instance variables.

There are situations, however, that can result in the inability of a subclass to do so. Consider NSURLRequest:

@interface NSURLRequest : NSObject <NSCoding, NSCopying, NSMutableCopying>

An immutable class, but one that implements NSMutableCopying, it responds directly to the -mutableCopyWithZone:(NSZone *)zone method that returns a mutable url request, an instance of NSMutableURLRequest. While this isn’t a problem in itself, it becomes an issue when this method is not re-implemented in NSMutableURLRequest.

– Nope: Implement the NSMutableCopying protocol only in your immutable class.

The implementation of -mutableCopyWithZone:(NSZone *)zone in the immutable class must certainly hard-code the name of the mutable class — it cannot be aware of any external subclasses of the mutable class. This means that asking for a mutable copy of the immutable class creates an instance of the known mutable subclass. Consider the following example:

@interface MyMutableURLRequest : NSMutableURLRequest {} @end

MyMutableURLRequest *request = [MyMutableURLRequest requestWithURL:url];
request = [request mutableCopy];

In this case, the object returned from -mutableCopy will be an instance of NSMutableURLRequest instead of the expected MyMutableURLRequest.

– Nope: Hardcode class names in -copyWithZone: and -mutableCopyWithZone:

Hardcoding class names serves to block subclassers from subclassing the copy methods without dirty hacks.

+ Yup: [Re]Implement the NSMutableCopying protocol in your mutable class.

By doing this, the class name doesn’t have to be hard-coded (in the mutable class), and so a subclasser can simply call [super mutableCopy] and then copy their specific instance variables.

+ Yup: Use [self class] in -copyWithZone: and -mutableCopyWithZone:

By using [self class], an object of the subclass type will be allocated.

Readable If Statements

Devin Lane | Coding Yups and Nopes | Friday, April 17th, 2009

To kick of the Coding Yups and Nopes section, I’ll start with something simple. There are a set of things to do, and a set of things to avoid. For each thing, there are one or more reasons justifying its classification. These reasons are ordered by descending importance and increasing subjectivity.

– Nope:

if (condition) doWork();
else doOtherWork();
  • If you’re stepping through with a debugger (I used gdb), it is difficult to tell when stepping over the line if the statement is executed. To do so you must determine the value of condition, or determine if the function was called based on its side effects.
  • If you add an additional statement to the false case, it’s possible that braces may be forgotten, leading to incorrect operation.
  • Assuming the reason for putting both condition and statement on one line was to use less lines, the benefits of this are negated by the decreased readability of such a statement. By putting both condition and statement on one line, searching for a specific condition becomes more difficult.
  • Assuming other parts of your code are scoped and indented, this breaks that pattern.
if (condition) {
    doWork();
} else doOtherWork();
  • Similar to the above case. Basically this is inconsistent and has none of the advantages of either method.
if(condition){
    doWork();
}
  • This is a pure spacing issue. if is not a function, please do not call it as one. Also, that brace is squashed against that parentheses, give it some room! Skimping on spacing issues like this while writing makes it harder to read later.

+ Yup:

if (condition) {
    doWork();
} else {
    doOtherWork();
}
  • The perfect if-else statement. Well spaced, condition and statement on separate lines.
if (error) return;
if (error) break;
if (error) continue;
  • When used only for error/early exit conditions, these help to condense code so that the reader can quickly get to the meat of a function.
  • The statements and the condition are on one line because I do not consider the flow control statements return, break, and continue to contribute useful work, such that it should be scoped in braces and on its own line.

Window Resizing on Resolution Change

Devin Lane | App Yups and Nopes | Thursday, April 16th, 2009

This post kicks off the “App Yups and Nopes” section in which I’ll post both good and bad things I see applications doing. This differs from the other Yups and Nopes sections in that it talkes specifically about observed application behavior. My intent is to show why a specific behavior is undesirable, then present a solution.

In this situation, I connect my laptop to a projector to watch a movie. My screen resolution changes to match that of the projector. I see applications with the following behaviors:

– Nope: Resize application windows to fit on screen, regardless of window or application visibility.

The goal of resizing an application window on screen resolution change is to make sure the window is placed so that the user is able to manipulate it (move and resize.) An application is only required to resize and reposition windows that are actually visible. If a window is offscreen or the application is hidden, the window size and position should not be modified. When an offscreen window or a window of a hidden application is modified in this way, useless computation results. If the window cannot be seen, what use is there in constraining its bounds?

+ Yup: Resize application windows as they are made visible, making only the smallest change necessary to satisfy visibility and interaction requirements.

A common trick I use before connecting an external monitor of smaller resolution is to hide all applications — for most applications this prevents their window state from being altered. In this way when I have finished using the projector, I can unhide these applications and continue using them with a window state identical to before the resolution change. This works great for applications that implement correct window resizing behavior. Most Cocoa applications do this correctly by default. Noted offenders are iTunes and iPhoto, both of which require me to pointlessly resize their windows after the resolution is restored.

Note also that only the height must be constrained to the visible screen height — windows are allowed to be wider than the screen. Furthermore, it is not necessary to reposition the window such that its entire contents are visible — windows can hang off the edge of the screen on the left and right sides. This, of course, becomes more complicated when multiple displays are attached as these restrictions change.

Regression: Adobe CS4

Devin Lane | Regressions | Thursday, March 26th, 2009

So I was talking with my friends a while back, deep into one of my frequent rants about ineptitude in the software industry, when it struck me that I should start blogging this stuff. Also, my friends pointed out that the manner in which I deliver my criticism is particularly funny, and that I should post audio clips of this to accompany the rant.

I’ve decided to start a new section on this site devoted to these rants. The section is called “Regressions” and will contain my rants on various software issues that have been solved correctly before but, for some reason, are (still) present in some piece of software. These rants are a bit harsh: I’m trying to write it how I spit it when I get in the mood, and so if you find intense criticism and profanity offensive, I suggest you stop reading.

So, lets kick this bitch off with Adobe’s Creative Suite 4!

First, the installer is called “Setup.” On a Mac. See guys, on the Mac, we name things after what they really are. The correct name for this application is “Adobe CS4 Installer.” “Setup” is a) Windows-esque and b) useless for determining what this thing actually installs. Consider this regression #1.

Not only is the installer misnamed, but it’s in a folder with some other files. On the disk you see a folder with an icon that looks vaguely archive-like called “Adobe CS4 Master Collection.” I assumed this was the installer; what else would you put on the disk? Instead of the installer, however I see another Finder window open with 6 files: Bootstrapper.dmg, deployment, extensions, payloads, resources, Setup. What is all this crap? I just wanna install the apps dude, I don’t care about any of this junk.

After prompting for a password, the installer then proceeds to open a copy of the installer app running as root. This then makes another couple of child processes, which do something for a bit. Despite Apple’s consistent recommendation for and implementation of installers that use privilege-escalated tools only for the actual file copying portion, it seems that this part is too hard for Adobe, so they just launch the entire installer(s) as root. Nice job guys! Lets hope there’s no security flaws in your installer :) Regression #2.

I then get a dialog about some applications that need to be closed for installation. Photoshop of course is on there duh, so I close that. But what the fuck? Safari and Excel? What does that have to do with Adobe products? Presumably, Adobe thinks that because it can install its horrible PDF plugin for Safari and Office programs, you need to close these programs before you can install! Of course I don’t actually use these plugins because this is Mac OS X, where the graphics subsystem has had PDF support for 7 fucking years, and neither does anyone else who hasn’t been living under a rock. Regression #3.

Finally I get to the list of applications to install. I choose a custom install to see what there is and damn. That’s a LOT of crap to install. And fucking huge too! Acrobat 9 Pro is 1.2GB. How on earth you manage to make a PDF editor that big I have no idea, but clearly the folks at Adobe are good at it. Fortunately, Photoshop, which is about 2000 times more awesome and useful, is about half the size. Clearly Acrobat 9 has some absolutely AMAZING features that warrant this ridiculously huge installation size. Perhaps we should all aspire to make excessively large applications. Regression #4.

So I select some of these components and click install. The install finishes and I check my Applications folder to see the new apps. Wow. There’s a folder for each of the apps I wanted, of course, but there’s also a folder for some of the apps I didn’t want, containing an application container with nothing in it. Clearly along the packaging process Adobe thought that littering my hard drive with crap was acceptable. Regression #5.

You would think a big company like Adobe with a Mac userbase that is (or was) a significant portion of their customer base would be able to put more effort into their presentation, but perhaps to a big company these things are not such critical issues. With no serious competitor to Photoshop, customers are likely to overlook issues like these, if only because they have to.

[Note: Apologies if this seems a bit old -- I wrote it several months ago and completely forgot about it until today]

mach_absolute_time on the iPhone

Devin Lane | Programming | Wednesday, October 1st, 2008

As programmers on OS X are well aware, the units of mach_absolute_time() have been nanoseconds since 10.2, although this was only documented since 10.5. As the iPhone runs a slimmed down version of OS X, I thought it reasonable that the units would remain the same on the device.

Not so.

On the iPhone, the timebase is different. The mach_timebase_info structure contains a numerator and denominator that can be used to convert between the result returned by mach_absolute_time() and nanoseconds. On OS X Leopard, the numerator and denominator are both 1, meaning that the units are already in nanoseconds. On OS X iPhone, the numerator is 1,000,000,000, while the denominator is 6,000,000. Thus, every unit of absolute time on the iPhone is 166.67 nanoseconds.

So if you’re trying to get some timing from the iPhone using mach_absolute_time() and seeing times that indicate faster results on the iPhone, (not that this happened to me…), make sure to account for the difference in timebase.

Here’s the code for conversion between the iPhone’s mach_absolute_time() and nanoseconds:

/* Get the timebase info */
mach_timebase_info_data_t info;
mach_timebase_info(&info);

uint64_t start = mach_absolute_time();

/* Do some code */

uint64_t duration = mach_absolute_time() - start;

/* Convert to nanoseconds */
duration *= info.numer;
duration /= info.denom;

/* Log the time */
NSLog(@"My amazing code took %lld nanoseconds!", duration);

Time Machine: Switching Computers

Devin Lane | Leopard | Friday, September 19th, 2008

My main Mac is out of service for a few days as the screen gets replaced, so I performed a Time Machine restore onto another computer to use in the meantime. I’ll let this computer continue backing up to the same time machine volume, then do a TM restore back onto my main machine when it’s fixed.

On the temporary machine, Time Machine didn’t want to continue backing up to the same backup folder. From Time Machine’s perspective, this is a different computer and so it creates a separate folder to perform the backups in. The MAC address of the computer is set as an extended attribute (xattr) on this folder so TM knows which folder to use.

Allowing the temp machine to use the old machine’s backup folder is simple, but not as easy as it should be. The goal is to put the temp machine’s MAC address in the place of the original’s in the xattr on the backup folder. However, using xattr -w com.apple.backupd.BackupMachineAddress 00:00:00:00:00:00 doesn’t work. Instead, you need a little program that can write the address using the xattr API.

I’ve written a little program that performs the switch. It turns off acls on the backup volume, sets the new xattr, logs the old MAC address, then turns the acls back on. You’ll need to run it with root privileges.

It’s available here, or in svn at http://svn.shiftedbits.org/public/backupswitch/trunk.

Key Value Observing Improvements v1.2.2

Devin Lane | Programming | Saturday, September 13th, 2008

Quick bug fix update:

1.2.2 Fixes a crash due to a missing implementation of __KVOAdditions__dealloc__original__ on NSObject.

1.2.1 now runs on the iPhone.

The new updates are in SVN at http://svn.shiftedbits.org/public/KVOAdditions/trunk and tagged at http://svn.shiftedbits.org/public/KVOAdditions/tags/1.2.2

Key Value Observing Improvements v1.2

Devin Lane | Leopard, Programming | Wednesday, September 3rd, 2008

Hot on the heels of the 1.1 bug fix release comes version a freshly rewritten 1.2 with API cleanup, bug fixes, and a great new feature.

Lately, I’ve been using observers to allow an object to observe changes to its own properties. This means I don’t have to override the setter method (I’m using synthesized properties) and the observation is managed the same way that it would be externally. The current KVO implementation (at least, in non-GC land,) however, doesn’t allow you to remove observations from yourself in your -dealloc method. This is due to the way KVO works, by interposing a KVODeallocate function before your -dealloc method. It expects that all observers of the object have been removed at this point, and warns if they are not. This is, of course, blindingly annoying — you now have to create a method that gets called on your objects by their owner before they dealloc so that they can remove their observers.

To fix this, I’ve done a little interposing of my own, putting in another dealloc method before KVODealloc that will remove self observers and call a -KVODealloc method on the deallocating observer object before KVODeallocate is called. If you don’t want automatic removal, simply return NO from +automaticallyRemoveSelfObservations on the class of your choice.

The new updates are in SVN at http://svn.shiftedbits.org/public/KVOAdditions/trunk and tagged at http://svn.shiftedbits.org/public/KVOAdditions/tags/1.2
(more…)

Key Value Observing Improvements

Devin Lane | Programming | Thursday, July 24th, 2008

Key value observing is quite a useful tool, no doubt about it. But it has a singularly annoying manner of informing the observer of a change. The -[NSObject observeValueForKeyPath:ofObject:change:context:] method is sent to the observer when a change occurs. It’s up to the implementor to parse the `change’ dictionary to figure out what changed.

When I use observers, I usually want a method called on my class when a change occurs, similar to target/action with UI elements. So my -[NSObject observeValueForKeyPath:ofObject:change:context:] is basically a set of if’s for each key I’m observing that calls the appropriate method.

This turns out to be pretty nasty, especially when you have multiple classes in a hierarchy implementing the method. It wasn’t obvious to me that I needed to call super in this method. Thirty minutes of debugging later, and I was convinced there had to be a better way of doing it.

To this effort, I present two new APIs. To NSObject, I add -[NSObject addObserver:forKeyPath:options:selector:] and to NSArray, -[NSArray addObserver:toObjectsAtIndexes:forKeyPath:options:selector]. The code is available here, or keep reading for the details.

Update 1.1: Two critical bug fixes. Please update to the new version if you have the old one. Also, anonymous svn is now available if you want to use an external. http://svn.shiftedbits.org/public/KVOAdditions/trunk. (more…)

SSE Optimized Compositing

Devin Lane | Programming | Tuesday, January 29th, 2008

As part of a graphics API I’ve been working on (for my own use, it’s hardly ready for production,) I decided to try learning SSE optimization by making the compositing routine faster.

I came up with an implementation which, according to my tests, is 3-5X faster than the non-optimized version. The optimized version below composites a single color starting at a destination pixel buffer for a specified run of pixels. It uses source over compositing on a RGBA, 8bpc, integer pixel buffer.

The optimized version is presented below:

(more…)

Next Page »

Powered by WordPress | Theme by Roy Tanck, modifications by Devin