I’ll be revisiting XHTML 2 in about five years, assuming it survives that long.
World Wide Web Consortium (W3C), 2009:
XHTML 2 Working Group Expected to Stop Work End of 2009, W3C to Increase Resources on HTML 5; Today the Director announces that when the XHTML 2 Working Group charter expires as scheduled at the end of 2009, the charter will not be renewed. By doing so, and by increasing resources in the Working Group, W3C hopes to accelerate the progress of HTML 5 and clarify W3C’s position regarding the future of HTML.
On one hand, it survived for six years. On the other hand, it’s not like the people running it gave anyone a reason to revisit it.
This very post is delivered to your browser or news reader by the famous and fabulous WordPress blogging system. In my work as the developer of MarsEdit, I am exposed to countless blogging options, each with its own strengths and weaknesses. And yet, I stick with WordPress because it strikes a balance of power and ease of use which feels comfortable to me. Not to mention that Joseph Scott and others are tirelessly working to improve its API.
WordPress is licensed under the terms of the Gnu Public License (GPL) which, in a nutshell, stipulates that you are free to use the software however you like, but if you make changes and distribute those changes, then you must share those changes under the same terms. This simple, radical restriction means that you are prohibited from taking a GPL project and incorporating it with a closed-source project.
Violating the GPL is easy. All you have to do is write some code, intermingle it with some GPL code, distribute a changed copy of the original, and refuse to share your contributions. Bam! You’re toast. Assuming the original authors discover your violation and decide to pursue a resolution.
Once a violation occurs, it might be settled privately, or could escalate to legal court procedures. But the most obvious form of resolution is for the author of the changes to release their code to the public under the terms of the GPL.
Depending on how much code you “mixed” with the GPL code, this could mean only a small portion, or it could mean the entire source code of your project. This so-called “viral nature” of GPL is what scares the bejeezus out of companies, large and small, who fear the consequences of having to give up their own intellectual property to the public.
The terms of the GPL sound pretty simple at first read, but due in part to the epic consequences of a violation, there has been a great deal of debate and uncertainty about what legally constitutes a violation. Most of the debate seems to boil down to two questions:
If you can legally justify that your additions to a GPL project either don’t change or derive from the original product, or haven’t technically been distributed, then you are not subject to the restrictive terms of the license.
Take GIMP, the popular GNU-licensed image editing application. The application supports plugins, analogous to the types of plugins you might find for the commercial, closed-source application Photoshop. A savvy developer may argue that a plugin doesn’t meet the criteria of changing the original application, because the original application still runs in its unaltered condition whether the plugin is there or not.
But promoters of the GPL take the position that plugins, by nature of being loaded into the same code space as other GPL code, do constitute a modification of the original, and are therefore subject to the terms of the GPL. As far as I know this is not a question that has been well-tested in courts.
Let me take a moment to make this abundantly clear: I respect the rights of authors to license their software under whatever terms they choose, including the GPL. In my opinion, all the legal mumbo jumbo ceases to matter once the original author’s intentions are made clear. So if the author of GPL-licensed code clarifies to me that it cannot be run on Sundays, then their GPL means it cannot be run on Sundays. But this is one of the problems with the GPL: its terms are not often understood, even by the authors of GPL-licensed code.
WordPress supports two explicit forms of extension, each of which may affect the appearance and functionality of the product. Themes tend to work as a “skin” for the appearance of a blog, while plugins tend to introduce completely new features. Since plugins in WordPress are analogous to GIMP or Photoshop plugins, it would stand to reason that they would also be covered by the terms of the GPL. But what about themes?
Themes have been controversial in the WordPress community, as a few commercial business models have sprung up to take advantage of bloggers’ desires for high quality themes at an affordable price. One approach is to distribute “free” themes that contains commercial ads. So you might stumble upon the perfect theme for your blog, only to learn that the glaring “Brought to you by Hostess Cupcakes” line near the bottom of your page cannot be removed.
But the terms of the GPL, if themes are covered, would require that end users be granted the legal right to modify and redistribute their own copy of the theme. Zap the sponsorship, reupload to your site, and you’ve got a free, high quality theme with no ugly ads.
Today, Matt Mullenweg of the WordPress project announced his lawyer-supported opinion that themes are partly covered by the GPL:
I reached out to the Software Freedom Law Center, the world’s preeminent experts on the GPL, which spent time with WordPress’s code, community, and provided us with an official legal opinion. One sentence summary: PHP in WordPress themes must be GPL, artwork and CSS may be but are not required.
If you’re starting with the understanding that WordPress itself is GPL, and WordPress plugins are GPL, then it’s not so much extra hay on the camel’s back, to also clarify that its themes are to some extent GPL. But it got me thinking again about my own blog, and about the restrictions the GPL imposes on the kinds of things I can do with the software that runs it.
Now for the most controversial point of this article, where I suggest that the GPL does more to harm collaborative development than it does to help it.
For the purposes of this argument, let me reduce all the source code in the world down to three rough categories. I recognize I have omitted some classes of license here, but for the sake of argument, most projects fall into these camps:
Now, there are a few people in the world who, for political or philosophical reasons, will only participate in a GPL project. And for compariable yet opposite reasons, there are some who will only participate in commercial, closed-source projects. But I propose that the vast majority of developers will participate in any project that is advantageous to them.
So let’s imagine a representative, run-of-the-mill developer who is working on a project that falls into each of these three camps. If this developer is not radically committed to their own project’s license, they will naturally look to outside resources in order to bolster the success of their own work.
As the developer evaluates communities to participate in, they must evaluate the legal impact such participation will have on their own project. The closed source communities are, by definition uninviting to outsiders. GPL communities are open and embracing of other GPL developers, but generally off-putting to liberal-license and closed-license developers. Only the liberal-license communities are attractive to developers from all 3 camps.
I know what some of the GPL-enthusiasts are thinking now: “leeches don’t count as community.” Many GPL developers take comfort in the fact that their hard work can’t be quietly taken and incorporated into a commercial product, without any payback of time or money to the original project. But you’re piloting an open source project, and the first step of building a community is to get people in the door. Liberal licenses? Whoo-eee do they ever get people in the door.
If you operate from the presumption that great developers love to build great projects, the first step in any successful open source project is to get as many great developers in the door as possible.
Yes, this is just me and my crazy theories. I haven’t done exhaustive research to prove that liberal-license communities thrive more than GPL communities. But the anecdotal examples are staggering. The very foundation of Mac OS X, the operating system through which I’m typing, is thanks to the liberally-licensed FreeBSD operating system.
Looking over to the right of my screen, I’m watching this sentence appear in a live web preview as I type, thanks to the WebKit project, whose liberal license makes it compatible with closed source projects such as Safari, as well as open source efforts such as Google’s Chromium project.
For years, the problem of a generic HTTP client library that runs on every major platform has been addressed by libcurl, whose liberal license has caused it to be embraced by countless companies and projects.
The popular Subversion source control system’s liberal license enabled Sofa, a commercial software business to contribute value to the community with its extremely polished, award-winning client application. Meanwhile, the newly popular distributed source control systems presents three major choices: git, Mercurial, and Bazaar. All are restrcted by the GPL-license, and therefore none is likely to inspire development of a Versions-caliber client.
I’ve touched the tip of the iceberg, and yes I’ve neglected to mention some GPL success stories such as Linux, MySQL, and gcc. These communities have thrived to some extent because the passions of the GPL community are strong, but we can’t know whether their success is in spite of the restrictions their license places on participation by the broader developer community.
Speaking of GPL succeses, WordPress is itself an example of monumental success. All of its developers have something to be immensely proud of. But whenever I am reminded that WordPress is GPL, my passion for it takes a bit of a dive. I’m more comfortable with the true freedom of liberally-licensed products. If a liberally-licensed blog system of equal quality, ease of use, and popularity should appear, my loyalties to WordPress would not last long.
It’s your party, and you’re entitled to write the guest list. But take a look around the room: not as many folks as you’d hoped for? Liberally-licensed projects are booming. Speaking for myself, a developer who has been to all the parties, I’m much more likely to pass through the door that doesn’t read “GPL Only.”
If you have custom views configured in code, it can be time consuming to configure them for each instance and make them look right in context. To make the process smoother, you can create Interface Builder plugins and configure your objects in Interface Builder. While the Xcode documentation explains how to do this, it is long, thorough and confusing. Here is the simplified set of steps that I use to create Interface Builder plugins quickly.
Apple do have extensive documentation on Interface Builder Plugin creation. However, their documentation is thorough enough that may be overwhelming the first time you want to write an Interface Builder plugin.
I also find the workflow in Apple's IBPluginGuide very different to my typical workflow. I think this is because I normally just want an existing class to show up in Interface Builder so I can tweak one or two attributes — the processes for creating a redistributable library of components is more than I need.
So I was inspired to write this post: a shorter, workflow-optimised version of the same process described in the IBPluginGuide — using Interface Builder to configure a custom button.
Consider the button in the following window:
A big, drab, gray button. Maybe gray is an all right default but it doesn't really match this window. This post will make it easy to adjust this color in Interface Builder.
The large gray button here uses a custom button cell which draws the button using the Gloss Gradient from one of my earlier posts. The drawing code is:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
NSBezierPath *roundRectPath =
[NSBezierPath
bezierPathWithRoundedRect:NSInsetRect(cellFrame, 2, 2)
xRadius:5
yRadius:5];
NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
[graphicsContext saveGraphicsState];
[roundRectPath addClip];
DrawGlossGradient(
[graphicsContext graphicsPort],
self.buttonColor, // -- this determines the color of the button
cellFrame);
if ([self isHighlighted])
{
[[NSColor colorWithCalibratedRed:0.0 green:0.15 blue:0.35 alpha:0.5]
set];
[roundRectPath fill];
}
[graphicsContext restoreGraphicsState];
[[NSColor lightGrayColor] set];
[roundRectPath setLineWidth:2.0];
[roundRectPath stroke];
[self drawInteriorWithFrame:cellFrame inView:controlView];
}
This is all fine except that the color of the button is determined by the self.buttonColor property and if we have to choose this in code (continually changing the value, fixing, continuing, refreshing and repeating) it could get very time consuming.
A better solution would be to edit the button's color in Interface Builder. That way, we will be able to use color sliders to update the button drawn in the complete context of the window, in real-time.
My process normally starts by creating a custom view component. In this case, I have already created a class named CustomButtonCell in the project for my main application (AppWithButton). After creating the class, I have decided it would be a good idea to have an Interface Builder Plug-In.
A quick point about class requirements: every property we want to configure in Interface Builder must be encoded and decoded for the object using implementations of theNSCoderprotocol methodsinitWithCoder:andencodedWithCoder:overrides. Download the project linked below to see how this is done.
"Interface Builder Plug-In" is a project template in Xcode. In the New Project window, it's in the "Mac OS X -> Standard Apple Plug-ins" section.
An important point to consider is the name for the project — it should not be the same as the custom view component (in this case, CustomButtonCell) because the template will create a class with the name and it can't conflict with the existing view component.
I chose "ButtonPlugin" and saved the new project in the folder for my existing AppWithButton project.
The template creates a custom view in the ButtonPlugin project. I never use this (since I want to use my existing view). So I delete the ButtonPluginView.m, ButtonPluginView.h references and files.
I add my CustomButtonCell.m and CustomButtonCell.h files to the ButtonPlugin project but I don't copy or move the files — I leave the files at their current locations in AppWithButton project but drag them into the ButtonPlugin's Source Tree.
You need to check that the CustomButtonCell.m is added to the Compile Sources build phase of the ButtonPluginFramework Target and the CustomButtonCell.h is added to the Copy Headers build phase of the ButtonPluginFramework. Neither should appear in the ButtonPlugin target.
Then, select the "CustomButtonCell.h" file from the "Copy Headers" build phase and in the Detail View, change the "Role" from "Project" to "Public". This is a pretty obscure thing to do. I normally don't have the Detail View visible — if you don't know which view is the Detail View, it's time to learn because there is no other way to change this value in Xcode.
Change the Role of the "CustomButtonCell.h" to "public" in the Detail View
Rename the file ButtonPluginViewIntegration.m to CustomButtonCellIntegration.m and replace every occurrence of ButtonPluginView in this file to CustomButtonCell.
Make the following file changes:
ClassName to match the actual class name CustomButtonCell and change the SuperClass to be NSButtonCell (again, matching the actual super class for our custom button cell).com.mattgallagher.ButtonPlugin — this needs to be unique among Interface Builder plugins, so you pick an appropriate value each time.com.mattgallagher.ButtonPlugin value.Open the ButtonPluginLibrary.nib. This file contains the "Library Object Template" view (an instance of IBLibraryObjectTemplate). You can find it in the "Library Objects" view at the top level.
The Library Object Template will contain a "Template" and an "Example" square. The "Template" version is for NSView subclasses but our CustomButtonCell needs to be embedded in another object (an NSButton) so we will use the "Example" square — so delete the "Template" square.
If you click on the button in the "Example" square then click again, it will select the NSButtonCell inside the button (these clicks should be slower than a double-click, since a double-click will edit the text of the NSButton instead of selecting the NSButtonCell inside). With the NSButtonCell selected:
CustomButtonCell.NSButton to the right of the "Example" box in the same way.Select the Library Object Template (ButtonPluginLibrary.nib window -> Library Objects -> Library Object Template) and:
Back in the ButtonPlugin project again, open the ButtonPluginInspector.xib.
I deleted everything in the "Inspector View" window except 1 label and the NSColorWell. I moved these controls to the top of the view and then made the view 35 pixels high.
To make the color selector do something, I used bindings. Select the NSColorWell and:
inspectedObjectsController.selection.buttonColor.This binding needs one other change to work. Back in the ButtonPlugin project, open the CustomButtonCellIntegration.m file. Change the [[keyPaths objectForKey:IBAttributeKeyPaths... line to:
[[keyPaths objectForKey:IBAttributeKeyPaths]
addObjectsFromArray:[NSArray arrayWithObjects:@"buttonColor", nil]];
This tells Interface Builder to track and monitor the buttonColor property on the CustomButtonCell class. It does not actually perform the value changing (the binding we established will change the value directly) but it will make certain that this value will be tracked for undos and can be edited correctly.
At this point, you can run the ButtonPlugin project and it will launch Interface Builder with the plugin visible. The only problem is that the plugin won't be visible if you launch Interface Builder in any other way.
The following steps will make the ButtonPlugin project build the ButtonPlugin.framework and ButtonPlugin.ibplugin and install it in your Library.
Double click the ButtonPlugin project item in the ButtonPlugin project Source Tree to edit the project settings. Select the "Build" tab, then choose "Configuration: All Configurations". Then make the following changes:
Double-click the "ButtonPlugin" target in the Source Tree to edit that target's settings. Select the "Build" tab, then choose "Configuration: All Configurations". Then make the following change:
With these changes made, build and run the ButtonPlugin project and it will install the ButtonPlugin.framework in your ~/Library/Frameworks directory and load it in Interface Builder. From now until you remove the framework from your library, it will remain in Interface Builder and you can use it at any time.
I realize that this configures the app to install the "Debug" build in the user's library, over the top of the "Release" build (if the Release build is already built) but this is really just for local use, so I don't really care. If you do care, you might not want to apply these settings to all configurations. For me: this is faster to implement and easier to manage.
After setting up the Interface Builder plugin, instance of CustomButtonCell in your existing .xib and .nib files will not instantly update. You'll probably need to create new versions of the cell by dragging them out from the Library.
In this example, I have chosen to not link the original AppWithButton project with the ButtonPlugin.framework. This is because I don't want to distribute either the framework or the plugin — they are both for my purposes only.
If you wanted to link them, you could remove the CustomButtonCell.m and CustomButtonCell.h files from the original project and replace them with the ButtonPlugin.framework. According to the Xcode documentation, this arrangement would allow you to avoid installing the ButtonPlugin.framework in your ~/Library/Frameworks directory (Interface Builder would automatically find the plugin for any .nib or .xib file in the project). That's a nice idea but it has never worked for me, so I never bother.
Instead, I like to drag the ButtonPlugin.xcodeproj file into the AppWithButton Source Tree and edit the AppWithButton target, go to the "General" tab and add the ButtonPlugin.xcodeproj as a Direct Dependency.
The reason for this is that if I change the CustomButtonCell class and rebuild, it will automatically rebuild the Interface Builder plugin accordingly (you need to restart Interface Builder to see the changes).
The AppWithButton window in Interface Builder, adjusting the color of the button in real-time.
You can download the complete AppWithButton project zip file (including the ButtonPlugin project) (385kB).
It takes quite a few steps to set up an Interface Builder plugin. Fortunately, they're all simple, if a little menial.
Notice how little code was actually written though: bindings and Objective-C 2.0 properties make this whole process considerably easier — there is no actual code written to set the buttonColor (except in the NSCoder method implementations) since the bindings do it all for us.
Once you're practiced at making plugins in this way, the effort to create one may be justified, even just to tweak simple properties like this.
Do I know how to do this for Cocoa Touch classes for iPhone development? No. I imagine it's possible but you'd need to completely change the IBPlugin project from the template and I've never tried.
by Matt Gallagher (noreply@blogger.com) at July 02, 2009 05:47 PM
Yesterday, my new Unibody 15″ MacBook Pro arrived. Like last time, there are stats to be had, but they won’t give you the big picture. (I’ve placed them at the bottom.)
I ordered it with a 256 GB SSD, and I don’t think I’m ever going back.
I started fresh this time (migrating by hand) and the CPU is also a healthy 3.06 GHz Core 2 Duo, so a fresh system and a better CPU can obviously rig the results. However, there are plenty of findings to suggest that the gain is systemic and prevailing. Mail never loaded its main window so fast, and Mail needs to load a database. Many apps that for a few months with a fresh system would start in one dock icon bounce now start immediately, even before their first bounce has really taken off.
I’m also getting 8 GB RAM from OWC; partly to create a better environment for running Windows simultaneously, partly to stave off unnecessary swaps from the not-infinite SSD, but mostly because you can never have enough RAM.
Lastly, I am absolutely in love with the quality of this enclosure. The earlier MBPs will still beat many laptops, but they now seem like dinosaurs, made of damp cardboard and duct tape. The new MBP is solid, the rubber bezel around the very edge of the screen helps make the magnet latch snap the lid shut slightly less bewilderingly than for my old white MacBook, the speaker holes are small enough to impress (and avoid being filled by stray crumbs) and plenty enough to fill the Albert Hall, the glass trackpad is nice if equipped with a slightly too noisy click and the keyboard is the same I’ve loved since the MacBook. I still don’t like the black bezel around the display, and I don’t like the glossy display, and I still think it’s worth it because this is such a solid computer in every possible sense of the word.
| MacBook Pro Early 2008 (discrete enclosure) | MacBook Pro Mid-2009 (unibody enclosure) |
|---|---|
| 2.4 GHz Core 2 Duo (64-bit) | 3.06 GHz Core 2 Duo (64-bit) |
| 3 MB L2 cache | 6 MB L2 cache |
| 800 MHz FSB | 1066 MHz FSB |
| 4 GB RAM (OWC) | 4 GB RAM Waiting on 8 GB (OWC) |
| 250 GB HDD (5400 rpm) | 256 GB SDD |
| 8x SuperDrive | 8x SuperDrive |
| NVIDIA GeForce 8600M GT (256 MB GDDR3 RAM) | NVIDIA GeForce 9400M (siphoning 256 MB RAM) —and— NVIDIA GeForce 9600M GT (512 MB GDDR3 RAM) |
| 15.4″ anti-glare display 1440 x 900 | 15.4″ glossy (reluctantly) display 1440 x 900 |
| iSight | iSight |
| FireWire 400 + 800 | FireWire 800 |
| 2 x USB 2.0 | 2 x USB 2.0 |
| Optical digital/analog audio in/out | Optical digital/analog audio in/out |
| Gigabit Ethernet | Gigabit Ethernet |
| 802.11n Wi-Fi | 802.11n Wi-Fi |
| Bluetooth 2.1 + EDR | Bluetooth 2.1 + EDR |
| DVI + DVI-VGA adapter | Mini DisplayPort |
| ExpressCard/34 slot | SD Card slot |
| Kensington lock slot | Kensington lock slot |
| Trackpad with multi-touch Up to 3 fingers supported by 10.5 | Glass trackpad with multi-touch Up to 4 fingers supported by 10.5 |
| Backlit Aluminium keyboard | Backlit roundrect-style Keyboard |
| 300-cycle battery Up to 5 hours (advertised) | 1,000-cycle battery Up to 7 hours (advertised) |
| 2.45 kg | 2.49 kg |
Book Reports
Bring along the book you're currently reading, or one of your old favorites. Hopefully the book would have some relevance to programming, but we're fairly open-minded. Just give us an overview the book's topic, and what you liked (or disliked) about it.
Topics
Victoria Wang will give an introduction to programming 2D animations and web-based games. She will focus on both the industry standard, ActionScript for Adobe Flash, as well as the hot open source alternative, Processing and its counterpart, Processing.js. Join us for some fun code and shiny demos!
Dinner
Please Bring Your Own Beverage. And food.
Just finished up the Map Kit screencast.
tell application "Colloquy" to activate -- this delay lets me take my fingers off the hotkey delay 0.2 -- hide all apps, from within Colloquy tell application "System Events" to keystroke "h" using command down & option down -- let the apps hide delay 0.1 tell application "System Events" to set visible of process "iChat" to true tell application "System Events" to set visible of process "Colloquy" to true -- There's probably a better way to do all this, but I haven't found it yet.
tell application "Finder"
activate
set visible of every process whose name is not "Finder" to false
end tellFor C4[3] (September 25-27 2009 in Chicago, registration opening “soon”) I'm introducing a new feature: Blitz Talks.
The idea is simple: micro-presentations just prior to the normal, full length ones.
Blatantly ripped off from O'Reilly’s Ignite, the Blitz Talk format is constrained and predefined: you get 20 slides. Each slide is displayed for 15 seconds before I advance to the next slide for you. That adds up to five minutes, total.
A lot of really interesting topics just don’t need the full 45-minute treatment, and I think Blitz Talks are a great way to pack even more content into C4.
Another facet to the Blitz Talks are how they're selected. Normally I ask specific people to talk about specific things at C4. With Blitz Talks, that’s turned around.
Instead, I invite you to submit your proposed 20 C4[3] slides to me by Monday, July 13th 2009. Please send me a private URL to your Keynote file, PowerPoint file or 280 Slides presentation, with what you're going to be saying stashed in the speaker notes.
I'll select about 10 talks. Selected speakers get early access to registration, ahead of when I open general registration. (For now, I've abandoned the C4 attendee lottery idea and am going with this instead — my intent is to “reward” folks passionate enough to contribute to C4).
For your actual presentation at C4, Victoria and I made an app that will display your slides along with an on-screen timer: Blitz.app. The elapsed-time indicator takes up the lower-right corner of your slides, so be sure to test your slides against it.
For now Blitz.app only accepts PDF files, so eventually your slides will have to make it into that format. I'm looking into reading Keynote 5 files directly, but I don’t know if that feature will be ready in time for C4[3]. Patches welcome.
I managed to completely miss Jeff Atwood’s (pent-up) love letter towards the iPhone product, platform and experience. He argues, and he’s right, that the platform is good enough to kick ass, and that the hardware is starting to catch up with the promise of the software. (We won’t get there until we get better multi-tasking, but that requires yet more hardware; more resources, more battery and quicker computation. I’m guessing next year’s model will take some decisive steps in the right direction. Conceptually, the 3G S is a mere patch.)
Two years ago, I’d just tried the original iPhone myself:
The iPhone is not perfect. A first for any phone this side of, oh, 2003?, it doesn’t have MMS at all, so you can’t send images or audio to other cell phones without being convinced the other cell phones have set up email clients. As we all have heard, 3G would really be better for data transfer speed. And about four to five of the bundled applications are crap – the specific group varies depending on who you ask, but it generally includes the Notes app. And what kind of PDA has a YouTube client but not copying and pasting, or the ability to view Excel Office Open XML documents but not an accessible file system?
I am telling you today to shut up for a few seconds and stay for the ride. It is pretty much impossible to make the kind of conclusions I make about the iPhone’s UI and its role in making this sort of thing available for the first time to ordinary people without also making the comparison to the original Macintosh. It was far from perfect when it came out, which led a lot of people to discredit it. Within three years almost all of the “but it doesn’t have/do X” concerns were gone, and within ten years it had swayed every other competitor. I believe something similar is going to happen. It’s not a coincidence that the iPhone owners are – AT&T handcuffs aside – generally satisfied.
Except for the “accessible file system”, which was a bullshit substitution for “some sort of shared-ish general storage system”, every one of those things have been fixed. (I didn’t mention conventional Bluetooth file transfers, which is just about the only thing left to add to the platform itself.) They’re even slowly wiggling out of the AT&T handcuffs.
Two years. What’s happened in two years for everyone else? Some other companies have gotten around to providing an iPhone-like — and by that I mean usable — experience, but most still think that this will blow over. There’s a time and a place for simple phones with simple keypads and simple functionality, but they haven’t even really gotten that right. (That might sound a bit ridiculous since some of these companies have been making mobile phones for 40 years now. My point is that if you want a simple phone, you have to get a cheap phone, and if you want a good simple phone, you have to get an expensive phone and avoid the rest of it.)
I think it’s time to settle once and for all that ordinary phones (non-smartphones) are like DOS. They are a sequence of drilling down into textual menus, sometimes represented by 12 colorful icons or with four or five tabs, but it is still menus. It is perfectly serviceable for some tasks, and it’s not a broken approach, but for most tasks, we can do better. Dialing numbers or picking people to dial from a list by typing is maybe the only task these interfaces do graciously.
Once in this narrative, it just keeps coming. The Palm Pilot was the Xerox Alto. Ahead of its time, defining some of the core concepts of what a mobile GUI could look and feel like. Windows Mobile was… well, Windows. No one loved Windows until Windows 3.11, and that took eight years. The first undeniably good Windows was still wretchedly dual: Windows 95 was stuck between 16-bit and 32-bit, famously unable to be taken seriously as an OS by some people because of some DOS gunk at the bottom of the stack. Now everyone’s trying to patch Windows Mobile out of suckiness with their own abstraction layer on top, and any month now it might even start working.
Not everything’s a perfect fit to this mental model, but you have to agree that there are similarities. The two biggest changes are that Apple made a late entry and snagged substantial market share (like their MP3 player entry with the iPod) and that Palm came back with something that theoretically could beat everyone if well executed.
Jeff makes an interesting prediction: “I believe the iPhone will ultimately be judged a more important product than the original Apple Macintosh.” I agree with that and I hope it’s obvious why; the original Mac was a platonic product that eventually saw success with some professionals and developed at the pace of the industry, and the original iPhone was a platonic product that immediately went for market share and developed aggressively. I still get jeers for owning an iPhone, but there are fewer and fewer with any real substance to them because most of the gaps are being filled. Most of them are centered around the premise that I should own a Windows smartphone just because it’s Windows, which seems exactly like the kind of argument these people like to stick to owners of Apple products in general.
Since this is an iPhone post, I have a quota to meet around a certain subject (bear with me). The real shocker in Jeff’s post isn’t that the iPhone OS is less messy, or that having many apps is good, it’s that the oligarchy of the mobile phone market neutralized Apple’s flaws. Jeff is, like me, pacified with what the phone offers him, but he shouldn’t be happy with why that is. The carriers use pricing models fit for Kafka. They collaborate with mobile phone vendors to lock you into their platform under both their and the carrier’s control scheme. That this balance is apparently shifted towards the mobile phone end seems like a good development, until you realize that the carriers are working to reverse this and that you’re screwed either way.
Jeff ostensibly cares about software freedom in that he calls Macs expensive Mac OS X dongles. Mac OS X is infinitely more open than the iPhone OS in that while they don’t want you to install any of them on an arbitrary device, you can develop and use whatever you want for Mac OS X. Is his excuse seriously that the mobile phone market is already so far down the crapper that he doesn’t have to change his position?
The confusingly named “Worms 2: Armageddon” (Worms 2 and Worms Armageddon were earlier, consecutive games at the series’ prime) was sneakily announced for Mac OS X in the description of its Xbox Live Arcade trailer. (Fire (again) and vertical levels? Neat.)
As someone who spent time trying to get one or the other variation of the great 2D Worms running under VMWare Fusion, I can appreciate that.
Today’s short AppleInsider report, speculating on iMac price drops:
Roughly six week later at its annual developers conference, the Cupertino-based company announced price cuts of between $100 – $300 on its 13- to 17-inch unibody aluminum notebooks, all of which are now dubbed MacBook Pros.
Let’s see:
Every AppleInsider report must refer to Apple as “Apple”, “the [Mac, iPhone or iPod] maker” and “the Cupertino-based company“. One assumes that this is a habit acquired from fine journalism. AppleInsider might actually be high-end, and periodically unusually correct, for a rumors site. My guess is that they’re trying to fecundate this impression by making every article read as if the New York Times took the time to cover just this one story from the otherwise fetid heap. That includes using varying appellations, which in the New York Times helps add context, but in AppleInsider looks formulaic, which it is, and ridiculous, which it gets.
“[Price] cuts of between $100 – $300″. Bzzt. “[Price] cuts of between $100 and $300″, or “price cuts of $100 — $300″. (That’s also an em dash in my correction; paging Dr. Gruber…) If you use “between”, also using a range makes it tautological.
This one sentence references elapsed time (since a previous AppleInsider report), the venue of an announcement (WWDC), the location of the company (by appellation), the announcement itself (price cuts, the size range of the price cuts, the renaming of the MacBook) and the range of the affected products. It’s dense with information, but how much is actually relevantly connected, and how much is just newspaper-y looking text strung together? Simpler and therefore less copy is better, but this could have been extended to two or three sentences for clarity, and some detail could have been folded into a link; say, to AppleInsider’s copious coverage of the announcement. They are selling ads, after all.
But wait, there’s more!
iMacs were just recently refreshed in March but will see another update by fall, at which time they’ll also be repositioned as more affordable offerings. Apple is reportedly mulling similar 7% – 10% price reductions alongside the introduction of those models, people familiar with the company’s thinking say.
If the relative price reduction is important, and you just listed the range of absolute price reductions on the MacBook Pro series, why not compare the putative relative price reduction (for the iMac) with the actual relative price reduction (for the MacBook Pro)? The two are different creatures, but there are obvious comparisons to be drawn and ways to analyze it.
Off the cuff: If the MacBook Pro is a high-end product with, likely, higher margins and they slashed the price by way less than 7%, that’d invalidate the theory (or make the move more “daring”); if they slashed the price by way more than 7%, that indicates that lowering the margin to that extent is something they’re willing to do.
Even without the big drop in price, you report this much as fact: “[the iMac line] will see another update by fall, at which time they’ll also be repositioned as more affordable offerings”. So they will seem more affordable. But if they won’t drop it by quite as much as 7% — 10% (that part’s reported as conjecture), what other lower drop could they pick? That’s not explored, even in passing, and this is supposedly the fact end of the stick.
(How’s my driving? More or less of this sort of thing?)
I still think “I Want You Back” is Michael Jackson’s biggest triumph. I never really liked his style as an adult (to be honest, I like his sister Janet’s songs better), but it takes a lot for someone as young as Michael — and the rest of the Jacksons — to pull this off at this age. I hope it had less of a role in later turning him into a weird Peter Pan-ish guy than everyone and their psychiatrist friends seem to believe.
Yeah, it’s cute. But that doesn’t mean it’s not a wonderful musical composition, executed with passion and in style, that could give the better part of the iTunes Top 100 (including today’s) a handsome run for its money.
I’ve seen a good bit of sample code that shows how to implement using a UINavigationController in a view controller that is managed by a UITabBarController, but I haven’t seen much on how to do it with Interface Builder. Turns out that it’s pretty simple and I’m going to show you how.
To get started, create a new project in Xcode. Select the Tab Bar Application template and click Choose….
Name the project, TabBarNavigator and click Save
In Xcode, expand the Resources group in the Groups and Files view and double-click MainWindow.xib to open the xib in Interface Builder
When Interface builder opens, change the xib View Mode to the tree view mode and expand your Tab Bar Controller item.
Make sure that the Tab View Controller object is selected. In the object inspector switch to the Attributes tab and change the Class for the First controller from View Controller to Navigation Controller.
You will notice back in the MainWindow.xib window that the view controller for First View is now a UINavigationController. Expand the UINavigationController item now.

Now select the View Controller that is a child of our new UINavigationController. Click on the Identity tab of the object inspector and change the Class to FirstViewController.

Click on the Navigation Item that is a child of the First View Controller and change the Title in the Attributes tab of the object inspector to “First”.

Save your changes in Interface Builder, switch back to Xcode and Build & Go. You should see something like the following in your First view tab.

Notice that there is a Toolbar visible at the bottom of the first view. If you would like to hide this, go back into Interface Builder and select the Navigation Controller In the object inspector, uncheck the Shows Toolbar checkbox.

Before we create a new view controller, let’s set up our FirstViewController to create an event that will trigger pushing the new controller. Since our FirstView was not created with a xib, let’s create one and then add actions and outlets to it.
In Xcode, right-click or ctrl-click the Resources group and select Add | New File…. In the ensuing dialog, select View XIB in the User Interface section of iPhone OS templates and click Next. Name the XIB FirstView and click Finish.
Double-click the new XIB to open it in Interface Builder and select the Identity tab after you have made sure that File’s Owner is selected. Change the Class field to FirstViewController.
Ctrl-click and drag a connection from the File’s Owner object to the View object in the main window for our XIB and select view in the ensuing pop up menu.
From the Object Library drag and drop a button onto the view. Double-click the button and give it the title “Push”. Save your changes in Interface Builder and switch back to Xcode.
Open the file FirstViewController.h and add an action so that the header looks like this:
1 2 3 4 5 6 7 8 9 | #import <UIKit/UIKit.h> @interface FirstViewController : UIViewController { } - (IBAction)push:(id)sender; @end |
Open the file FirstViewController.m and implement the action so that the code looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #import "FirstViewController.h" @implementation FirstViewController - (IBAction)push:(id)sender { } - (void)dealloc { [super dealloc]; } @end |
Note: You can safely delete all of the view controller template code which I have done in this example. Don’t worry. You won’t hurt anything.
We will come back to the push implementation after we have created our new view controller that we are going to push.
We are now going to create the new view controller that will be displayed when we tap our “Push” button in the FirstViewController.
In Xcode, right-click or ctrl-click the Classes group and select Add | New File…. In the ensuing dialog, select UIViewController subclass in the Cocoa Touch Class category of the iPhone OS templates. Make sure With XIB for user interface is checked and click Next. Name the file NewViewController.m and click Finish.
For organization purposes, move the newly created NewViewController.xib file into the Resources group. Then double-click it to open it in Interface Builder.
In Interface Builder, drag a UILabel onto the view and give it the title “New View Controller”. Save the file and switch back to Xcode.
Open the file FirstViewController.m again and implement controller push code so that it looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #import "FirstViewController.h" #import "NewViewController.h" @implementation FirstViewController - (IBAction)push:(id)sender { NewViewController *controller = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil]; [[self navigationController] pushViewController:controller animated:YES]; [controller release], controller = nil; } - (void)dealloc { [super dealloc]; } @end |
Don’t forget to #import “NewViewController.h”
To connect our button to the push action, double-click the FirstView.xib file in the Resources group to open it in Interface Builder.
Ctrl-click and drag a connection from the “Push” button in our view to the File’s Owner object and select push: in the ensuing pop up menu. Save your changes in Interface Builder. Switch back to Xcode and Build & Go.
Additionally, you can give your New View Controller a title by implementing -viewDidLoad. Just open your NewViewController.m file and uncomment the -viewDidLoad code making it look like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #import "NewViewController.h" @implementation NewViewController - (void)viewDidLoad { [self setTitle:@"New View"]; [super viewDidLoad]; } - (void)dealloc { [super dealloc]; } @end |
And that’s pretty much all there is to it. There are a lot of steps here, but I think you’ll see that setting up your application to implement both a UITabBarController and a UINavigationController is pretty straight forward.
How best to organize your views in an iPhone application is a design process. Often applications will have a lot of data for your users to access and it will be up to you to find the best way to organize it. Being able to utilize both a tab bar and a navigation controller can really help in this process. Until next time.
You heard me: stoned wallaby crop circles.
“The one interesting bit that I found recently in one of my briefs on the poppy industry was that we have a problem with wallabies entering poppy fields, getting as high as a kite and going around in circles,” Lara Giddings told the hearing. “Then they crash,” she added. “We see crop circles in the poppy industry from wallabies that are high.”
Reality. It’s a wonderful thing.
In an imaginary parallel universe where everything is the same but the Pirate Party doesn’t exist, I’d still be slightly optimistic about how the entire piracy vs private industry abuse fight has been going lately.
Three, or maybe four or five of you, may not have already heard how Jammie Thomas-Rasset was sentenced to pay $1.92 million for having shared, and thus infringed on the copyrights on, 24 songs. This sum — $80,000 per song — is so unsubstantiatedly princely that even one of the artists involved in the evidence who happens to agree with the verdict on principle publicly shames the claim and distances himself from these shenanigans.
The purpose of the ridiculous sums are two-fold:
a) To set an example and coerce people out of widespread behavior
and, to a lesser extent,
b) To recuperate some of the losses from an alleged statistically reliable, obvious and therefore missed conversion from “illicit download” to “karma friendly purchase”.
Both branches are demonstrably wrong. Let’s take a), the technical progress that enabled the behavior to appear can’t be rolled back, and people aren’t scared of being sued; in the extreme cases where they are, they are also either forgetful or resourceful. The funny business will continue no matter what happens.
As for b), this subdivides further. The conversion may look obvious on paper given that the business model is established, but there are several possibilities. We all know by now that there’s no guarantee that any download would have resulted in a purchase or for that matter even precluded a purchase. There’s no model to calculate an accurate number, so one has to be pulled from… somewhere.
And we also know that for the purported losses, which can’t be proven to not be imaginary, to be recuperated, if the given amounts are correct, they’d have to end up with way more money. The road towards that goal forks; either you search higher damages (which seems unlikely, given the unrest in everyone involved, including the judge, in accepting the sum as ‘fair’ already) or you file more cases, which to approach the sufficient volume would hold up courts that could have been righting more egregious wrongs, and which is practically hard since bringing each case involves effort and attorneys.
There may also be a slight pulling sensation in your public perception.
Let’s say even that the music industry is right: the claims are substantiated, and the sums, in the face of the sheer heinousness of the crime, are actually fair. Now what are they going to do? They’ve still got practicality, public opinion and increasingly their own pool of artists against them.
And artists aren’t fools. Some, maybe even most (it’s hardly a homogenous group), may feel threatened by the direction reality is heading. But many are taking precautions, and many more still are actually being made tenable by the recent development. Did you know that when you buy a cassette tape or a CD-R in Sweden, a small portion of the price goes, ear-marked, to a special music industry organization for composers? Did you know that, for anyone to cash in on that, and by way of being a large organization named in a prominent law, in effect calling yourself a composer in the first place because you’ll have trouble and be at a competitive disadvantage selling anything otherwise, you’d have to join that organization and accept their terms? And did you know that up until very recently, that blocked you from placing your works under Creative Commons-like licenses in order to explicitly allow things like remixes and sampling which not only has the potential of spreading your work and giving you more consumers and customers, but actually on a macro scale, thanks to the way the human mind works, is what creation is about in the first place?
I’m thinking about how this will play out into the future and it occurs to me that the wins are pyrrhic and elusive. The industry actually hurts less when they’re awarded losses. A reasonable win is too hard-fought and brings in too little money; other wins just turn your putative customers into new plaintiffs because they don’t want to deal with cold and bloodthirsty. The artists themselves are already headed en masse for the ejector chair and the good ship Independent.
At some point, either politicians are going to have to be lured into penning the industry more and more odious laws (which, per previous paragraphs, won’t actually solve anything but will happily trash the system while waiting, and per the very first paragraph and related efforts will only actually work for so long), or the matter will have to be dropped. Lumps taken. Sour medicine tasted. Lesson learned. Democracy restored. Lemonade, finally, made.
This is the first in a short series on my adventures getting my software out the door. Rather than this first lesson be a lesson in what to do, here’s what not to do.
Don’t order a DSL upgrade 7 days before your ship date.
Yes, indeed. Last Wednesday morning, Qwest “upgraded” our DSL to 7Mbps. From then until Saturday lunchtime, I had 13 hours of solid connectivity, most of which I was asleep for. It did indeed get resolved and I am enjoying the new speed but it certainly wasn’t worth stress of trying to find connectivity while trying to ship. I went to Barnes and Noble, Krispy Kreme and the seminary library in search of the uplink. I also wasted a trip to Best Buy to get a new DSL modem which didn’t solve the issue, despite the Qwest tech’s assurances that it would. (That reminds me, I need to return it and get my $75 back.)
Thinking about it, my rule should be generalized as:
Don’t replace/upgrade/otherwise mess with any of your critical infrastructure within two weeks of your ship date unless it’s broke.
After being burned by the DSL outage, I left well alone newly released updates to XCode, PHP, Safari and Java. I’ll get to those at a less critical moment.
Well, lesson learned. I won’t be doing that again. Now if you’ll excuse me, I’m gonna download a movie from Netflix.
To celebrate the official release of iPhone OS 3.0 this week, I will show you how to verify that an NSString contains a syntactically valid email address using NSPredicate — a class that joins the iPhone SDK 3.0 as part of the Core Data additions. This code will work on Mac OS X too since, as with the rest of Core Data, NSPredicate has been part of Mac OS X since 10.4 (Tiger).
I gave an interview to Anthony Agius of MacTalk.com.au this week. You can download the MP3 from their website. I'm hesitant to listen to my own voice but I think I talked about what it's like to be an independent Mac/iPhone developer in Melbourne, Australia.
In programming, a predicate is a condition that returns true or false if the object it processes has the properties that the predicate describes. The key difference between a predicate and a regular boolean expression is that a predicate only considers the properties of one object, where a boolean expression may consider multiple, unrelated objects.
Many programmers are familiar with predicates as used in SQL database queries. For example a query to extract the complete row from the "people" database table for every person named "John Smith" might look like this in SQL:
SELECT * FROM people WHERE firstname = 'John' AND lastname = 'Smith'
Everything after the "WHERE" is the predicate — it looks at properties of the row only and is either true (the row will be extracted) or false (the row will be ignored).
In Cocoa, NSPredicate works in much the same way as the "WHERE" clause of SQL. The main reason that NSPredicate is being brought to the iPhone is that NSPredicate fulfils the same role in Core Data that "WHERE" clauses fulfil in SQL — to allow the persistent store to fetch objects that satisfy specific criteria.
Imagine we had an NSDictionary created using the following method:
- (NSDictionary *)personRowWithFirstname:(NSString *)aFirstname
lastname:(NSString *)aLastname
{
return
[NSDictionary dictionaryWithObjectsAndKeys:
aFirstname, @"firstname",
aLastname, @"lastname",
nil];
}
we could test if a given row created by this method matched the predicate "firstname = 'John' AND lastname = 'Smith'" with the following:
// given an NSDictionary created used the above method named "row"...
NSPredicate *johnSmithPredicate =
[NSPredicate predicateWithFormat:@"firstname = 'John' AND lastname = 'Smith'"];
BOOL rowMatchesPredicate = [johnSmithPredicate evaluateWithObject:row];
The string format used to construct an NSPredicate in Cocoa is very similar to the syntax of the "WHERE" clause in SQL. You can also construct this NSPredicate in code by building it from two NSComparisonPredicates and an NSCompoundPredicate.
A more common use of NSPredicate is filtering — extracting rows that match an NSPredicate from a larger collection:
// given an NSArray of rows named "rows" and the above "johnSmithPredicate"... NSArray *rowsMatchingPredicate = [rows filteredArrayUsingPredicate:johnSmithPredicate];
This is then more like an SQL query where we have selected matching rows from the larger table of data.
Note:NSPredicatehandles filtering only. If you'd like to replicate SQL's "ORDER BY" clause, you can applyNSSortDescriptoras a separate step.
The "LIKE" comparison operator in NSPredicate (NSLikePredicateOperatorType) is commonly used as a convenient means of testing if an NSString matches a Regular Expression. It's advantage over full libraries with greater options and replacement capability is that it is already in Cocoa — no libraries, no linkage, no hassle.
To test if an NSString matches a regular expression, we can use the following code:
NSPredicate *regExPredicate =
[NSPredicate predicateWithFormat:@"SELF MATCHES %@", regularExpressionString];
BOOL myStringMatchesRegEx = [regExPredicate evaluateWithObject:myString];
The only question that remains is: what is a regular expression that can be used to verify that an NSString contains a syntactically valid email address?
NSString *emailRegEx =
@"(?:[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[a-z0-9!#$%\\&'*+/=?\\^_`{|}"
@"~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\"
@"x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-"
@"z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5"
@"]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-"
@"9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21"
@"-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])";
This regular expression is adapted from a version at regular-expressions.info and is a complete verification of RFC 2822.
This adaptation involved escaping all backslashes with another backslash (otherwise the NSString will try to interpret them before they are used in the Regular Expression) and escaping the ampersands (&) so it isn't interpreted as a Unicode escape sequence and caret (^) characters because — actually I have no idea why except that the expression wouldn't parse without it.
The linked regular-expressions.info page recommends using slightly different regular expressions that force the top-level domain to be a country code or a known top-level domain. With the number of top-level domains due to increase in the near future, I'm not sure this is a good constraint to impose — since this check isn't intended to verify that the provided domain name is valid.
Since this regular expression NSString is so long, I've split it over 7 lines. This is an underused feature in Standard C languages — if you split a string into pieces but put nothing except whitespace between the pieces, the compiler will treat it as one continuous string. You don't need to write an extremely long string on a single long line.
Just a few lines of code this week but I thought it would be good to draw attention to one of the minor additions making its way to the iPhone in SDK 3.0. I use NSPredicate all the time on Mac OS to perform searches, extract subarrays and perform quick Regular Expression tests. Without it, the only alternatives on the iPhone were methodical array iterations and manual comparisons in code.
by Matt Gallagher (noreply@blogger.com) at June 23, 2009 06:35 AM
FastScripts 2.4 is out, with an important change in the evaluation terms.
Use FastScripts for free, for as long as you like. All features are enabled and you may define up to 10 keyboard shortcuts. If you decide you want unlimited keyboard shortcuts, purchase a license to remove that limitation.
I have been thinking for some time of eliminating FastScripts Lite. Customers found it confusing to differentiate between the versions, and I found it tedious to artificially maintain two versions. With the new, liberal evaluation terms in FastScripts 2.4, all of the old Lite functionality and much more is now included for free in the full version.
This new version also includes a software update mechanism so you can be sure to stay up to date with new releases as I update the application. So whether you’re an existing paid user, or a Lite user, be sure to download this release!
If you’ve been thinking of giving FastScripts a try, now is a great time to give it a spin. I hope you will enjoy what it has to offer!
C++ was designed to ensure you "only pay for what you use," and at that it is mostly successful. But it’s not entirely successful, and I thought it might be interesting to explore some counterexamples: C++ features you don’t use but still negatively impact performance, and ways in which other languages avoid those issues.
So here’s the first episode of I Didn’t Order That, So Why Is It On My Bill, to be continued until I can’t think of any more.
Inline Functions
The C++ standard says this:
A static local variable in an extern inline function always refers to the same object.
In other words, static variables in inline functions work just like static variables in any function. That’s reasonable, because that’s probably what you want. That’s what statics are for, after all.
But wait, it also says this:
An inline function with external linkage shall have the same address in all translation units.
That’s borderline silly. Who cares what the address of the function is? When’s the last time you used the function pointer address for anything except calling it? Dollars to donuts says never. You aren’t using a function pointer as a hash table key. You aren’t comparing the same function via pointers from different files. And if you did, you’d probably have the good sense to ensure the functions are defined exactly once (i.e. not inline). You can easily live without this feature.
So hopefully I’ve established that you don’t use this feature. Now I have to show how you’re paying for it anyways. Well, this feature complicates linking. Ordinarily, there should be only one copy of a function, and if the linker finds more than one it gives you a multiple definition error. But with inline functions, the compiler is expected to generates multiple copies of the code and it’s up to the linker to sort it out. See vague linkage.
So the linker has more work to do, and link time is increased. Again, who cares? Linking occurs after compilation, and I promised you a runtime cost. But ah – link time is runtime when we’re using dynamic libraries!
Every time you start up a program, the linker has to make sure that your implementation of vector<int>::capacity() has the same address as the vector<int>::capacity() defined in libWhoGivesAHoot.dylib just in case you decide to go and compare their function pointers.
And it gets a little worse. You know how class templates have to live in header files? The function definition goes right there in the class declaration, and that makes them inline automatically. So nearly every function in a template gets this inline uniquing treatment. Every template function in a class that cannot or should not be inlined – because its address is taken, because it is recursive, because you’re optimizing for size, or simply because it’s too darn big and inlining is counterproductive – will incur a launch time cost.
The situation is dire enough that gcc added a "-fvisibility-inlines-hidden" flag. This is a minor violation of the C++ standard, but will improve launch time in cases with a lot of dynamic libraries. In other words, this flag says: I’m not using this feature, please take it off my bill.
C does not have this problem. Why not? C (in the C99 standard) has very different semantics for inline functions. To oversimplify a bit, a C99 inline function acts as sort of an alias for a real function, which must be defined once and only once. Furthermore, inline functions can’t have static variables, or have their addresses taken. This approach is more limiting, but doesn’t require the linker to bend over backwards, and so avoids the performance penalty of the C++ approach.
Following this week’s atrocious sell-out of freedoms and human rights in Germany under the pretense of “thinking of the children”, Social Democrat and Bundestag representative Jörg Tauss has resigned from the party in protest. He will now join Piratenpartei, the German Pirate Party, and act as the first Pirate Party national parliament representative ever, although not as elected from that party.
In the interests of covering every angle, there’s more that seems relevant to the story. One of the traditional crutches to sell the populace on the importance of censorship has been to prevent the spread of child pornography. Indeed, this week’s law was largely pushed by the motive that censorship will prevent the spread of child pornography, even if such a benefit would come at the cost of fundamental freedoms and be completely foreign to an open, modern democracy. However, Wikipedia’s Jörg Tauss article reveals some interesting prior entanglement.
I haven’t even heard of Jörg Tauss before today, so I’m not going to guess one way or the other. Chances are that Herr Tauss is opposing the censorship law for all the right reasons, just like the majority of the German electorate are; it’s in fact very likely. But given the background, a distant possibility becomes stronger: I can’t rule out that Tauss is guilty of the charges and is trying to save his own bacon by adopting a sensible position for his own gain. In that case, it’s regrettable, and a sad entry in the row of historic “firsts” that Pirate Parties have been stacking up lately. I know nothing, however, but I don’t want to be coy and selective in my reporting.
And that’s all I’m going to say about that for now.
Given the attention it received and the number of bugs I know it contained, I wanted to revisit an old post of mine: Streaming and playing an MP3 stream. In this post, I'll talk about the problems the original contained, how I fixed those problems and I'll present the updated result.
Last September, I wrote a post titled "Streaming and playing an MP3 stream". The post was largely an experiment — I just wanted to see if I could play a streaming MP3 by quickly adapting Apple's AudioFileStreamExample to accept an HTTP data stream.
Unexpectedly, the post became one of my most popular. The attention quickly revealed the limitations in my approach:
I've finally decided to take the time to present a solution to these issues and present an approach which is a little more robust and a little easier to extend if needed.
You can download the complete AudioStreamer project as a zip file (around 110kB) which contains Xcode projects for both iPhone and Mac OS. You can also browse the source code repository.
One point should be clarified before I continue: this class is intended for streaming audio. By streaming, I don't simply mean "an audio file transferred over HTTP". Instead, I mean a continuous HTTP source without an end that continues indefinitely (like a radio station, not a single song).
Yes, this class will handle fixed-length files transferred over HTTP but it is not ideal for the task.
This class does not handle:
These things often can't be done on streaming data, so this class doesn't try. See the "Adding other functionality" section for hints about how the class could be reorganised to handle some of these features.
Since I had borrowed the AudioFileStream and AudioQueue callback functions from Apple's example, they were Standard C.
My first change was to make these 6 callback functions (7 including the CFReadStream callback) little more than wrappers around Objective-C methods:
void MyPacketsProc(
void *inClientData,
UInt32 inNumberBytes,
UInt32 inNumberPackets,
const void *inInputData,
AudioStreamPacketDescription *inPacketDescriptions)
{
// this is called by audio file stream when it finds packets of audio
AudioStreamer* streamer = (AudioStreamer *)inClientData;
[streamer
handleAudioPackets:inInputData
numberBytes:inNumberBytes
numberPackets:inNumberPackets
packetDescriptions:inPacketDescriptions];
}
At a compiled code level, this is a step backwards: all I've done is slowed the program down by an extra Objective-C message send.
Technically, a C function that takes a "context" pointer (like the inClientData pointer here) is not significantly different to a method. What a method does is makes data hiding and data abstracted actions easier. Within a method, you can easily access the instance variables of an object and you don't need to explicitly pass context into each function.
This is the cliché argument in favor of object-orientation — but it isn't why I reorganized these functions and methods.
The honest reason why I did it is aesthetics: it is easier to read a class that is implemented using Objective-C methods alone — it's more consistent. I chose to move towards an Objective-C aesthetic and away from the Standard C aesthetic of the CoreAudio sample code to promote consistent formatting, consistent means of accessing state variables, consistent ways of invoking methods and consistent ways of synchronizing access to the class.
With the majority of code now inside the class, I was in a better position to start handling changes through methods rather than direct member access.
My original approach to state came from Apple's original example. This example had just one piece of state: a bool named finished (which indicated that the run loop should exit).
The problem with this flag is how simple it is. It is unable to distinguish between the following:
AudioStreamer to stop but the AudioQueue thread has not yet responded.AudioQueue thread is created and we must exit.AudioQueue for temporary reasons (clearing it, changing device, seeking to a new point) but we don't want the loop to stop.For Apple's example, there was no problem: the first case was the only one that ever occurred.
As a hasty solution, I had added started and failed flags but these really only covered the first and third case adequately.
In the end, I realized that the AudioStreamer needed much more descriptive state where every combination of progress within each thread had a different position:
typedef enum
{
AS_INITIALIZED = 0,
AS_STARTING_FILE_THREAD,
AS_WAITING_FOR_DATA,
AS_WAITING_FOR_QUEUE_TO_START,
AS_PLAYING,
AS_BUFFERING,
AS_STOPPING,
AS_STOPPED,
AS_PAUSED
} AudioStreamerState;
and when stopping, one of the following values would also be needed:
typedef enum
{
AS_NO_STOP = 0,
AS_STOPPING_EOF,
AS_STOPPING_USER_ACTION,
AS_STOPPING_ERROR,
AS_STOPPING_TEMPORARILY
} AudioStreamerStopReason;
In this way, the state always describes where every thread is and the stop reason explains why a transition is occurring.
Combining this with an error code that replaces the old failed flag, I now have a complete desription of the state.
By cleaning up the state of the object, I was able to make the object capable of state transitions that weren't previously possible including pausing/unpausing and returning to the AS_INITIALIZED state after a stop (instead of requiring that the class be released after stopping).
In the old version of the project the only way for the user-interface to follow the playback state was to observe the isPlaying property on the object which reflected the kAudioQueueProperty_IsRunning property of the AudioQueue.
This observing was handled through KeyValueObserving. I'm a big fan of KeyValueObserving for its simplicity and ubiquity but this was not the correct place to use it.
KeyValueObserving always invokes the observer methods in the same thread as the change. Since all changes in AudioStreamer happen in secondary threads, this means that the observer methods were getting invoked in secondary threads.
Why is this bad? A minor drawback is simply the unexpectedness for the observer but the biggest reason was that the sole purpose of observing this property was to update the user-interface and the user-interface on the iPhone cannot be updated from any thread except the main thread. Even on the Mac, performing updates off the main thread can have unexpected and glitchy results.
The solution is to retain the NSNotificationCenter of the thread that first calls start on the object and use this center to send messages as follows:
NSNotification *notification =
[NSNotification
notificationWithName:ASStatusChangedNotification
object:self];
[notificationCenter
performSelector:@selector(postNotification:)
onThread:[NSThread mainThread]
withObject:notification
waitUntilDone:NO];
Don't invoke postNotification: directly from the secondary thread as, like most methods, it is not thread safe and it could be in use from the main thread.
Despite adding an extra thread on top of Apple's AudioFileStreamExample, I never really spent any time thinking about thread safety — a reckless approach to stability. In my defence Apple's example wasn't exactly cautious with its threads and would quit while the AudioQueue's thread was still playing the last buffer.
The most efficient approach to threading is to carefully enter @synchronized (or NSLock or pthread_mutex_lock) in a tight region around any use of a shared variable.
Unfortunately for the AudioStreamer class, almost everything in the class is shared. Instead, I decided to go for the decidedly less efficient approach of running almost everything in the class within a @synchronized section, emerging only at points when control must be yielded to other threads.
The drawback is that the code rarely runs simultaneously on multiple threads (although threading here is for blocking and I/O, not for multi-threaded performance reasons so that's not a probem). The advantage with this heavy-handed locking approach is that the only threading condition that may cause problems are deadlocks.
When do deadlocks occurs? Only when you're waiting for another thread to do something while you're inside the synchronized section needed by that other thread. The simple solution: never wait for another thread inside a synchronized section.
AudioStreamer has three situations where 1 thread waits for another:
AudioFileStream thread waits for any kind of control communication from the main thread or playback finished notification from the AudioQueue thread).enqueueBuffer method (AudioFileStream thread waits for the AudioQueue thread to free up a buffer).AudioQueueStop invocations (waits for the AudioQueue to release all buffers).The first two points are easy: perform these actions (any any method invocation which invokes them) outside of the @synchronized section.
The final point is harder: the synchronous stop must be performed inside the @synchronized section to prevent multiple AudioQueueStop actions occurring at once. To address this, the release of buffers by the AudioQueue (in handleBufferCompleteForQueue:buffer:) must perform its work without entering the @synchronized section (although it's allowed to use the queueBuffersMutex as normal since that isn't used by anything else during a synchronous stop).
Of course, every time the @sychronized section is re-entered, a check must be performed to see if "control communication" has occurred (the class checks this by invoking the isFinishing method and exiting if it returns YES).
The easiest source of metadata comes from the HTTP headers. Inside the handleReadFromStream:eventType: method, use CFReadStreamCopyProperty to copy the kCFStreamPropertyHTTPResponseHeader property from the CFReadStreamRef, then you can use CFHTTPMessageCopyAllHeaderFields to copy the header fields out of the response. For many streaming audio servers, the stream name is one of these fields.
The considerably harder source of metadata are the ID3 tags. ID3v1 is always at the end of the file (so is useless when streaming). ID3v2 is located at the start so may be more accessible.
I've never read the ID3 tags but I suspect that if you cache the first few hundred kilobytes of the file somewhere as it loads, open that cache with AudioFileOpenWithCallbacks and then read the kAudioFilePropertyID3Tag with AudioFileGetProperty you may be able to read the ID3 data (if it exists). Like I said though: I've never actually done this so I don't know for certain that it would work.
The biggest variation you may want to make to the class is to download fixed-length files, rather than streaming audio.
To handle this, the best approach is to remove the download from the class entirely. Download elsewhere and when "enough" (an amount you should determine on your own) of the file is downloaded, start a variation of the class that plays by streaming from a file on disk.
To adapt the class for streaming from a file on disk, remove the CFHTTPMessageRef and CFReadStreamRef code from openFileStream and replace it with NSFileHandle code that uses waitForDataInBackgroundAndNotify to asynchronously stream the file in the same way that CFReadStreamRef streamed the network data.
Once you're streaming from a file, you'll probably want to permit seeking within the file. I've already put hooks within the file to seek (set the seekNeeded flag to true and set the seekTime to the time in seconds to which you want to seek) — however, the mechanics of seeking within the file would be dependent on how you access the file.
Incidentally, the AudioFileStreamSeek function seems completely broken. If you can't get it to work (as I couldn't) just seek to a new point in the file, set discontinuous to true and let AudioFileStream deal with it.
At the moment, if the AudioQueue has no more buffers to play, the state will transition to AS_BUFFERING. At this point, no specific action is taken to resolve this situation — it assumes that the network will eventually resume and requeue enough buffers.
I actually expect there will be cases where this action is insufficient — you may need to ensure that the AudioQueue is paused until enough buffers are filled before resuming or even restart the download entirely. I haven't experimented much since it is easiest with streaming audio just to stop and start new.
Incidentally, if you're curious to know how many audio buffers are in use at any given time, uncomment the NSLog line in the handleBufferCompleteForQueue:buffer: method. This will log how many 1 kilobyte audio buffers are queued waiting for playback (when the queue reaches zero, the AudioStreamer enters the AS_BUFFERING state).
You can download the complete AudioStreamer project as a zip file (around 110kB) which contains Xcode projects for both iPhone and Mac OS. You can also browse the source code repository.
The functionality of this new version has not changed greatly — my purposed was to present a version that is more stable and tolerant of unexpected situations, rather than add new features.
As before, the AudioStreamer class should work on Mac OS X 10.5 and on the iPhone (SDK 2.0 and greater).
The source repository is hosted on github so you can browse, fork or track updates as you choose. I will likely update again in future (I can't imagine I've written this much code without causing more problems) and this way, you can see the changes I've made.
I hope this post has shown you a number of problems that can happen when code is written hastily. This doesn't mean you should always avoid hastily written code (timeliness and proof of concepts are important) but it does mean you should be practised at refactoring code and not simply slap poor fixes onto code that doesn't cleanly solve a problem in the first place.
by Matt Gallagher (noreply@blogger.com) at June 19, 2009 06:59 PM