Archive for the 'software' Category

Teach yourself Cocoa Touch Programming in 24 hours

Monday, October 19th, 2009

A few months ago, Pearson Education contacted me about writing a book about Cocoa Touch. I agreed because although I had a lot of experience on other platforms, I found Cocoa somewhat bewildering when I first encountered it. Most books either assumed a Mac background or really wanted to teach me Object-Oriented programming from the ground up. Often after reading one of them, I had the impression I had learnt something until I tried to apply whatever it was I learned: The problem was that they glossed over how Cocoa really works, and why it was architected in that way.

Cocoa and Objective-C are very powerful tools to build Applications and Graphical Interfaces quickly. This power comes from splitting GUI tasks into a few well-chosen abstractions that reduce the amount of code we have to write. Once understood, these abstractions seem obvious, so most books simply introduce them as the way things are done in Cocoa, leaving beginners bewildered.

Instead I show readers how these abstractions work and how they simplify their code. Because they understand the mechanics of each task, readers will be able to resolve most difficulties on their own quickly, rather than exploring new levels of frustration. To further help, the book is interlaced with debugging techniques. My goal in writing the book was to provide a sound foundation that programmers can use to build functional applications, and feel confident that they can solve the problems they will encounter. Instead of covering the latest API fashions, I concentrate on the APIs and tools you’ll actually use and will need to understand.

The book was published last Thursday, Oct 15 2009, and is available now at the publisher’s. Amazon shows they have yet to receive it (and therefore discount it). It’s in full color!

Understanding the Objective-C Language

In the first five chapters, you’ll build a basic application: a Calculator. You’ll learn how to use the key tools used when developing iPhone applications: Xcode, the debugger and Interface Builder. It also teaches you Objective-C: Objective-C is a thin layer on top of C which adds language support for object-oriented programming. Cocoa uses reference counting for memory management, but the judicious use of auto release pools makes this much easier than you might expect. You’ll also learn about Cocoa Touch’s Foundation classes with provide basic functionality such as Unicode strings, arrays, dictionaries. Practically speaking, by the end of chapter 4 you’ll have written a functioning calculator.

When I first learned Objective-C, I found many bugs intractable because I did not understanding messaging. To help you avoid this pitfall, I explain messaging and how it is implemented in chapter 4. Similarly, it took a while for me to understand auto release pools. They simplify your code, but are often presented as a form of magic you just use. My book is a magic-free zone, clarifying how they work and their limitations up-front. Other Objective-C particularities you’ll learn about are class objects, updating classes on the fly, class clusters, key-value coding and key-value observing. By the end of chapter 5, for most intents and purposes you’ll be an Objective-C expert. Unlike competing books, and Apple’s own documentation, I detail the computational complexity of Objective-C arrays and dictionaries.

User Interface Foundations

The building blocks of user interfaces are views. To build a user interface from views, one simply builds a tree of views (a view hierarchy) which specifies the location and order in which views are drawn. Most interfaces can be built using Interface Builder, a graphical design tool. Unlike competing solutions, Interface Builder does not generate code, but a NIB file that states how to build the view hierarchy. Most books only cover building user interfaces in Interface Builder. As a programmer this left me with a bad taste, as I was relying on some unknown mechanism working behind my back. Debugging was difficult because I had no idea how NIB files were loaded. Instead I show you how to build view hierarchies in code, how NIB files are loaded, and the pitfalls you may encounter.

Views draw themselves using a 2D renderer called Core Graphics (or Quartz). They respond to user interaction dispatched to them by Cocoa. Implementing your own User Interface element (a button) will give you a clear understanding of the ins and outs of user interface elements, and will help you polish your applications with custom user interface elements.

Unlike most user-interface solutions, Cocoa is not architected as a library. That is to say, your application does not just call library functions at will. Cocoa is architected as a framework: it calls your application code when it needs to. This turns out to be a powerful solution, but is disconcerting for programmers coming from other platforms. Because misunderstanding the run loop causes seemingly unrelated bugs I spend entire chapter discussing how run loop is used and how it interacts with other Cocoa sub-systems such as the auto release pool, and the responder chain.

A distinguishing feature of the iPhone’s user interface is how much animation it uses. These animations are created with Core Animation. Core Animation uses the iPhone’s 3D graphics chip and a separate thread to create smooth animations. Understanding Core Animation’s architecture will help you use it and remember its limitations. To further help you I share tips learned developing commercial software. By the end of this chapter, you’ll have implemented your own Cover Flow clone shown above! (animal images courtesy of http://www.public-domain-image.com).

Advanced User-Interface Elements

View Controllers reduce the amount of code you must write. Unlike many beginning programmers, you will not confuse them with views because the book clearly differentiates them. An example will solidify your understanding: displaying a Scientific Mode in the calculator when the iPod is rotated.

Tables are an essential component of most user interfaces. The iPhone’s table support is particularly flexible, and I’ll show you how you can customize it to create fast scrolling distinctive tables. In this hour, you’ll build a table based application that lazily loads data from an Internet source: a Twitter application.

Many applications present information on multiple screens. Navigation bars and Tab bars provide a standard way of navigating between the screens. I show you how to build applications using these user interface elements, and show you how the views and view controllers interact.

iPhone OS 3.0 introduces undo/redo functionality. Cocoa Touch solves this problem in a particularly elegant manner. However, it differs substantially from other solutions you may have used. To help you understand it fully, I dedicate an entire chapter to understanding the problems it solves, how it works, and why it was architected in this way.

Accessing the Internet

By providing full internet connectivity and the ability to render most web pages, the iPhone enables whole new classes of applications. To help you build robust applications, chapter 14 discusses how networks work in detail. Working through a real example (adding error handling to the Twitter application) shows you how this is done in practice.

Chapter 15 introduces UIWebView, a versatile User Interface component able to render documents in HTML, PDF, RTF, RTFD, Microsoft Office or iWork formats. You’ll learn how to use Javascript to update HTML formatted pages or parse JSON strings, and how to use new HTML5 features supported by the iPhone.

Saving and Retrieving Data

Cocoa Touch provides four means of saving and retrieving data: application preferences, files, a small sql database, and Core Data. You’ll learn how to add application preferences to the Twitter application, that users can customize in the Settings application. In chapter 17, I discuss when and how to use files or the sqlite database. iPhone OS 3.0 added Core Data which lets you load and save objects to storage transparently by providing a single object definition instead of writing your own classes and your own serialization code. To help you decide whether or not to use Core Data, I discuss its performance characteristics.

Interacting with the World

Only a few years ago, the idea of a tiny device containing your music and video library, finding your location anywhere on the planet, and using accelerometers as a user interface would have seemed like Science Fiction. The iPhone is a remarkable convergence device. Chapters 19 and 20 explains how to use these capabilities in detail.

Chapter 21 shows you how to share data with other applications. Custom URLs let you start other applications. Pasteboards provide cross-application copy and paste functionality. To export data from your device, I show you how to send emails from your application, or run a tiny webserver.

Completing Your Application

Completing your application involves four tasks: debugging it, optimizing it, localizing it and shipping it.

I devote an entire chapter to debugging, showing you not only how to use the debugger gdb, but also dtrace, valgrind, and nib2obj. Sometimes however, bugs are caused by misunderstanding how the frameworks work. Usually other programmers can help you, but sometimes the only solution is to reverse engineer the framework. To help you do this, the chapter ends with a short tutorial teaching you how to do this.

Optimizing your application is another key topic and has its own chapter. You’ll learn how to write your own profiling code, and learn how to use the two iPhone profilers: Shark and Instruments. Because of the iPhone’s memory limitations, it is particularly important to minimize your application’s memory consumption. To further help you, I introduce the Clang static analysis tool which helps you detect memory leaks at compilation time.

The final chapter covers localization and shipping your application. Because the App Store is the only authorized means of distributing applications, and because Apple can reject your application once you submit it for publication, developing for the iPhone is somewhat risky. To help you mitigate this risk, I analyze the types of applications Apple has refused to distribute on the AppStore, and provide some simple guidelines to follow.

Additional materials

If you haven’t programmed C in a while, I provide Appendix A to quickly refresh your memory of the salient points.

While developing for the iPhone you may encounter a number of issues due to Apple’s development tools. To help you resolve these issues quickly, Appendix B devotes 18 pages to debugging these issues.

Appendix C lists resources I have found useful when developing iPhone applications.

Appendix D includes a number of advanced topics of interest to expert programmers: how Cocoa starts your application, a deeper discussion of exceptions, and how and when to use threads.

UI: Computers should be seen, not heard

Tuesday, August 21st, 2007

When I was a child, my father would quote the Victorian era expression “Small children should be seen, not heard” when I became overly boisterous. At that time I was not sure whether or not he meant it. I do however believe it should apply to computers…

Don’t put words in other people’s mouths!

Safari’s URL and form autocompletion really irritate me: they always add their guess to the end of what I’m typing a third of a second later. That means I press enter, only to find incorrect data was added to the field, and then submitted. Others have the same complaint. Nevertheless people do want some form of autocompletion.

For Find It! Keep It! I added an almost identical interface: the same popup window, the same use of up and down keys to choose, the same ability to click or double click on a choice, but crucially, you’re always in control. Nothing is added behind your cursor until you use the up and down keys to choose a selection. Type a new character in, and your selection is cleared.

Don’t just grab something that I was about to use!

Spotlight invariably updates its window the instant before I click the file I want to open, causing me to open the wrong thing.

A different solution is to use colour to show which page contain the search term. Although it does mean more information on the screen, it lets users click on pages as soon as the search term is found. That’s what I’m doing in Find It! Keep It! when searching pages by content.

Don’t jump up and down to get attention!

An application’s icon will bounce automatically when the application opens a modal panel if it is running in the background. It is possible for each application to remove this effect on a per-application basis. The problem then is that either one has to set a preference in each app to override default behaviour, or apps behave inconsistently. Apple could system wide preference for this to alleviate the problem.

Delay informing people about things until it’s relevant!

Software Updaters are another source of interruption that I don’t believe is necessary: as long as I don’t adversely notice the change when I use the tool, I don’t need to know that it’s updated the moment I use it. Web applications change constantly, but no one complains unless they stop working. Desktop applications will, I believe, adopt a similar model. How then, will the user know about the latest improvements? By email! People are in the mood for reading things when reading their email, not when working. Apple’s Software Updater is the worst in this category: it interrupts you and then asks you to drop everything and restart your computer.

Software should not behave like 3 year old children

It is difficult to hold a conversation when someone continually interrupts: one can’t organize one’s thoughts while being bombarded with new information. Using a computer is a similar situation. When the user talks, the computer should just listen.

Solving the evolving File Format problem

Tuesday, August 14th, 2007

Andy Matuschak brought up a really good obstacle to providing an easier upgrade and downgrade path for applications: changing file formats.

Changing file-formats is far from free. It fractures your user base as your users find they cannot share their documents with their friends. It makes it difficult for you to regression test your code. And it dissuades third parties from supporting your file-format, because you’re giving them more work each time you change. Overall changing file-formats should be avoided where possible. How then can you extend your application?

Future proof your file-formats!

I try to keep my file-format forwards compatible: the same file should work with older versions of my software. This makes it easier to find bugs that I may inadvertently have added: I can regression test against an older version of the software.

I use a file-format that lets the Application store but not use the fields it does not understand. There are many such solutions: SQLITE database, Trees, XML, python pickles… The reason to store fields one does not understand is that one can save them back out later.

For example consider an HTML document. Even if your HTML editor does not know what the “blink” tag means, it can remember the fact that that there is a “blink” tag around the text over there. Even if you edit the text within the blink tag, it can make reasonable guesses about grouping. Of course if you delete the text and type it back in, the blink tag will be gone. But most of the time you don’t just lose your formatting because a colleague used a different tool to edit your file. The key idea here is that you can use an extensible hierarchical data format to keep things associated with their attributes.

Keep the conversion code separate!

Sometimes, to simplify the main application’s code, it does makes sense to change file-formats. In that case, write a bidirectional converter. A version field in the file-format specifies (by naming convention) the converter to use so that the App when encountering a file of a different file-format can figure out the chain of conversions it must apply to get something it understands.

Now if you downgrade, you’re in almost the same position as your friend who never upgraded: your application can’t read the new format files. There is however a difference: as part of the upgrade process a bidirectional converter was added. It does not need to be removed on a downgrade. Because the the older version of the application knows how to figure out which converters to call, it will just find them and call them.

Note that because the original file-format is extensible, it’s possible for the bi-directional converter not to throw away any data. Similarly because the oldest version of the app stores the stuff it does not understand and saves it back out again, changes made by a co-worker with an older version of the software should cause little data loss.

Additional benefits of independent converters

Because you have independent converters you can earn bonus points with people who have not yet been able to upgrade. You can give or sell them the tool to convert the files. The tool can even advertise the features they’d get by upgrading.

You benefit because your code is simpler. The application code is simpler because it only knows its native file-format, the converter’s code is simpler because it’s focussed on one task. You also benefit because it’s easier to require a new base OS for your next version without fracturing your user-base.

Of course writing writing a converter is a pain, and a bidirectional one even more of a pain, but as I said in the beginning changing file formats should be avoided. Writing a bidirectional converter forces you to consider more cases, leading to a better upgrade converter. Furthermore you can reduce the pain for third parties by giving or licensing your converter to them.

What about standard file formats?

I think the same idea of independent converters can be applied, although it may not be possible to keep the additional information known to later versions of the file-format in earlier versions of the file format.

A better Software Update mechanism

Monday, August 13th, 2007

I’m using Sparkle to update Find It! Keep It!. It’s a state of the art desktop application update solution. It would however like to see some improvements.

What I don’t like about the current solution

Don’t interrupt me!

Sparkle asks whether you want to update the app when you start it. When someone opens his mouth, it’s not a good time to ask him something: he’s about to say something. Starting an app is the same.

Sparkle can also ask you whether you want to update the app when it’s running. That’s just plain rude: it could be interrupting the user’s thought process, especially as, IIRC, it causes the application’s icon to bounce if you’re using another app.

How am I supposed to know?

There is no way the user can know whether an upgrade will improve his experience, or waste time as he tries to recover his mistake. Asking can only make people feel stupid. Web Applications don’t ask. You get the upgrade, and you may not even notice.

Automatically upgrading software on other people’s computers could be a support nightmare, so it must be very easy to go back to where you were. Sparkle currently doesn’t provide a downgrade path.

Don’t make me wait!

Sparkle downloads the whole app on each update. For people on slower connections it’s noticeable and irritating. It would be better if the user didn’t notice it.

Overall impact

About 60% of my users seem to have disabled Sparkle. That’s a lot given that during the beta-test cycle, upgrades are not optional.

A solution

Transparent upgrades

Updates are small bidirectional binary diffs (created using bsdiff for example) that are downloaded in the background. To minimize user visible lag, they could be downloaded in small sections.

By default upgrades are applied silently at application startup, but the user can require a confirmation step using the Application’s Preference Panel.

In built version selector

The Application’s Preference Panel easily lets users switch back to an older version. It shows the version information for each version that Sparkle currently only shows when checking for a new update. Because each downloaded upgrade is a bidirectional diff, one can always downgrade.

Downgrade Appcasts

The Appcast Sparkle currently uses to inform the Application that an upgrade is available is enhanced so that a developer can correct an “Oops” by retracting a faulty upgrade. Adding this to the appcast protocol would be easy.

Harness

A small unchangeable harness is used to launch and monitor the App. If the app crashes on startup before the user could access the preference panel, it retracts any recent unproven upgrades and sends crash logs to the developer, after asking the user. Using a process to monitor another is extensively used for high reliability Telecom Systems: nine nines reliability (that’s 30ms downtime a year).

Growl and Email

If Growl is installed, it can be used to inform the user of the upgrade.

Generally however I think email is the most appropriate forum to inform the user of updates: one is more inclined to read information if one is not busy doing something else. Notice that even users who worry about providing their email addresses to the software developer can be catered for: the app itself can email the user with the information currently in the appcast when an upgrade has been downloaded.

Why I’m hoping Sparkle will implement this

I’m hoping the current Sparkle improvement discussion will consider and adopt this idea: Sparkle is already a standard, with tools like AppFresh supporting it.

IPhone apps: they weren’t kidding

Friday, June 22nd, 2007

The new WebKit inspector may be the demonstration vehicle that convinced Apple that AJAX UIs could work, despite many people’s concerns.

Very few applications so far use HTML for rendering. Find It! Keep It!’s Database view is rendered as HTML and uses Javascript within the application. NetNewsWire’s Combined View is also implemented in this way. WebKit Inspector goes one step further, by emulating what looks to be a Cocoa app in Javascript extremely effectively. Like Find It! Keep It!, it calls native functions to perform operations that would otherwise be impossible with Javascript. However it also uses the canvas element to make its bar graphs. Its only flaw is that it does not scale correctly when you press Command + to make the text bigger.

Find It! Keep It!’s Tag Palette

Wednesday, April 25th, 2007



The Tag Palette was requested by a user. It’s invoked by a click on the tag palette icon on the toolbar. Floating over the main window, it lets you quickly select the tags you would like a page to have, rather than type them in. Because it shows you every tag, you won’t end up with duplicate tags (such as ai and artificial-intelligence). Of course it lets you add new tags, or delete ones. Deleted tags are crossed out so that you can undelete them, if you made a mistake.

For people who prefer keyboards, you can still type them into the tag field. To avoid duplicating tags, press Option-Escape for the auto-complete feature. Tags known to Find It! Keep It! are listed first, followed by words from the dictionary so that difficult tags such as axolotl are spelt correctly.

Find It! Keep It! is in Beta. It works well, but I’m adding UI enhancements requested by my users. You can join the beta here.

Mac Google Desktop… unfortunately rather intrusive.

Thursday, April 5th, 2007

The user experience

I like Google’s tools. I use GMail and google. My Find It! Keep It! tool provides shortcuts to Google’s web, book, froogle, news and scientific paper searches. So I downloaded Google’s desktop mac hoping for a faster Spotlight.

Like the folks at TidBits, I found it slowed down my computer significantly when indexing my drive. However one can turn it off using the System Preferences panel it installs. Like that I can let it index stuff at night.

Press Command twice and a search panel shows up, which will show the first 10 results. To see more, your browser will be opened to display the results as page that looks like google’s generic search page, so it’s running a small web server.

It runs as root, and does not respect your update statistics settings

Google Desktop installs itself as root: the index is at /Library/Google/Google Desktop/Index/(some directory which only root can access). This means it can access anything on your machine and do anything it likes. It doesn’t need to and on a first date, I don’t trust anything that much. Every user on the machine will have their content indexed, even if they don’t agree. You could say that Spotlight also runs as root, but people using an operating system written by Apple do have to trust Apple.

Even more bothersome: I told it not to upload statistics to Google. Their Privacy Policy says:

If you choose to enable Usage Statistics on Google Desktop, it allows Google Desktop to send crash reports and to collect a limited amount of non-personal information from your computer and send it to Google. This includes summary information, such as the number of searches you do and the time it takes for you to see your results, and application reports we’ll use to make the program better.

Well I didn’t, but Little Snitch tells me that a program called StatsUploader wants to talk to dc-in-f99.google.com every 30 odd minutes so. I happen to trust Little Snitch as I used it to help me make sure that Find It! Keep It! wasn’t loading anything from the Internet (unlike most other “internet page saving solutions”, such as those that use WebArchives).

It silently installs an Input Manager

Find It! Keep It! crashed, and the crash started neither Apple’s CrashReporter nor my built in CrashReporter which is extremely odd. Given my past bad experience with Input Managers, I used Find It! Keep It!’s input manager panel to see whether I had acquired a new one. Indeed I had. It lurks in /Library/InputManagers/GoogleModLoader.

Now this bothers me. I did NOT agree to have an InputManager installed. InputManagers in /Library/InputManagers are loaded into EVERY application running on the computer for every user. So what the #!$! does it do? Simply running
cd /Library/InputManagers/GoogleModLoader/
strings GoogleModLoader.bundle/Contents/MacOS/GoogleModLoader
in the Terminal tells us that it loads modules.

Further investigation using OTX shows that indeed it crawls a Google/Mods directory and loads modifier bundles into the applications specified by the key GoogleModTargetApplications in some dictionary somewhere. It also appears to do a fair amount of stderr, debugging, pthread and system logging.

If you attach gdb to a running copy of Safari, you can see that SafariSearchResults.gmod and SafariWebHistory.gmod from /Library/Application Support/Google/Mods/ are now loaded by typing info sharedl. One thing they do is to add a new item to your google searches: “About 34 results stored on your computer”. I’m guessing that SafariWebHistory allows pages you just visited to be found with google desktop.

Nevertheless, Input Managers should not be installed silently. They can easily cause system instabilities and this particular mechanism could be diverted by third parties to install unauthorized gmods in a place no one knows about: a big security risk. Given the furore over Unsanity’s Smart Crash Reporter, I’m surprised Google installs this. It’s not like anybody worries about Unsanity’s secret plans of world domination.

It also installs a Kernel Extension

John Gruber over at Daring Fireball found where the injected code lives and noticed that they’re also installing a kernel extension!

Again kernel extensions aren’t something that should be installed silently as they could very easily impact the system’s stability.
For instance, it includes the nice message “socred_fini() failed, which is a known bug with Apple’s socket filters. Sorry but you have to reboot”.
cd /Library/Google/Google\ Desktop/GoogleDesktopDaemon.bundle/Contents/Resources
sudo strings GDFSNotifications.kext/Contents/MacOS/GDFSNotifications

I’m have no idea what its doing with the sockets, but a guess would be that they might need something like that to inform Google Desktop when a file changes to reindex it or for their snapshot capability.

Conclusion

I’m disappointed. I was going to look into Google’s open API to speed up searching the Find It! Keep It! Database for those users using Google Desktop. I think I’ll wait.

Hopefully future versions of Google Desktop will respect user preferences, clearly request the right to install any Input Managers and allow paranoid people like me to give it limited permissions (eg: a single user’s permissions). Alternatively they could release its source code, as they have done with MacFUSE so that we know what it’s doing. In the mean time, I’m uninstalling it.

Hopefully Leopard will draw attention to Input Managers which will prevent nasty surprises. That’s not to say they’re all bad. They just shouldn’t be installed without a user’s consent.

Changed on Thursday 5 April to add some more information, and clarify it

Nasty printing bug fixed

Thursday, January 4th, 2007

Sometimes printing would crash Find It! Keep It!… The problem was the “sometimes”. It happened extremely rarely on my Mac Mini, but often on my Intel Macbook. Clearly Rosetta was to blame!

Well, no. Finally someone other than me experienced it so I decided to dig in deeply.

The first problem is that debugging a PPC process under Rosetta is an order of magnitude slower. Having written a debugger and having been fascinated by emulators for a long time (ZX Spectrum emulator on the Atari ST, UAE’s 68000 emulator, Bochs, and Awesim), I know why it’s slow: when debugging you have to keep track of state such as the instruction pointer you can simplify away when running at full speed. So the first task was to find a webpage that would crash regularly on PPC.

As luck would have it, displaying a big database with my new theme would cause the crash every second time you tried to print it. My first guess was memory being freed that shouldn’t be…

WebLibrarian(216,0x1b37e00) malloc: *** Deallocation of a pointer not malloced: 0x3927e70; This could be a double free(), or free() called with the middle of an allocated block; Try setting environment variable MallocHelp to see tools to help debug

Look at that! Clearly memory allocation failing!

Well, no. I had noticed that a separate thread was being started by the printing process, but the pieces only fell into place when I saw:

(gdb) info threads
* 12 process 216 thread 0x802f 0x95eb4e88 in khtml::main_thread_malloc ()
10 process 216 thread 0x6807 0x9001f08c in select ()
8 process 216 thread 0x7103 0x9000ab48 in mach_msg_trap ()
7 process 216 thread 0x6903 0x9000ab48 in mach_msg_trap ()
6 process 216 thread 0x450f 0x90049ea8 in syscall_thread_switch ()
4 process 216 thread 0x3603 0x9000ab48 in mach_msg_trap ()
2 process 216 thread 0x2303 0x9000ab48 in mach_msg_trap ()
1 process 216 local thread 0xf03 0x95eb4e88 in khtml::main_thread_malloc ()

Main thread malloc probably should not be called in two threads. Indeed, googling on it found Added assertions to ensure that main_thread_malloc and friends are only called on the main thread.

Let’s look at the two threads. The printing thread looks like this:

#0 0x95eb4e88 in khtml::main_thread_malloc ()
#1 0x95c8000c in KWQListImpl::insert ()
#2 0x95cfbb5c in khtml::RenderBlock::insertFloatingObject ()
#3 0x95cfb520 in khtml::RenderBlock::skipWhitespace ()
#4 0x95cf9d4c in khtml::RenderBlock::findNextLineBreak ()
#5 0x95cf8cec in khtml::RenderBlock::layoutInlineChildren ()
#6 0x95cf54d4 in khtml::RenderBlock::layoutBlock ()
#7 0x95cf886c in khtml::RenderBlock::layoutInlineChildren ()
#8 0x95cf54d4 in khtml::RenderBlock::layoutBlock ()
#9 0x95cf886c in khtml::RenderBlock::layoutInlineChildren ()
#10 0x95cf54d4 in khtml::RenderBlock::layoutBlock ()
#11 0x95cf886c in khtml::RenderBlock::layoutInlineChildren ()
#12 0x95cf54d4 in khtml::RenderBlock::layoutBlock ()
#13 0x95cf886c in khtml::RenderBlock::layoutInlineChildren ()
#14 0x95cf54d4 in khtml::RenderBlock::layoutBlock ()
#15 0x95cf886c in khtml::RenderBlock::layoutInlineChildren ()
#16 0x95cf54d4 in khtml::RenderBlock::layoutBlock ()
#17 0x95cf5fac in khtml::RenderBlock::layoutBlockChildren ()
#18 0x95cf54ec in khtml::RenderBlock::layoutBlock ()
#19 0x95cf5fac in khtml::RenderBlock::layoutBlockChildren ()
#20 0x95cf54ec in khtml::RenderBlock::layoutBlock ()
#21 0x95cf224c in khtml::RenderCanvas::layout ()
#22 0x95cf1b04 in KHTMLView::layout ()
#23 0x95dd3750 in KWQKHTMLPart::forceLayoutWithPageWidthRange ()
#24 0x95de84a4 in -[WebCoreBridge forceLayoutWithMinimumPageWidth:maximumPageWidth:adjustingViewSize:] ()
#25 0x95adc92c in -[WebHTMLView layoutToMinimumPageWidth:maximumPageWidth:adjustingViewSize:] ()
#26 0x95b103dc in -[WebHTMLView _setPrinting:minimumPageWidth:maximumPageWidth:adjustViewSize:] ()
#27 0x95b107ec in -[WebHTMLView knowsPageRange:] ()
#28 0x9392df4c in -[NSView(NSPrinting) _knowsPagesFirst:last:] ()
#29 0x9392dc68 in -[NSView(NSPrinting) _setUpOperation:helpedBy:] ()
#30 0x9392d7b8 in -[NSView(NSPrinting) _realPrintPSCode:helpedBy:] ()
#31 0x9392d6f4 in -[NSConcretePrintOperation _doActualViewPrinting] ()
#32 0x9392d520 in -[NSConcretePrintOperation _continueModalOperationToTheEnd:] ()
#33 0x92961194 in forkThreadForFunction ()
#34 0x9002b508 in _pthread_body ()

There are other variations, but basically the printing thread renders the window differently to print it. Clearly you have to do that if CSS has different settings for display and print media. In fact you always do it.

So what’s happening in the main thread?

#0 0x95eb4e88 in khtml::main_thread_malloc ()
#1 0x95c8000c in KWQListImpl::insert ()
#2 0x95d21f1c in DOM::NodeImpl::dispatchGenericEvent ()
#3 0x95d21d0c in DOM::NodeImpl::dispatchEvent ()
#4 0x95d2682c in KHTMLView::dispatchMouseEvent ()
#5 0x95d24294 in KHTMLView::viewportMouseMoveEvent ()
#6 0x95d23994 in KWQKHTMLPart::mouseMoved ()
#7 0x95adb2dc in -[WebHTMLView(WebPrivate) _updateMouseoverWithEvent:] ()
#8 0x95adb040 in -[WebHTMLView(WebPrivate) _updateMouseoverWithFakeEvent] ()
#9 0x9296bbf8 in __NSFireDelayedPerform ()
#10 0x907f0550 in __CFRunLoopDoTimer ()
#11 0x907dcec8 in __CFRunLoopRun ()
#12 0x907dc47c in CFRunLoopRunSpecific ()
#13 0x93208740 in RunCurrentEventLoopInMode ()
#14 0x93207d4c in ReceiveNextEventCommon ()
#15 0x93207c40 in BlockUntilNextEventMatchingListInMode ()
#16 0x93730ae4 in _DPSNextEvent ()
#17 0x937307a8 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#18 0x9372ccec in -[NSApplication run] ()
#19 0x9381d87c in NSApplicationMain ()
#20 0x0060910c in init_AppKit ()
...
#45 0x00006e00 in py2app_main (argc=-1870946304, argv=0x7204, envp=0x3986548) at src/main.c:952
#46 0x00007964 in main (argc=1, argv=0xbffffaec, envp=0xbffffaf4) at src/main.c:1007

Each time the mouse moves over the WebView, WebKit tells javascript about it (to enable popups, etc). So my main thread was pottering along as normal, blindly oblivious of the other thread.

Telling the printOperation NOT to be threaded should fix the bug, preventing non reentrant code from being reentered. So I changed

printOperation.setCanSpawnSeparateThread_(YES)

to

printOperation.setCanSpawnSeparateThread_(NO)

and suddenly printing no longer crashes. Not only that, but some other bugs went away too: sometimes printing would make the browser redisplay the website incorrectly, sometimes the printout would have massive gaps in it. All these suddenly were fixed.

Now another piece fell into place. I had noticed that the crashes happened more often on large complex pages than simple ones. The bigger a page, the longer it takes the printing thread to render it, and the more likely the two threads would interfere with each other. Furthermore, it happened more often on the Intel Mac… which is a dual core processor. If the threads were running concurrently on different cores, they would interfere much more often.

So where did I get that crazy idea of threading the print? From the apple developer mailing lists. It runs out that the code is also in Shiira and Adium.

For kicks, I tried Shiira on a large complex document which crashed my program regularly… and BOOM! This is apparently a known problem which is worse on dual core systems… The bug’s still in Shiira 2.0, so I’m trying to email Shiira’s author to tell him.

Argh… Beta 3

Sunday, December 3rd, 2006

The first bug I got back from Beta 2 showed that the new Crash Reporter functionality wasn’t always started up correctly… despite my testing for that case. Turns out that what I thought was memory given to me by Python was in fact garbage collected (and therefore … disappears after an indeterminate amount of time). Since the Crash Reporter is the means for users to signal bugs, I’ve had to rush the third beta.

Second Beta released & Input Manager disabler

Sunday, December 3rd, 2006

I released the second beta of Find It! Keep It! today.

It contains a few bug fixes, and a crash reporter to help people report bugs.

The biggest change is that Input Managers are disabled within Find It! Keep It! by default. They’ll still work in your other applications.

Why? All but one of the reported crashes at launch were due to Input Manager conflicts: 50% of all reported bugs! Understandably, if a crash is your first experience with Find It! Keep It!, you’ll assume Find It! Keep It! is at fault.

Contacting the author of every Input Manager that fails to ask them for a fix isn’t ideal: my users need to tell me what crashed and work with me to isolate the fault, and the Input Manager author needs to be willing to fix the problem. In the real world, most users will just give up.

Furthermore, in many cases, Input Managers are targeted at a specific application. For instance, Safari Tidy targets Safari, Chax targets iChat. At best they are harmless when loaded into other applications. At worst they’ll crash them.

So what if you actually want to access an Input Manager’s functionality from within Find It! Keep It!? I provide a Preference Panel that allows you to enable any of your Input Managers. When loading a new combination of Input Managers, Find It! Keep It! will step you through the process to help isolate crashes on load. It also disables any Input Manager it recognizes as having crashed on load, and restarts without it.

Hopefully, this is the best of all worlds: the ability for users to customize their systems without the penalty of instability.

I would like to see Apple providing stronger system controls over Input Managers, and other customizations: the user should always be in control. In particular Input Managers make adware trivial to implement on the Mac. Apple could improve overall user experience by requiring users to explicitly go to a preference panel to allow a given Input Manager to load into a given application.