Vincent Gable’s Blog

April 29, 2010

What Am I About To Call?

Filed under: Cocoa,iPhone,MacOSX,Objective-C,Programming,Reverse Engineering,Tips | , ,
― Vincent Gable on April 29, 2010

Say you’re in gdb, and about to execute a call instruction for dyld_stub_objc_msgSend, how do you know what’s about to happen?

On i386

(gdb) x/s *(SEL*)($esp+4)

tells you the message that’s about to be sent.

(gdb) po *(id*)$esp

tells you the target object that’s about to get the message.

December 9, 2009

Compile Safer

Filed under: Bug Bite,C++,Cocoa,iPhone,MacOSX,Objective-C,Programming | , , ,
― Vincent Gable on December 9, 2009

Peter Hosey explains what warnings he uses and why. It’s good, but long. Fortunately, you can just grab a script, and enable those warnings in your Xcode projects.

Warnings = Errors

If I could force just one compiler flag on everyone who’s code I use, it would be TREAT_WARNINGS_AS_ERRORS. As a rule, things don’t get improved if they aren’t broken. (How many times have you said “I’ll come back and fix this code later”? Yeah.) Warnings fester and grow on each other, until they cause a real breakage. It’s an inescapable evil of building software with finite resources.

If a warning isn’t worth stopping the build over — it’s not worth checking for in the first place.

Use the Latest Tools

Specifically, if you aren’t using Snow Leopard and Xcode 3.2 to build your Objective-C code, you are crazy. Trust me, painless static analysis is worth upgrading for. It catches maddening memory leaks, not just trivial type errors, like adding an int to an NSArray, that you would catch immediately.

October 12, 2009

Don’t Check malloc()

Filed under: C++,Cocoa,iPhone,MacOSX,Objective-C,Programming,Quotes,Tips | , , ,
― Vincent Gable on October 12, 2009

There’s no point in trying to recover from a malloc failure on OS X, because by the time you detect the failure and try to recover, your process is likely to already be doomed. There’s no need to do your own logging, because malloc itself does a good job of that. And finally there’s no real need to even explicitly abort, because any malloc failure is virtually guaranteed to result in an instantaneous crash with a good stack trace.

Mike Ash

This is excellent advice. Peppering your code with if statements harms readability and simplicity.

It’s still a good idea to check large (many MB) mallocs, but I can’t imagine recovering gracefully from a situation where 32 byte memory allocations are failing on a modern desktop.

October 6, 2009

‘Yum!’

Filed under: Design,iPhone,MacOSX,Programming | , , , , ,
― Vincent Gable on October 6, 2009

I give Microsoft’s current Mac software some shit, but I think it’s deserved. So it’s only fair I mention their glory days.

From “Classic” Mac OS 8.1 in 1998 through Mac OS X 10.2 Microsoft’s Internet Explorer (for Mac) was the default web browser Apple chose for Mac OS. The very fist iMac? It came with IE:mac, just like the first version of Mac OS X.

And IE:mac wasn’t so bad, for it’s era. (It was the first browser to have color correcting PNGs, by the way!) There was one really neat feature that I think is worth calling out: it would match your iMac’s color, automagically.

Technical Details That I Only Half Remember

If you have a better understanding of how this worked, please let me know! I couldn’t find any details online. Mostly, I’m writing down what I remember before I forget.

The poorly named Gestalt function lets you check information about the Mac OS runtime, like “what version of Mac OS is this?“. You pass it a FourCharCode, and replies with a 32-bit value or an error code — old stuff from the “Classic” Mac OS days.

There was an undocumented code, 'yum!' 1, that returned the color of an the iMac or iBook case. IE:mac would check this when it first started, and choose a color scheme to match the operator’s Mac. It was a seamless personal touch that really impressed me.

It’s the sort of thing I’d like to see more of on today’s multi-colored iPods and iPhones.

1It might have been 'Yum!', I don’t remember exactly, and Gestalt() returns gestaltUndefSelectorErr, -5551, for all of variations on my MacBook Pro under Snow Leopard.

September 17, 2009

Installing Mac Apps

Filed under: Accessibility,MacOSX,Programming,Usability | , , ,
― Vincent Gable on September 17, 2009

Today’s Daringfireball article on the shortcomings of the Mac application-install procedure is worth a skim. Gruber’s suggestion that Mac OS X automatically move 3rd-party applications into the /Applications/ folder on first-run, (a la the dashboard widget install process) is a good one1. Since Mac OS X already prompts you on first run (“Are you sure you want to run a program Apple didn’t write?”) it’s hard to see any downsides to this idea.

But that’s not the behavior we have today1.

Don’t Use a Damn .dmg!

As it stands today, I don’t see a good reason to ship your apps as a .dmg. I’ve been suspicious of disk images for a few years now; and usability tests show that people get confused by them.

Distribute your application as a single .app in a .zip archive. What possible use are other files besides the application? If a “Readme” file should be read before using the application, then show it when the application is first launched.

Installers are opaque and un-Mac like. There’s always a risk that they’ll install something that breaks the computer. As a developer I am even more suspicious of installers on the Mac, because I know how broken Apple’s installer tools are.

Of course, as a developer, I know that applications do sometimes need to install components. And here the best solution is for the application to check it’s environment and ask to install missing components as needed (in essence be it’s own installer). It’s more robust, since it detects-and-corrects missing or damaged components. It always preserves the user-facing abstraction that the icon is the application.

Applications shouldn’t install hacks dangerous enough to require a bundled user-facing unisntaller. To make IMLocation work, I had to install a background process — but I made it intelligent enough that it would uninstall itself if the main application had been deleted. Yes, this is more work, but it’s worth it.


1Another idea is to make Safari and Firefox smart enough to download applications directly into the right /Applications/ folder, bypassing the usual downloads folder. This elegantly solves the instillation problem, although it creates some new problems.

2Although it would be a cool hack to write.

July 19, 2009

For iPhone and or iPod Touch and or Other Things As Well

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

It’s very clear that a program “for Mac OS X” works with any personal computer Apple sells, because they all have “Mac” in their name. Unfortunately, the flavor of OS X that runs on the iPhone and iPod Touch is officially called “iPhone OS” by Apple, which it implies an incompatibility with the iPod Touch, and any future device that doesn’t have “phone” in the name.

I don’t know a good way to unambiguously say that a program is for any iPhone OS device, without tedious enumeration.

“For iPhone OS” sounds like it excludes the iPod Touch.

“For all models of iPhone and iPod Touch” sounds terrible. It will sound even worse when Apple comes out with other iPhone OS devices (“…for iPhone or iPod Touch or iTablet or iFPGA…”).

Apple could help by renaming “iPhone OS” to “Mobile OS X”, but I don’t see this happening.

I personally lean towards using “for iPhone” in general writing, and clarifying, if necessary, in “systems requirements” fine print. This feels closest to how the press covers iPhone OS applications, and of course it’s how Apple named the OS.

I’d love to hear what you call iPhone OS applications, and why.

May 29, 2009

Don’t Waste Memory to Save Time

Filed under: iPhone,MacOSX,Programming,Usability | , , ,
― Vincent Gable on May 29, 2009

In the provocatively titled, Space is Time: How Your CS Theory Class Lied to You, Greg Parker explains why hogging memory to speed up your program is a bad idea on consumer-level devices.

Space is time. An optimization that makes your program faster may make the user’s system slower overall. Play well with others.

I think it’s excellent advice. There’s only one point I want to make (and it must be made now, before silent SSD hard disks become more popular).

Every time your whole computer is nonresponsive, it makes that grrnrrnrnrr sound, right? That’s because it ran out of RAM, and the virtual memory system is thrashing to the disk. In the real word, on computers like the one you are on now, low memory plays a part in every system-wide slowdown.

Hogging the CPU can make your application slow. (Although if you pay attention to threading, and the run loop, using 100% of every core shouldn’t kill responsiveness and usability.) But hogging memory makes the whole damn world slow, including your application. And the worst case scenario of bad memory management kills the system deader then a kernel panic.

This isn’t to say that caching never makes sense. But a healthy respect for memory usage generally trumps worrying about the CPU.

April 22, 2009

-[NSURL isEqual:] Gotcha

Filed under: Bug Bite,Cocoa,iPhone,MacOSX,Programming,Sample Code | , , , , , ,
― Vincent Gable on April 22, 2009

BREAKING UPDATE: Actually comparing the -absoluteURL or -absoluteString of two NSURLs that represent a file is not good enough. One may start file:///, and the other file://localhost/, and they will not be isEqual:! A work around is to compare the path of each NSURL. I’m still looking into the issue, but for now I am using the following method to compare NSURLs.

@implementation NSURL (IsEqualTesting)
- (BOOL) isEqualToURL:(NSURL*)otherURL;
{
	return [[self absoluteURL] isEqual:[otherURL absoluteURL]] || 
	[self isFileURL] && [otherURL isFileURL] &&
	([[self path] isEqual:[otherURL path]]);
}
@end

[a isEqual:b] may report NO for two NSURLs that both resolve to the same resource (website, file, whatever). So compare NSURLs like [[a absoluteString] isEqual:[b absoluteString]]. It’s important to be aware of this gotcha, because URLs are Apple’s preferred way to represent file paths, and APIs are starting to require them. Equality tests that worked for NSString file-paths may fail with NSURL file-paths.

The official documentation says

two NSURLs are considered equal if they both have the same base baseURL and relativeString.

Furthermore,

An NSURL object is composed of two parts—a potentially nil base URL and a string that is resolved relative to the base URL. An NSURL object whose string is fully resolved without a base is considered absolute; all others are considered relative.

In other words, two NSURL objects can resolve to the same absolute URL, but have a different base URL, and be considered !isEqual:.

An example should make this all clear,

NSURL *VGableDotCom = [NSURL URLWithString:@"http://vgable.com"];
NSURL *a = [[NSURL alloc] initWithString:@"blog" relativeToURL:VGableDotCom];
NSURL *b = [[NSURL alloc] initWithString:@"http://vgable.com/blog" relativeToURL:nil];
LOG_INT([a isEqual:b]);
LOG_INT([[a absoluteURL] isEqual:[b absoluteURL]]);
LOG_ID([a absoluteURL]);
LOG_ID([b absoluteURL]);

[a isEqual:b] = 0
[[a absoluteURL] isEqual:[b absoluteURL]] = 1
[a absoluteURL] = http://vgable.com/blog
[b absoluteURL] = http://vgable.com/blog

Remember that collections use isEqual: to determine equality, so you may have to convert an NSURL to an absoluteURL to get the behavior you expect, especially with NSSet and NSDictionary.

Getting the Current URL from a WebView

Filed under: Cocoa,iPhone,MacOSX,Objective-C,Programming,Sample Code | , , ,
― Vincent Gable on April 22, 2009

UPDATED 2009-04-30: WARNING: this method will not always give the correct result. +[NSURL URLWithString:] requires it’s argument to have unicode characters %-escaped UTF8. But stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding will convert # to %23, so http://example.com/index.html#s1 would become http://example.com/index.html%23s1. Unfortunately, the two URLs are not equivalent. The un-%-escaped one refers to section #s1 in the file index.html, and the other tries to fetch the file index.html#s1 (“index dot html#s1”). I have not yet implemented a workaround, although I suspect one is possible, by building the NSURL out of bits of the JavaScript location object, rather then trying to convert the whole string.


UIWebView/WebView does not provide a way to find the URL of the webpage it is showing. But there’s a simple (and neat) way to get it using embedded JavaScript.

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script

Is a deceptively powerful method that can execute dynamically constructed JavaScript, and lets you embed JavaScript snippets in Cocoa programs. We can use it to embed one line of JavaScript to ask a UIWebView for the URL it’s showing.

@interface UIWebView (CurrentURLInfo)
- (NSURL*) locationURL;
@end
@implementation UIWebView (CurrentURLInfo) - (NSURL*) locationURL; { NSString *rawLocationString = [self stringByEvaluatingJavaScriptFromString:@"location.href;"]; if(!rawLocationString) return nil; //URLWithString: needs percent escapes added or it will fail with, eg. a file:// URL with spaces or any URL with unicode. locationString = [locationString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; return [NSURL URLWithString:locationString] } @end

With the CurrentURLInfo category, you can do aWebView.locationURL to get the URL of the page a WebView is showing.


One last note, the

if(!rawLocationString)
	return nil;

special-case was only necessary, because [NSURL URLWithString:nil] throws an exception (rdar://6810626). But Apple has decided that this is correct behavior.

March 31, 2009

How To Write Cocoa Object Getters

Setters are more straightforward than getters, because you don’t need to worry about memory management.

The best practice is to let the compiler write getters for you, by using Declared Properties.

But when I have to implement a getter manually, I prefer the (to my knowledge) safest pattern,

- (TypeOfX*) x;
{
  return [[x retain] autorelease];
}

Note that by convention in Objective-C, a getter for the variable jabberwocky is simply called jabberwocky, not getJabberwocky.

Why retain Then autorelease

Basically return [[x retain] autorelease]; guarantees that what the getter returns will be valid for as long as any local objects in the code that called the the getter.

Consider,

NSString* oldName = [person name];
[person 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 the implementation of [x name] used retain/autorelease, then oldName would still be valid, because it would not be destroyed until the autorelease pool around the NSLog was drained.

Also, autorelease pools are per thread; different threads have different autorelease pools that are drained at different times. retain/autorelease makes sure the object is on the calling thread’s pool.

If this cursory explanation isn’t enough, read Seth Willitis’ detailed explanation of retain/autorelease. I’m not going to explain it further here, because he’s done such a through job of it.

Ugly

return [[x retain] autorelease]; is more complicated, and harder to understand then a simple return x;. But sometimes that ugliness is necessary, and the best place to hide it is in a one-line getter method. It’s self documenting. And once you understand Cocoa memory management, it’s entirely clear what the method does. For me, the tiny readability cost is worth the safety guarantee.

Big

return [[x retain] autorelease]; increases peak memory pressure, because it can defer dealloc-ing unused objects until a few autorelease pools are drained. Honestly I’ve never measured memory usage, and found this to be a significant problem. It certainly could be, especially if the thing being returned is a large picture or chunk of data. But in my experience, it’s nothing to worry about for getters that return typical objects, unless there are measurements saying otherwise.

Slow

return [[x retain] autorelease]; is obviously slower then just return x;. But I doubt that optimizing an O(1) getter is going to make a significant difference to your application’s performance — especially compared to other things you could spend that time optimizing. So until I have data telling me otherwise, I don’t worry about adding an the extra method calls.

This is a Good Rule to Break

As I mentioned before, getters don’t need to worry about memory management. It could be argued that the return [[x retain] autorelease]; pattern is a premature optimization of theoretical safety at the expense of concrete performance.

Good programmers try to avoid premature optimization; so perhaps I’m wrong to follow this “safer” pattern. But until I have data showing otherwise, I like to do the safest thing.

How do you write getters, and why?

« Newer PostsOlder Posts »

Powered by WordPress