Rss

Archives for : objc

Swift and the Last Mile

So the initial thrill of Swift is over, and lots of us are digging in. So far, there haven’t been any screaming denunciations that I’ve heard, but surely they’re coming… if there aren’t, then we’re all too easy to please.

The best thing I can say about Swift is that it’s deeply pragmatic. Rather than set about creating some beautiful, “perfect” language (or identifying one that already exists), Swift’s highest priorities include reconciling C and Objective-C and the entirety of the existing frameworks for iOS and OS X. Having to rework all those frameworks to suit some other language is presumably a non-starter, as Apple’s tried that without success three times now (the Cocoa bridges from Java, Ruby, and Python).

As I rework our iOS intro book for Swift — no, it won’t be formally announced until the publisher is convinced it’s a go, but anyone following me on Twitter knows I’ve been working hard on it for the last week — much of what Swift does for beginners pleases me. To the degree that it unifies the conventions of C and Obj-C under the guise of global functions and methods respectively, it certainly saves me from having to tell beginners “square brace idiom here, but parentheses here, because history.” And Swift’s closure syntax is far more memorable than blocks ever were; I’m already writing them by hand without resorting to the cheat sheet.

But the fact that the frameworks were programmatically adapted for Swift rather than carefully combed over introduces some new pain points.

Continue Reading >>

We were already dead before the chip even shrank

Interesting questions are floating around about what changes iOS developers need to make, given the introduction of dual-core on the iPad 2, particularly in terms of threading. One question that came up is whether we should still default to nonatomic properties, as multiple threads could run through the getters and setters at the same time.

My opinion, at this time, and as expressed in a devforums thread, is that any code that will break under dual-core is already broken under single-core. The problem is not that two threads can execute simultaneously with dual-core, it’s that you have code sections that aren’t thread-safe and need to be. Dual-core will, at most, encounter the problem more frequently.

On iOS, we tend to make all properties involving UIKit nonatomic, meaning they do not synchronize access to the getter or setter (i.e., enforce a one-thread-at-a-time rule), for performance reasons. The reason for this really doesn’t have to do with the nature of atomicity, per se, it’s that any time you touch UIKit, you must do so from the main thread. Declaring nonatomic is just a consequence of this: the property should only be called from one thread, main, so you shouldn’t need to worry about coordinating multi-threaded access to the property. In other words, your thread management issues lie elsewhere, not with the atomicity of the property, so you can still declare these properties as nonatomic.

On the other hand, properties that aren’t related to UIKit may need to be atomic. If you think you’ll only ever call it from a single thread, fine, but if you’re not consciously enforcing that and just getting lucky, then you may be just one block, NSOperation, or callback on a mystery thread away from a nasty race condition.

In the first version of our iPhone SDK Development book, Bill and I all but ignored threading — NSOperation doesn’t even appear in the index — because we kept things simple enough that everything could be assumed to be running on the main thread. In the new world of blocks and Grand Central Dispatch, this is no longer a safe assumption, and we’d probably need to engage threading issues early. A greater awareness of threads and a willingness to use NSOperation or other threading approaches should help with performance on the new device too.

Do Over

One thing I’m coming away from CodeMash with is a desire to clean up a lot of my old habits and dig into tools and techniques I’ve long known were available, but haven’t used. In some ways, I’m still stuck in my iPhone OS 2 ways in an iOS 4 world.

Daniel Steinberg has taken a pretty extreme position, but one that makes sense: he no longer has any private instance variables in his header files, since the current SDK allows you to put them in the implementation. Combined with the use of a class extension in the .m for helper methods, this makes it possible for the header to be exactly what it’s supposed to be: an exposure of the public interface to your class, with no clues about the implementation underneath.

To my mind, Daniel was also the winner of the “mobile smackdown” session, in which one presenter each from the iOS, Windows Phone 7, and Android camps was given 15 minutes to develop a trivial Twitter app that could manage a persistent list of user names and, when tapped, nagivate to that user’s twitter.com page. I say Daniel won because his iPhone app was the only one to complete all the features in time (actually, Daniel needed an extra 30 seconds to finish two lines of code). The Windows Phone presenter never made it to adding new names to the list, and the Android guy didn’t get around to showing the user’s page. One of Daniel’s wins was in using the “use Core Data for storage” checkbox: by graphically designing a data model for his “Twitterer” class, he picked up persistence and his table view in one fell swoop. Now that I think of it, I don’t remember how, or if, the other platforms persisted their user lists. I don’t use Core Data often, but after this demonstration, I’m much more inclined to do so.

There was a whole session on unit testing for iOS, something I just explored on my own for my first day tutorial (and even then, I was using it as much for illustrating the use of multiple targets in an Xcode project as for the actual testing of features). I’ve never been religious about testing, particularly given that GUIs have long proven difficult to make fully testable, but with a testing framework buit into Xcode (not everyone’s favorite, but it’s a start), it’s well worth rethinking how I could use it to get some measure of test coverage and fight regressions.

All of this makes me worry about the status of the iPhone SDK Development book I wrote with Bill Dudney. That was an iPhone OS 2 book that slipped far enough to be an early iPhone OS 3 book, with the addition of new chapters for important new frameworks like Core Data and Game Kit. But with iOS 5 surely looming, some of it is starting to look pretty crusty. In particular, the arrival of Grand Central Dispatch means that means that it’s no longer safe to blithely ignore threads, as we did, since there are scenarios where you can have even simple code that unwittingly manages to get off the main thread, which means trouble for UIKit. Furthermore, new frameworks demand blocks for completion handlers, so that’s something that now needs to appear early (and given that the block syntax is pure C, readers will need to be acclimated to C earlier than they used to). And I’ve long wanted to move the debugging and performance chapters (my favorites, actually) much earlier, so readers can figure out their own EXC_BAD_ACCESS problems. Not that I can currently even plan on a rev to that book – I still have four chapters to go on Core Audio, and would need a long and difficult conversation with the Prags besides. But I certainly see where my guidance to new developers has changed, significantly, in the last few years.

Betweeen Christmas break, a week of CodeMash prep and home office reorganization, and CodeMash itself, I feel like I’ve been off for a month (and my MYOB First Edge financial status would seem to agree). I feel ready to start anew this week, and making a clean break with the past suits this mood nicely.

Playing with KVC

Nothing major, I was just looking at some different approaches to setting contents of nib-loaded table cells that may need to be more dynamic than usual. I caught myself playing with key-value coding and thought that the following three lines – all of which do the same thing – were kind of neat:


cell.textLabel.textColor = [UIColor greenColor];
[cell.textLabel setValue: [UIColor greenColor] forKeyPath: @"textColor"];
[cell setValue: [UIColor greenColor] forKeyPath: @"textLabel.textColor"];

All you have to do to participate in KVC is to use properties, or follow a convention for naming getters and setters.

I’m looking at doing some custom tables (see my impromptu screencast on flippable cells) and was wondering about approaches to putting different kinds of cells different table instances. Seems like KVC would be one technique to get at the labels and other subviews of variant UITableViewCell subclasses, without having to know the actual class at compile time. Guess we’ll find out if it works nicely.

A shelf tour

After last week’s crunch, closing the last of the errata and our own to-dos, we have officially sent iPhone SDK Development to production, meaning it will now get a second copy-edit, typesetting, printing, binding, and shipping to your anxious hands.

A note of thanks is in order to the beta-program purchasers and the thousands of errata they filed, along with over 500 forum discussions on the book. No getting anything past this group, that’s for sure.

Well, maybe one little thing. With the Snow Leopard release date now set, it looks like Apple engineers have a chance to reply to e-mail from several months ago. Last night, I heard back from the dns-sd.org folks (at an @apple.com address) about my request back in June to reserve a Bonjour service type for the Game Kit example in the book. The bad news is, I included an illegal character in my service name, so I had to change it from amiphd_p2p to amiphd-p2p, which is now part of the public list of DNS SRV (RFC 2782) Service Types. And the only reason that’s bad is that the book still has the name with the underscore, and I’m currently locked out of the book during production.

It’s a minor point, and it will get fixed, it’s just silly-bad timing, getting a reply to a two-month-old e-mail just a day after we wrapped the book.

Another interesting @apple.com e-mail has to do with the Clang Static Analyzer that we cover in the performance chapter, but that remains NDA for now. Anyways, they’ll have their own updates in due course, so watch their page.

Related point: I went to the Barnes & Noble on 28th Street for the first time in ages today, and drifted by the computer book section. It’s probably the biggest in Grand Rapids, for what that’s worth. Computer book sections are shrinking everywhere, particularly the programming sections, for a number of reasons: anything nichey is a non-starter at retail and is basically only available via Amazon and the like, programmers are eagerly jumping into eBooks (or bundles where you get a PDF now and the paper book when it’s ready), some programmers prefer the immediacy of blogs and other informal sources to stuffy books, and of course nearly any computer eBook of any significance is on bitTorrent (including ours, despite the fact that the unauthorized PDFs all clearly identify the reader who chose to post his or her copy). All of which goes to explain why your local retailer has less reason to stock computer books when they can make more money off political screeds and trifling fiction. And, as I discussed a few weeks back, why you’re going to continue to see fewer and fewer programming books going forward.

Still, the iPhone SDK is such a hot topic that even all this friction can’t stop it from being a popular topic with readers and authors alike. There were at least four other iPhone programming books on the shelves, and I took a first peek at several of them today. Note of explanation here: when writing a book, I never look at anything that could be considered a “competing” book. It’s my own mental firewall that ensures that my work is my own, and that I don’t lift ideas from fellow authors. That said, I do read the official docs, both to learn things myself and to make sure that the book I’m writing goes beyond what you can get for free. There’s no value for the reader (or the writer) if the book is just a paraphrase of Apple’s programming guides.

I think the only one on the shelves today that is officially updated for iPhone SDK 3.0 is Dave Mark’s Beginning iPhone 3 Development book, which features significant coverage of Core Data, probably the most significant of the new features in iPhone SDK 3.0. Of the older titles covering iPhone 2.x, I saw Erica Sadun’s, Jonathan Zdziarski’s, Neal Goldstein’s and Christopher Allen and Shannon Appelcline’s books.

They’re probably all worth a deeper read, though a glance through them and a mental comparison to my own project of the last year shows some similarities and differences. I’m sure all of us are grateful for the ease of getting screenshots from the simulator, as all the titles are rich with illustrations. Nearly all of them cover OpenGL, which ours actually doesn’t, I think because Bill thought that readers would be better served by studying OpenGL on its own (and that there isn’t enough unique about its iPhone implementation… as opposed to say, SQLite, which I put in the book not so much for the SQL or even the C API as for the strategies of managing the database files within an iPhone context: creating them with your Mac’s sqlite3 interactive shell, putting them in a bundle for deployment and backup, etc.). On the other hand, I think ours is the only book to talk about debugging and the various performance tools (Shark, Instruments, and the third-party Clang Static Analyzer). Unsurprisingly, given my inclinations, it looks like we hit media a lot harder than our peers. Counting the new-for-3.0 “Music Library Integration” chapter, we ended up with four media chapters, totaling nearly 75 pages. And that’s after cutting the too-hard-for-now Audio Streaming chapter.

It looks like all the other authors assumed a pre-requisite level equivalent to ours: know a curly-brace language, preferably C, and we’ll cover Objective-C as we go. We’ve had a few scripting-language converts (Flash/ActionScript people, it seems) on our forums who have a hill to climb with the latent subtle C-isms, mostly the memory stuff, and I wonder if our colleagues have had similar experiences with their audiences. C knowledge is a strange thing: all us old folks think it’s a lingua franca, yet I think we all know that younger developers no longer learn it as a matter of course, and may not be particularly eager to do so.

Anyways, I imagine everyone else is rushing out their 3.0 updates too, so it’ll be interesting to see what new features get covered, and what our readers still want from us in future versions or more advanced titles.

Fun with varargs

For reasons you don’t need to know about (yet), I wanted to get my usual crutch of a logging UITextview implemented in plain C.

I hadn’t wanted to mess with varargs, so I usually write an Obj-C method like this:


-(void) screenLog:(NSString*) s {
	textView.text = [NSString stringWithFormat:@"%@%@n",
		textView.text, s];
}

What this does is to create an autoreleased NSString built from a format that’s just two strings concatenated together — the current contents of the text view and the argument string — and a new-line character. It then sets this new string as the new text of the UITextView

It sucks a little bit to call, because you have to pass in an NSString, not the usual varargs you’d use with NSLog. So to do:

NSLog (@"Current age: %d", 41);

you’d have to build the string up-front, like this:

[self screenLog: [NSString stringWithFormat: @"Current age: %d", 41]];

So, kind of annoying, but still useful when you want to log to the screen instead of standard out, like I’ve had to do this week while doing some Bonjour stuff between multiple devices scattered about the office, at most one of which gets to log to Xcode’s console. Yesterday’s post, with onscreen output of the two devices getting each other’s test message, shows why this is a nice crutch to have for experiments, prototypes, and throwaways.

Anyways, I actually wanted to do this with plain ol’ C, and happened across Matt Gallagher’s great write-up of varargs in Cocoa. Combining that with the realization that NSString has some method signatures that take a va_list, I was able to rewrite my screen logger in plain ol’ C:

void LogToUITextView (UITextView *view, NSString* format, ...) {
	va_list args;
	va_start (args, format);
	NSString* appendedText = [[NSString alloc]
				initWithFormat: format arguments: args];
	view.text = [NSString stringWithFormat:
				 @"%@%@n", view.text, appendedText];
	[appendedText release];
}

Calling it feels a lot more like calling NSLog:

- (void)viewDidLoad {
    [super viewDidLoad];

	// customize point
	LogToUITextView(textView, @"Current age: %d", 41);
	LogToUITextView(textView, @"Current weight: %3.1f", 243.6);
	LogToUITextView(textView, @"Available fonts:n %@",
				[UIFont familyNames]);
}

And check it out: it actually works:

varargs-logging-function

I’ll probably adapt the varargs approach in my Obj-C logging function going forwards, but still, it’s nice to be able to make the procedural C call, especially since you could switch all NSLog calls to LogToUITextView with a single global replace.

Update: Here’s an even “more C” version that’s functionally equivalent:

void LogToUITextView (UITextView *view, NSString* format, ...) {
	va_list args;
	va_start (args, format);
	CFStringRef appendedText = CFStringCreateWithFormatAndArguments (
		kCFAllocatorDefault,
		NULL,
		(CFStringRef) format,
		args);
	CFStringRef newText = CFStringCreateWithFormat (
		kCFAllocatorDefault,
		NULL,
		(CFStringRef) @"%@%@n",
		view.text,
		appendedText);
	view.text = (NSString*) newText;
	CFRelease (newText);
	CFRelease (appendedText);
}

Obviously wordier, and we lose a convenient autorelease, since CoreFoundation doesn’t have autoreleasing.

I didn’t know 1718449215 was the 4CC for “fmt?” I do now.

You know things are going badly when you get errors so cryptic and so consistently that you write yourself a pretty-print method to make sense of them.

Here’s where I was as of last night:


- (void) failTo: (NSString*) functionName withOSStatus: (OSStatus) stat {
	NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain
			code:stat userInfo:nil];
	NSLog (@"Error in %@: %@", functionName, [error description]);
}

Which allows me to catch errors like this:


if (audioErr != noErr) {
	[self failTo: @"AudioQueueNewInput" withOSStatus: audioErr];
	return;
}

And which produces output like this. A lot.

2009-02-03 21:20:18.874 AQRecorderThrowaway[3522:20b] Error in AudioQueueNewInput: Error Domain=NSOSStatusErrorDomain Code=1718449215 "Operation could not be completed. (OSStatus error 1718449215.)"

And a little searching through the Audio Queue Services documentation tells us that 1718449215 is is the four char code for fmt?, also known as kAudioFormatUnsupportedDataFormatError.

Opt-in Complexity

Last month, Erica posted a blog heralding the introduction of AVAudioPlayer in iPhone OS (and SDK) 2.2. She writes:

When the SDK finally did roll around, its Audio Queue approach to handling audio playback and recording proved to be an extreme disappointment. A quick glance through the Audio Queue Services programming guide reveals both the power and complexity of the service. Involving pages of low-level programming just for even the simplest audio requests, Audio Queues are perfect for serious performance-driven developers but lack the easy-to-use hooks that Celestial had provided. With Celestial, you could load a URL and then just play it.

Erica makes an excellent point here that gets overlooked: Audio Queue Services is powerful, as well as complex. Granted, with audio, we have something of an 80/20 scenario: presumably, about 80% of the iPhone developers with any use for audio need only about 20% of the Audio Toolbox’s functionality (namely, playback, volume and pan controls, and maybe level metering). So they’re probably very happy to have AVAudioPlayer.

But what about the other 20%? There’s a group of audio developers for whom simple playback is not enough. These are the guys and gals who want to:

  • Stream audio from the network
  • Pick out Shoutcast metadata from said stream
  • Apply effects
  • Inspect the audio format
  • Inspect metadata
  • Edit
  • Convert between formats
  • Perform monitoring other than peak/average power level
  • Perform arbitrary DSP (e.g., FFT frequency matching for a Karaoke Revolution / Rock Band type game)

Now how are you going to design an API to make them happy, while not drowning the basic developer with a hundred method signatures they won’t be able to make heads or tails of?

Intriguingly, Apple’s APIs on the Mac and iPhone largely don’t even try. Instead, the complex stuff gets segregated down to the lower levels of the SDK stack — Core Media and Core Services — while Cocoa and Cocoa Touch’s higher-level abstractions provide the most broadly used functionality.

In the case of audio, that means the developer with simple playback needs can stay in Obj-C and use AVAudioPlayer and not worry about things like latency or effects. When he or she is ready to opt in to more complexity, the first step is to use the C-based Audio Session API to describe how the app interacts with the rest of the system (can it mix its sounds with music playing from the iPod app, for example… does it want to be notified when the output path chances, like when the user removes the headphones, etc.). And if the developer needs more power, then they choose complexity and move on to Audio Toolbox (or perhaps even Core Audio… a DevForums thread and a blog by developer Michael Tyson report extremely low latency by using the RemoteIO audio unit directly).

This isn’t just true of the media APIs. You also see it in Foundation versus Core Foundation. The first chapter I did for the Pragmatic Programmers’ iPhone book was an omnibus I/O chapter (which later became separate chapters on file and network I/O), and while working on the networking portion, I wrote an example that used Cocoa’s NSHost class and NSStream‘s getStreamsToHost:port:inputStream:outputStream: method. It worked fine on the simulator, but started giving compiler warnings when I finally got my certificate. Search for the method in the documentation and switch between the Mac OS X and iPhone Doc Sets to see the problem: NSHost and getStreamsToHost:port:inputStream:outputStream: are not part of the public iPhone API (a hint of the reason why is on DevForums). Hilariously, it was only after I’d gone on to rewrite it with the lower-level, procedural-C CFNetwork that I decided to take a step back and say “you know what, the Obj-C URL Loading System is going to be enough for 80-90% of our readership’s networking needs.” Again, the functionality of opening a stream to an arbitrary port on an arbitrary host is there, but if you’re the 1 in 10 developers who really really needs to do that, then you’re going down to CFNetwork and using something like CFStreamCreatePairWithSocketToHost().

Need time zone awareness? NSTimeZone is your friend. Need to know every time zone that the device supports? Get to know CFTimeZoneCopyKnownNames(). Again, a niche-ier feature lives down at the Core Foundation level, and isn’t wrapped by an equivalent call in Foundation, though it’s easy enough to switch to procedural C and make the one-off lower-level call.

It’s an interesting trait that the Mac and iPhone stacks work this way, opting in to complexity and keeping the higher-level APIs sparser and simpler, and you have to wonder whether it’s a conscious design decision or a happy accident. After all, a key reason to put so much functionality in the lower-level procedural-C layers — aside from performance benefits from not having to do Obj-C message dispatch — is that these C APIs can be called equally easily from Carbon or Cocoa apps. But of course, the whole idea of Carbon/Cocoa compatibility is irrelevant on the iPhone, where Carbon is nonexistent. In a purely iPhone world, the only reason to have the complex stuff be C-only is to move the tricky, nichey, sophisticated stuff out of the way, optimizing the Obj-C APIs for the most common uses.

Advantage: it does make Cocoa a pleasure to work with. Disadvantage: non-trivial apps are almost surely going to need to make these low-level calls sooner or later, and switching between Obj-C and procedural C on a line-by-line basis takes some getting used to. Still, making the complex stuff an opt-in ultimately makes the SDK both more approachable and more interesting.

[Cross-posted to O’Reilly’s Inside iPhone]

Clanging away on the debugging chapter

Whew, another long stretch of radio silence on the blog, this time corresponding with my writing the debugging/performance chapter for the iPhone SDK book. It ended up running about 32 pages(!), because of the scope:

  • Build errors
  • Using documentation in Xcode
  • IB errors
  • Xcode debugger
  • Performance tuning with Shark
  • Performance tuning with Instruments
  • Static analysis with Clang

The last is a bonus topic that’s a bit of a risk, considering that we’re basically telling the user to download a nightly build, drop it in their /usr/local and try it out on their code.

I had the good luck to run the Clang Static Analyzer scan-build while my app-signing certificates were messed up, which totally failed and forced me to confront the realities of how it works: scan-build actually runs your command-line build script (either make or xcodebuild) and integrates into the gcc tasks of the build to do its work. If xcodebuild doesn’t work with your project’s default settings, you fail with an inexplicable error like this:

2008-10-15 15:58:33.461 xcodebuild[747:2a73] Warning: Couldn't discover the 'ccc-analyzer' compiler's built-in search paths and preprocessor definitions for language dialect 'objective-c'. This may lead to indexing issues.
Compiler: /usr/local/checker-109/ccc-analyzer
Reason: gcc-4.0: installation problem, cannot exec '/Developer/usr/bin/arm-apple-darwin9-gcc-4.0.1': No such file or directory

This turns out to usually be a total red herring. The problem in my case was that app signing was broken (I’d generated my new cert on the MacBook, and hadn’t exported my private key from that computer back to the Mac Pro), so the build was failing for code signing reasons. Of course, this would also happen for any of our readers who download a sample project where the default target is the device, if they either don’t have a certificate or haven’t changed the signing identity to something other than “iPhone Developer: Chris Adamson”.

Since we don’t really care about code signing or the device when we’re doing static analysis, the fix is to just go to the project’s properties and set the “Base SDK” to “Simulator – iPhone OS 2.1” rather than “Device”.

In other news, I still haven’t heard if I got my registration submitted in time to go to the iPhone Tech Talks in Chicago. Daniel says he registered for it too, so maybe we’ll road trip (or take a train, though the Amtrak schedules from GRR and K-zoo don’t fit real well). Also, check out the unintentional hilarity from Apple’s tech talks registration form:

If yes, what other mobile platforms do you develop on? Check all that apply.
Android
BlackBerry
BREW
Symbian
Palm
Windows Mobile
Other

Anybody notice a prominent mobile platform missing from that list? Like the one that everyone actually has on their phone, no matter how cheesy the model? I’m surprised the Java mob hasn’t blogged bitterly about this obvious example of nefarious skullduggery! and how much “Steve Jobs hates Java”. Actually, I think it’s quaint that they remembered that BREW exists. Or that Palm still does.

Clang!

Hope this doesn’t count as an NDA violation, but I tried out the Clang Static Analyzer (as noted by Rogue Amoeba’s Quentin Carnicelli) on some of my iPhone projects and it does work, at least for finding simple stuff like Obj-C memory leaks. Very helpful, and potentially faster at finding the obvious bugs than the diagnostic approach of Instruments.