<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lap Cat Software Blog</title>
	<atom:link href="http://lapcatsoftware.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://lapcatsoftware.com/blog</link>
	<description>Coding under the close supervision of cats</description>
	<lastBuildDate>Sat, 24 Jul 2010 13:30:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>A world without Twitter</title>
		<link>http://lapcatsoftware.com/blog/2010/07/09/a-world-without-twitter/</link>
		<comments>http://lapcatsoftware.com/blog/2010/07/09/a-world-without-twitter/#comments</comments>
		<pubDate>Sat, 24 Jul 2010 13:30:56 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[HTML]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=249</guid>
		<description><![CDATA[This post has been rolling around in my mind for a while &#8212; along with my bank balance, the song &#8220;Gee, Officer Krupke&#8221;, and Jennifer Aniston&#8217;s &#8230; nevermind. I was inspired to finally commit it to paper, or whatever the heck this big rectangular white thing is, by a recent tweet from @danielpunkass, AKA Daniel [...]]]></description>
			<content:encoded><![CDATA[<p>This post has been rolling around in my mind for a while &mdash; along with my bank balance, the song &#8220;Gee, Officer Krupke&#8221;, and  Jennifer Aniston&#8217;s &hellip; nevermind. I was inspired to finally commit it to paper, or whatever the heck this big rectangular white thing is, by <a href="http://twitter.com/danielpunkass/statuses/17949279943" title="danielpunkass tweet 17949279943">a recent tweet</a> from <a href="http://twitter.com/danielpunkass" title="@danielpunkass">@danielpunkass</a>, AKA Daniel Jalkut, AKA Mr. <a href="http://www.red-sweater.com/marsedit/" title="MarsEdit">MarsEdit</a>, AKA The Boston Strangler. As John Lennon might say if he were alive today, and unable to come up with any new ideas in the last 40 years: imagine there&#8217;s no Twitter. It&#8217;s easy if you try. Command-r, command-r.</p>
<p>The credit, or blame, goes to <a href="http://twitter.com/rentzsch" title="@rentzsch">@rentzsch</a> for luring me onto Twitter. Not once, but twice. The first time was as a requirement for attending the C4[2] conference. My initial run on Twitter came to a halt later when I famously (for some definition of famous) took a <a href="http://twitter.com/lapcat/statuses/1503216891" title="lapcat tweet 1503216891">5-month</a> <a href="http://twitter.com/lapcat/statuses/1503220251" title="lapcat tweet 1503220251">hiatus</a>. At C4[3], however, &#8216;Wolf&#8217; (as he is known in the porn industry) persuaded me to return to Twitter. You can see why C4 was evil and had to be abolished.</p>
<p>For a whale, err, while, Twitter was fairly stable, relatively speaking. Lately, though, the uptime has been absolutely horrid, even when there&#8217;s no World Cup game. The only thing that never fails is the <a href="http://status.twitter.com/" title="Twitter Status">status blog</a>. (Why don&#8217;t they make Twitter out of the status blog? Or the black box?) The Twitter developers don&#8217;t really inspire confidence; they keep pushing code changes that are supposed to improve reliability but end up causing outages. Furthermore, after all this time they&#8217;ve still failed to implement any convenient way to follow conversation threads on Twitter. The @reply is only the most rudimentary step in this direction. To me, that suggests the developers have completely missed what is important and special about Twitter, the social aspect.</p>
<p>Another threat to Twitter&#8217;s future, besides the continued technical suckage and danger of collapsing under its own weight, is the need to make money. We&#8217;ve grown accustomed to receiving this service for free, but Twitter is not a charity, it&#8217;s a for-profit corporation. We&#8217;re starting to see this manifested in &#8216;promoted&#8217; trends. Third-party spammers have existed on Twitter from the beginning, of course, but the question is whether commercialization will transform Twitter itself into something repulsive (cf. iAds). On the other hand, there&#8217;s a possibility that Twitter cannot make itself commercial enough. If Twitter doesn&#8217;t generate sufficient revenue to sustain itself and make a profit over the long term, it may have to close up shop and go out of business. It certainly wouldn&#8217;t be the first dot-com service to disappear. I believe that if Google wanted, they could create a serious competitor to Twitter, backed with Google&#8217;s superior knowledge, reliability, and bandwidth. Indeed, I&#8217;m surprised that Google hasn&#8217;t attempted this already. Maybe they don&#8217;t see any profit in it. But that doesn&#8217;t bode well for Twitter either.</p>
<p>As good programmers, we always have a backup. (Right? Right? Redundant and off-site, right?) The purpose of this post is to consider, what is our backup to Twitter? If Twitter dies, or if we&#8217;re compelled to leave, where do we go? We could go back to our pre-Twitter existence, back to our spouses, children, neighbors, and back to writing long blog posts instead of pithy 140- character tweets. We could, but blech! The un-tweeted life is not worth living. There would always be something missing. Twitter allows us to connect regularly with many friends that we don&#8217;t get to see very often (and to make arrangements when we do see them). It introduces us to people that we&#8217;d never meet or be able to talk to otherwise. It provides a unmoderated, unstructured forum for sharing crucial tidbits of information and knowledge with people we trust and respect (as well as everyone else, for better or worse). Twitter is like Cheers for the internet, except without Sam Malone. Oh wait, it&#8217;s got <a href="http://twitter.com/TedDanson" title="Ted Danson on Twitter">Sam Malone</a>. The problem is, we don&#8217;t have a good backup. Without Twitter, we&#8217;ve got nothing. (Please don&#8217;t even mention Gary&#8217;s Old Towne Tavern.)</p>
<p>Admittedly, there are some alternatives to Twitter, a few services that are similar, but they all suffer from the same inherent limitations as Twitter. They present a single point of failure. If Twitter&#8217;s servers are overloaded, then the service becomes unusable. Everyone gets the fail whale. In order to reliably handle the traffic, a centralized service require a large amount of resources, and thus money. So the only hope for a reliable, centralized Twitter-like service is heavily commercialization, and it&#8217;s not clear that a heavily commercialized service is one we&#8217;d want to spend our time on. Would a profit train be recognizable as the little Twitter we know and (sometimes) love? Besides, any centralized service is still prone to catastrophic failure. Even the mighty Amazon goes down.</p>
<p>So I had this idea. In the shower. (With Jennifer Aniston.) What if we made a distributed Twitter? Like DVCS. All the hip young programmers today use DVCS, whether that&#8217;s Git, or Mercurial, or &hellip; yeah, Git or Mercurial. The key to DVCS is that there&#8217;s no single point of failure, no centralized repository. Each working copy is its own repository, with the entire commit history. And you can commit locally! If one repository goes offline or disappears entirely, the other repositories can continue operating indefinitely without it. Nothing is lost. Similarly, each user of Dwitter (clever, huh?) would install Dwitter software on a web server, which would keep a record not only of the user&#8217;s own dweets but also the dweets of everyone the user follows.</p>
<p>When you follow someone, your Dwitter server would send a request containing information such as your name and server address to the Dwitter server of the person you follow. The followee&#8217;s Dwitter server stores this information, and then when that person dweets, his/her/its/their Dwitter server would send the dweet to the Dwitter servers of all followers. To prevent connection overload, the rate of sending out dweets to followers would be limited, perhaps to something like 1.5 followers per second. This would allow a dweet to be propagated to 5000 followers in less than an hour. And if you compose another dweet while your followers are still getting notified of the previous dweet, the dweet notifications would get consolidated for the followers who haven&#8217;t yet been notified. Furthermore, any Dwitter user directly mentioned in a dweet would receive immediate notification of the dweet, prior to anyone else, to facilitate quick conversation.</p>
<p>Obviously, this system won&#8217;t scale for someone who has a million Twitter followers. However, the only people who have a million Twitter followers are celebrities. There are plenty of other, better outlets for celebrity news, we don&#8217;t need Twitter for that. Note that celebrities did not make Twitter popular. Rather, celebrities came to Twitter because it was already popular. What makes the presence of celebrities on Twitter mostly useless is that they rarely participate in the culture of Twitter, they don&#8217;t foster conversation. You can&#8217;t have a conversation with a million followers, because that doesn&#8217;t scale either.</p>
<p>A potentially more serious problem for the distributed version of Twitter is the difficulty of use. Running your own Dwitter server requires a fair bit of technical knowledge. It&#8217;s much more difficult than going to a web site and signing up for an account. This may exclude mom and pop from joining Dwitter. However, I personally don&#8217;t consider the loss of mom and pop to be worse than the loss of celebrities. Frankly, I don&#8217;t want my mom following me on Twitter. Have you read my tweets? She&#8217;d make me wash my mouth out with soap! Almost all of the people I follow are programmers. They should be fully capable of setting up a Dwitter server on their own internet domain. Even though Twitter is completely public (except for protected tweets, you bastards), the irony is that it&#8217;s best suited for insulated groups such as programmers. 140 characters is not enough to teach outsiders your terminology and concepts. When you tweet, you&#8217;re forced to leave out a lot and to use shorthand that only likeminded people will understand. Tweeting is kind of like sending out coded messages. (<a href="http://twitter.com/IwayAmwayAwayUssianrayYspay" title="@IwayAmwayAwayUssianrayYspay">@IwayAmwayAwayUssianrayYspay</a>)</p>
<p>Even with the distributed system, there may be options for less technical users. WordPress operates under a similar model. You can install WordPress software on your own web server and run your blog yourself, or you can sign up to have a hosted blog on <a href="http://wordpress.com/" title="WordPress.com">wordpress.com</a>, where they take care of the technical aspects for you. The key is that even if one hosted Dwitter service fails, that would only prevent new dweets from the hosted accounts. The rest of the Dwitterverse would go on as usual, and the archives of the hosted dweet accounts would continue to exist on the Dwitter servers of their followers.</p>
<p>I don&#8217;t personally have the expertise to design a distributed Twitter-like service. I have some web knowledge, but I&#8217;m mainly a desktop programmer. Nonetheless, I will take all glory and riches arising from this idea. All I ask in return for all the glory and riches is that the designers of the new service don&#8217;t create a half-assed API. I&#8217;m tired of crappy, fatally flawed designs becoming popular by virtue of being first to market. Indeed, this is how Twitter itself became popular. Please, do it right the first time. Or I&#8217;ll be looking for an alternative to you too.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2010/07/09/a-world-without-twitter/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Safari extension: autocomplete</title>
		<link>http://lapcatsoftware.com/blog/2010/06/10/safari-extension-autocomplete/</link>
		<comments>http://lapcatsoftware.com/blog/2010/06/10/safari-extension-autocomplete/#comments</comments>
		<pubDate>Fri, 09 Jul 2010 13:40:19 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[HTML]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=243</guid>
		<description><![CDATA[Safari has a feature called form autocompletion, or AutoFill, that reads the username and password you type into a web form, saves them to your keychain, and automatically fills them in from the keychain the next time you visit the web form. This feature is completely opt-in: you can enable and disable it in Safari&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>Safari has a feature called form autocompletion, or AutoFill, that reads the username and password you type into a web form, saves them to your keychain, and automatically fills them in from the keychain the next time you visit the web form. This feature is completely opt-in: you can enable and disable it in Safari&#8217;s preferences, and even when it&#8217;s enabled, Safari will ask you before saving the username and password from each web form. Many other web browsers have a similar feature; it appears to have been introduced by Internet Explorer.</p>
<p>Unfortunately, a number of web sites (including my bank, for example) choose to disable autocompletion in a misguided attempt at security. Autocompletion can be disabled by using the attribute <code>autocomplete=off</code> in a web form. The idea behind disabling autocompletion seems to be that it leaves the account holder vulnerable to someone else accessing the computer and logging into the account. I believe that this is misguided for at least two reasons. First, autocompletion is opt-in, so the user can decide whether to save passwords on a particular machine. Anyone who chooses to save their passwords on a public terminal is an idiot. Indeed, anyone who logs in to their bank account on a public terminal deserves to be hacked and lose all their money, because who knows what manner of keyloggers or other malware could be running on the machine? I feel safe turning on AutoFill on my computer because I&#8217;m the only person who ever has access to it.</p>
<p>Another reason that disabling autocompletion is misguided is that it encourages the use of weak passwords. For security, I generate very long, random passwords for web sites and save them to my keychain. There&#8217;s no way I could memorize even one of my web site passwords, much less all of them. It&#8217;s difficult for almost anyone to memorize a bunch of web site passwords. Disabling autocompletion forces the user to type them in manually every time, and this encourages the use of short, easy to remember passwords. Worse, it encourages password sharing among different web sites. Thus, if an attacker can guess or brute-force one password, the attacker suddenly has access of all of a persons&#8217;s web site accounts. That&#8217;s terrible security.</p>
<p>WebKit, the web engine underlying Safari, respects the <code>autocomplete</code> attribute, and there&#8217;s no preference or API to make WebKit ignore the attribute. However, I discovered an excellent script written by Michael Kisor called <a href="http://magicpubs.com/mac/software/autocomplete/" title="Autocomplete Always On!">Autocomplete Always On!</a> that actually patches the WebKit framework itself on your system so that it ignores <code>autocomplete</code>. It works by changing one byte in the file
<pre><code>/System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/WebCore</code></pre>
<p> transforming the string <code>autocomplete</code> into <code>xutocomplete</code>. After that change, WebKit looks for <code>xutocomplete=off</code> but never finds it in the web form, which means autocomplete never gets disabled. WebKit is <a href="https://trac.webkit.org/browser" title="WebKit svn root">open source</a>, so we can verify the consequences of the patch.</p>
<p>The only downside of the Autocomplete Always On! script is that it needs to be re-run after any software update to the WebKit framework on your system. I&#8217;ve been using the script for years with no trouble &hellip; until Safari 5. After installing Safari 5, I discovered that the script was no longer effective in re-enabling autocompletion. This was no flaw in the script, however. Autocomplete Always On! still works as designed on the version of WebKit shipped with Safari 5. The problem is that the Safari 5 binary itself seems to include a new check for the <code>autocomplete</code> attribute in web forms. I&#8217;ve verified this behavior in the debugger. If the form contains <code>autocomplete=off</code>, then Safari 5 never calls the (private) WebKit method <code>-[WebHTMLRepresentation elementDoesAutoComplete:]</code>, so Safari doesn&#8217;t even ask WebKit whether autocomplete is enabled for the web form element. It is possible to patch the Safari binary just like the WebCore binary, after which Safari 5 will call <code>-[WebHTMLRepresentation elementDoesAutoComplete:]</code>, making the WebCore patch effective again. Unfortunately, patching the Safari binary breaks codesigning for the application, and the keychain uses codesigning to determine whether an application can access saved passwords.</p>
<p>That&#8217;s the bad news. The good news is that Safari 5 also introduced extensions. A Safari extension is like a plug-in, because it can run code inside the browser, but unlike a plug-in, an extension doesn&#8217;t run native C or Objective-C code but rather HTML or Javascript code. For example, an extension can specify some Javascript to run on page load. I found a <a href="http://userscripts.org/scripts/review/68645" title="Source for &quot;re-enable_password_manager&quot; &ndash; Userscripts.org">script</a> written by Andreas Huber that removes any <code>autocomplete</code> attributes from a web form, and I cleaned it up a bit for inclusion in a Safari extension. With my autocomplete extension installed, you don&#8217;t have to patch WebKit or Safari, because the <code>autocomplete</code> attributes are simply removed from the web page before the browser checks for their existence.</p>
<p>I&#8217;m making my autocomplete Safari extension available &mdash; in September. No, today! You can download the extension <a href="http://lapcatsoftware.com/downloads/autocomplete.safariextz" title="autocomplete Safari extension">here</a>. To install the extension on your computer, you first need to check &#8220;Show Develop menu in menu bar&#8221; at the bottom of Safari&#8217;s Advanced preferences. Then in the Develop menu, you need to check &#8220;Enable Extensions&#8221;. After extensions are enabled, you can simply double-click the Safari extension in Finder to install.</p>
<p>Enjoy! In lieu of donations, I am accepting sexual favors. Or an icon.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2010/06/10/safari-extension-autocomplete/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>Beware DIY component replacement</title>
		<link>http://lapcatsoftware.com/blog/2010/02/17/beware-diy-component-replacement/</link>
		<comments>http://lapcatsoftware.com/blog/2010/02/17/beware-diy-component-replacement/#comments</comments>
		<pubDate>Mon, 15 Mar 2010 14:47:52 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=240</guid>
		<description><![CDATA[I own a 17-inch 2.33GHz Intel Core 2 Duo MacBook Pro. When I purchased it, I chose the largest hard drive available, the 200GB 4200rpm option. Over the years, that hard drive had become quite full, and it&#8217;s also very slow compared to newer hard drive models. Thus, I decided recently to replace it with [...]]]></description>
			<content:encoded><![CDATA[<p>I own a <a href="http://support.apple.com/kb/SP24" title="MacBook Pro (Late 2006) - Technical Specifications">17-inch 2.33GHz Intel Core 2 Duo MacBook Pro</a>. When I purchased it, I chose the largest hard drive available, the 200GB 4200rpm option. Over the years, that hard drive had become quite full, and it&#8217;s also very slow compared to newer hard drive models. Thus, I decided recently to replace it with a <a href="http://www.seagate.com/www/en-us/products/laptops/momentus/momentus_7200/" title="Seagate Momentus 7200rpm">500GB 7200rpm Seagate Momentus</a> hard drive.</p>
<p>My original plan was to pay a local computer repair shop to swap the drives. I&#8217;m a software guy, not a hardware guy, and my time and sanity are (somewhat) valuable. However, some people who shall remain nameless shamed me into doing it myself, arguing that a proper computer geek should be able to replace components easily. (Sure, right, just like the midplane was a &#8216;user-serviceable&#8217; part of the iMac G5.)</p>
<p>I found several online videos with instructions for replacing my MacBook Pro&#8217;s hard drive. For example, Other World Computing has a video <a href="http://eshop.macsales.com/installvideos/macbookpro_17_hd_h/" title="MacBook Pro Hard Drive (High Quality) Installation Video at OtherWorldComputing.com">here</a>, which specifically mentions my model: MacBookPro2,1. After studying the instructions carefully, I set about to do it myself</p>
<p>The first problem was that the OWC video mentions the requirement of a #00 Phillips screwdriver, which I did not have but which I acquired for the purpose of this hard drive replacement. Nonetheless, it turns out that the #00 was not actually the right size for the screws. Fortunately, I did already happen to have a screwdriver that fit the screws in my MacBook Pro. Otherwise, I would have been forced to scrub the replacement at the start.</p>
<p>This screwdriver mixup was but a minor blip compared to the next and worst problem. The videos completely failed to show or mention that there was a very short and easily snapped wire on the far left side of the machine, running from the bottom board to the top case. I did not discover that this wire existed until I opened the top case, and after a few seconds of attempting to lift the top case, the wire indeed snapped.</p>
<p>Here&#8217;s a photo of the broken black wire:<br />
<img src="http://lapcatsoftware.com/downloads/img/broken-wire.jpg" alt="Photo of broken black wire"/></p>
<p>And here&#8217;s a photo of where it was attached on the top case:<br />
<img src="http://lapcatsoftware.com/downloads/img/broken-connection.jpg" alt="Photo of top case"/></p>
<p>At the time, I had no idea what the wire was for. I had to take the machine in to the genius bar at my local Apple Store. The genius examined it and informed me, to my great relief, that the wire was for the built-in microphone. My microphone is now broken and inoperative, but it could have been much worse.</p>
<p>Another thing the videos failed to mention is that the metal tabs with screw holes on the top case are extremely fragile. When I put the top case back on, one of the tabs broke off. It&#8217;s not a big deal, there are enough other screws around the case to keep it securely in place, but it&#8217;s annoying, and there is a little area of the bottom case on the right side that is sticking out slightly and bent.</p>
<p>In the end, I&#8217;m happy with my new hard drive. My advice, though, is to pay a professional to perform the replacement, don&#8217;t try to do it yourself. Ignore the DIY demons whispering in your ear. I don&#8217;t think so, Tim.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2010/02/17/beware-diy-component-replacement/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Local variables are free</title>
		<link>http://lapcatsoftware.com/blog/2009/12/19/local-variables-are-free/</link>
		<comments>http://lapcatsoftware.com/blog/2009/12/19/local-variables-are-free/#comments</comments>
		<pubDate>Sat, 06 Feb 2010 16:30:13 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=236</guid>
		<description><![CDATA[This is part II of my irregularly scheduled series on compiler optimization. In part I, I explained how the compiler can optimize away return statements, resulting in missed breakpoints. My given workaround to that problem, though effective, was very ugly and architecture-dependent, much like Cowboys Stadium. (gdb) break *0x00001fc5 if $eax != 0 Although there&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>This is part II of my irregularly scheduled series on compiler optimization. In <a href="http://lapcatsoftware.com/blog/2009/11/16/why-did-my-breakpoint-not-get-hit/" title="Why did my breakpoint not get hit?">part I</a>, I explained how the compiler can optimize away <code>return</code> statements, resulting in missed breakpoints. My given workaround to that problem, though effective, was very ugly and architecture-dependent, much like Cowboys Stadium.</p>
<pre><code>(gdb) break *0x00001fc5 if $eax != 0</code></pre>
<p>Although there&#8217;s not much we can do to prevent the compiler optimization, we can greatly simplify our conditional breakpoint. I had suggested rewriting the source code, which was awe-inspiringly prescient, because that&#8217;s what I&#8217;m going to do now. Here&#8217;s the original code:</p>
<pre><code>8	if (ShouldReturn())
9		return;</code></pre>
<p>And here&#8217;s the revised code:</p>
<pre><code>8	int localVar = ShouldReturn();
9	if (localVar)
10		return;</code></pre>
<p>The <code>return</code> at line 10 will still be optimized away. However, the revised code allows us to set a simple breakpoint at line 9 that will stop when we want:</p>
<pre><code>(gdb) break 9 if localVar != 0</code></pre>
<p>No knowledge of the architecture, machine registers, or assembly language is required.</p>
<p>From the beginning of time (January 1970, of course), programmers have struggled over coding style. Objective-C programmers, for example, expend undue effort arranging their brackets. (I have <code>[NSMutableArray array]</code> going to the Final Four.) For some, bracket-making becomes a kind of game or contest.</p>
<pre><code>[[[[[[[[[[[[[See how] many] method] calls] we] can] fit] on] one] line] of] source] code];</code></pre>
<p>I&#8217;ve changed my coding style over the years, but I&#8217;ve settled on one fundamental principle: write your code so that it&#8217;s easy to debug. All your fancy margin-aligning isn&#8217;t going to help when you need to figure out why your app keeps exploding. If you have nested method calls on one line of code, you can&#8217;t easily set a breakpoint in the middle. That&#8217;s why I prefer as much as possible to have only one method call per line of code, and create a local variable to store the return value.</p>
<p>There is a misconception that local variables are expensive, in terms of either computation or memory. The truth is that local variables are very cheap, the value meals of the computing world. (Would you like trans fat with your saturated fat?) It only takes one machine instruction to store a pointer address to a local variable. One machine instruction is really quite fast, about as fast as you can get &mdash; at least with restrictor plates. With regard to memory, local variables only take up stack space. To create a local variable, you simply move the stack a little. When the method or function returns, the stack is moved back, and thereby the space reserved for local variables is automatically recovered. Of course, you don&#8217;t want to create large C arrays on the stack, but a pointer to an Objective-C object only takes 4 bytes on the stack for 32-bit, 8 bytes for 64-bit. The default 32-bit stack size is 8MB, so you&#8217;re not going to run out of space unless you have deeply recursive calls.</p>
<p>Even these small costs are only relevant in the context of your app&#8217;s unoptimized, debug configuration. For your customers, on the other hand, local variables are free. As in Mumia, or Bird. When you compile your app using the release configuration, the local variables disappear, the compiler optimizes them away. (By the way, this is one of the reasons that debugging the release build of your app can be a frustrating and/or wacky experience.) To see the optimization in action, let&#8217;s consider some sample code:</p>
<pre><code>1  #import &lt;Foundation/Foundation.h&gt;
2
3  @interface MyObject : NSObject {}
4  @end
5
6  @implementation MyObject
7
8  -(NSString *)myDirectProcessName {
9  	return [[[NSProcessInfo processInfo] processName] lowercaseString];
10 }
11
12 -(NSString *)myRoundaboutProcessName {
13 	NSString *myRoundaboutProcessName = nil;
14 	NSProcessInfo *processInfo = [NSProcessInfo processInfo];
15 	NSString *processName = [processInfo processName];
16 	NSString *lowercaseString = [processName lowercaseString];
17 	myRoundaboutProcessName = lowercaseString;
18 	return myRoundaboutProcessName;
19 }
20
21 @end
22
23 int main(int argc, const char *argv[]) {
24 	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
25 	MyObject *myObject = [[[MyObject alloc] init] autorelease];
26 	NSLog(@"My direct process name: %@", [myObject myDirectProcessName]);
27 	NSLog(@"My roundabout process name: %@", [myObject myRoundaboutProcessName]);
28 	[pool release];
29 	return 0;
30 }</code></pre>
<p>The above code is obviously contrived and useless. It only has value for explanatory purposes, and perhaps in the app store for $0.99. The methods <code>-myRoundaboutProcessName</code> and <code>-myDirectProcessName</code> do the same thing, the former with and the latter without local variables. Here&#8217;s the i386 disassembly for the methods when compiled using the debug configuration:</p>
<pre><code>-[MyObject myDirectProcessName]:
00001d2a	nop
00001d2b	nop
00001d2c	nop
00001d2d	nop
00001d2e	nop
00001d2f	nop
00001d30	pushl	%ebp
00001d31	movl	%esp,%ebp
00001d33	pushl	%ebx
00001d34	subl	$0x14,%esp
00001d37	calll	0x00001d3c
00001d3c	popl	%ebx
00001d3d	leal	0x000012e8(%ebx),%eax
00001d43	movl	(%eax),%eax
00001d45	movl	%eax,%edx
00001d47	leal	0x000012e4(%ebx),%eax
00001d4d	movl	(%eax),%eax
00001d4f	movl	%eax,0x04(%esp)
00001d53	movl	%edx,(%esp)
00001d56	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d5b	movl	%eax,%edx
00001d5d	leal	0x000012e0(%ebx),%eax
00001d63	movl	(%eax),%eax
00001d65	movl	%eax,0x04(%esp)
00001d69	movl	%edx,(%esp)
00001d6c	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d71	movl	%eax,%edx
00001d73	leal	0x000012dc(%ebx),%eax
00001d79	movl	(%eax),%eax
00001d7b	movl	%eax,0x04(%esp)
00001d7f	movl	%edx,(%esp)
00001d82	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d87	addl	$0x14,%esp
00001d8a	popl	%ebx
00001d8b	leave
00001d8c	ret
-[MyObject myRoundaboutProcessName]:
00001d8d	nop
00001d8e	nop
00001d8f	nop
00001d90	nop
00001d91	nop
00001d92	nop
00001d93	pushl	%ebp
00001d94	movl	%esp,%ebp
00001d96	pushl	%ebx
00001d97	subl	$0x24,%esp
00001d9a	calll	0x00001d9f
00001d9f	popl	%ebx
00001da0	movl	$0x00000000,0xe8(%ebp)
00001da7	leal	0x00001285(%ebx),%eax
00001dad	movl	(%eax),%eax
00001daf	movl	%eax,%edx
00001db1	leal	0x00001281(%ebx),%eax
00001db7	movl	(%eax),%eax
00001db9	movl	%eax,0x04(%esp)
00001dbd	movl	%edx,(%esp)
00001dc0	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dc5	movl	%eax,0xec(%ebp)
00001dc8	movl	0xec(%ebp),%edx
00001dcb	leal	0x0000127d(%ebx),%eax
00001dd1	movl	(%eax),%eax
00001dd3	movl	%eax,0x04(%esp)
00001dd7	movl	%edx,(%esp)
00001dda	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001ddf	movl	%eax,0xf0(%ebp)
00001de2	movl	0xf0(%ebp),%edx
00001de5	leal	0x00001279(%ebx),%eax
00001deb	movl	(%eax),%eax
00001ded	movl	%eax,0x04(%esp)
00001df1	movl	%edx,(%esp)
00001df4	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001df9	movl	%eax,0xf4(%ebp)
00001dfc	movl	0xf4(%ebp),%eax
00001dff	movl	%eax,0xe8(%ebp)
00001e02	movl	0xe8(%ebp),%eax
00001e05	addl	$0x24,%esp
00001e08	popl	%ebx
00001e09	leave
00001e0a	ret</code></pre>
<p>As expected, <code>-myRoundaboutProcessName</code> makes more room on the stack than <code>-myDirectProcessName</code>:</p>
<pre><code>00001d34	subl	$0x14,%esp
00001d97	subl	$0x24,%esp</code></pre>
<p>At <code>00001da0</code>, <code>-myRoundaboutProcessName</code> sets the value of the local variable to <code>nil</code>, as in line 13 of the source code. The interesting differences, though, are immediately after the calls to <code>objc_msgSend()</code>. By the standard ABI, the register <code>eax</code> contains the return value of <code>objc_msgSend()</code>. In <code>-myDirectProcessName</code>, the value in <code>eax</code> is simply moved to the register <code>edx</code>:</p>
<pre><code>00001d5b	movl	%eax,%edx</code></pre>
<p>In contrast, <code>-myRoundaboutProcessName</code> first stores the value on the stack before moving it to <code>edx</code>. The address on the stack is the space reserved for the local variable:</p>
<pre><code>00001dc5	movl	%eax,0xec(%ebp)
00001dc8	movl	0xec(%ebp),%edx</code></pre>
<p>After the final <code>objc_msgSend()</code> call, <code>-myDirectProcessName</code> doesn&#8217;t bother to do much, because the return value in <code>eax</code> will become the return value of the whole method. In <code>-myRoundaboutProcessName</code>, it needs to store values in local variables as in lines 16 and 17 of the source code:</p>
<pre><code>00001df9	movl	%eax,0xf4(%ebp)
00001dfc	movl	0xf4(%ebp),%eax
00001dff	movl	%eax,0xe8(%ebp)
00001e02	movl	0xe8(%ebp),%eax</code></pre>
<p>So that&#8217;s how the methods differ in the unoptimized build. Now let&#8217;s see what happens when we use the release configuration. Here&#8217;s the optimized disassembly for <code>-myDirectProcessName</code>:</p>
<pre><code>-[MyObject myDirectProcessName]:
00001dce	pushl	%ebp
00001dcf	movl	%esp,%ebp
00001dd1	subl	$0x18,%esp
00001dd4	movl	0x00003000,%eax
00001dd9	movl	%eax,0x04(%esp)
00001ddd	movl	0x0000302c,%eax
00001de2	movl	%eax,(%esp)
00001de5	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dea	movl	0x00003004,%edx
00001df0	movl	%edx,0x04(%esp)
00001df4	movl	%eax,(%esp)
00001df7	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dfc	movl	0x00003008,%edx
00001e02	movl	%edx,0x0c(%ebp)
00001e05	movl	%eax,0x08(%ebp)
00001e08	leave
00001e09	jmpl	0x0000400a	; symbol stub for: _objc_msgSend</code></pre>
<p>The optimized method is significantly shorter, as expected from the compiler option <code>-Os</code>. First, you&#8217;ll notice that all those pesky <code>nop</code> instructions have been deleted. Stallman put them in unoptimized builds just to annoy us. (Or they may have been for Fix and Continue, but I always assume the worst.) There are additional optimizations as well that I won&#8217;t belabor here, because I&#8217;m eager to get to the climax. (Sorry, dear.) For your enlightenment and enjoyment, here&#8217;s the optimized disassembly for <code>-myRoundaboutProcessName</code>:</p>
<pre><code>-[MyObject myRoundaboutProcessName]:
00001e0e	pushl	%ebp
00001e0f	movl	%esp,%ebp
00001e11	subl	$0x18,%esp
00001e14	movl	0x00003000,%eax
00001e19	movl	%eax,0x04(%esp)
00001e1d	movl	0x0000302c,%eax
00001e22	movl	%eax,(%esp)
00001e25	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001e2a	movl	0x00003004,%edx
00001e30	movl	%edx,0x04(%esp)
00001e34	movl	%eax,(%esp)
00001e37	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001e3c	movl	0x00003008,%edx
00001e42	movl	%edx,0x0c(%ebp)
00001e45	movl	%eax,0x08(%ebp)
00001e48	leave
00001e49	jmpl	0x0000400a	; symbol stub for: _objc_msgSend</code></pre>
<p>Identical! Ah, that&#8217;s nice. Smoke &#8216;em if you got &#8216;em.</p>
<p>In conclusion, feel free to sprinkle, pepper, dash, or even drown your code with local variables. And with the engineering hours of debugging time you save, get me a nice (not free) present. I&#8217;m partial to flavored coffee and unflavored MacBooks.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2009/12/19/local-variables-are-free/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Why did my breakpoint not get hit?</title>
		<link>http://lapcatsoftware.com/blog/2009/11/16/why-did-my-breakpoint-not-get-hit/</link>
		<comments>http://lapcatsoftware.com/blog/2009/11/16/why-did-my-breakpoint-not-get-hit/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 17:04:33 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[C]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=228</guid>
		<description><![CDATA[This is part I of a II+ (take that, trademark trolls) part series on compiler optimization. For the gcc compiler, you can specify the level of optimization with various -O options. The default for compiling is -O0, which means do not optimize. As we shall see, however, the compiler always optimizes to an extent. That [...]]]></description>
			<content:encoded><![CDATA[<p>This is part I of a II+ (take that, trademark trolls) part series on compiler optimization. For the <code>gcc</code> compiler, you can specify the level of optimization with various <code>-O</code> options. The default for compiling is <code>-O0</code>, which means do not optimize. As we shall see, however, the compiler always optimizes to an extent. That is to say, <code>gcc -O0</code>, you lie!</p>
<p>The primary reason for using the <code>-O0</code> option (besides to avoid compiler optimization bugs) is to facilitate debugging of your code. With higher levels of optimization, the compiler is given more freedom to &#8216;ignore&#8217; your source code in writing machine instructions, as long as the results are the same. Although it is possible to debug optimized binaries, the experience is often confusing and unhelpful for the programmer (much like reading cocoa-dev). Turning off optimization gives the closest correlation between source code and machines instructions. Yet even with no optimization, the correlation is not perfect, and this can lead to debugging problems.</p>
<p>Let&#8217;s consider a simple example:</p>
<pre><code>$ cat &gt; returnbreak.c
#include &lt;stdio.h&gt;

int ShouldReturn(void) {
	return 1;
}

void HelloWorld(void) {
	if (ShouldReturn())
		return;

	printf("Hello, World!\n");
}

int main(int argc, const char *argv[]) {
	HelloWorld();
	return 0;
}
$ gcc -g -O0 -o returnbreak returnbreak.c
$ gdb returnbreak
GNU gdb 6.3.50-20050815 (Apple version gdb-966) (Tue Mar 10 02:43:13 UTC 2009)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin"...Reading symbols for shared libraries ... done

(gdb) list HelloWorld
2
3	int ShouldReturn(void) {
4		return 1;
5	}
6
7	void HelloWorld(void) {
8		if (ShouldReturn())
9			return;
10
11		printf("Hello, World!\n");
(gdb) break 9
Breakpoint 1 at 0x1fc9: file returnbreak.c, line 9.
(gdb) run
Starting program: /Users/jeff/Desktop/returnbreak
Reading symbols for shared libraries ++. done

Program exited normally.</code></pre>
<p>WTF?!? Why did my breakpoint not get hit?</p>
<pre><code>(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x00001fc9 in HelloWorld at returnbreak.c:9</code></pre>
<p>Hmm, that seems ok. Let&#8217;s try something else.</p>
<pre><code>(gdb) break HelloWorld
Breakpoint 2 at 0x1fc0: file returnbreak.c, line 8.
(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x00001fc9 in HelloWorld at returnbreak.c:9
2   breakpoint     keep y   0x00001fc0 in HelloWorld at returnbreak.c:8
(gdb) run
Starting program: /Users/jeff/Desktop/returnbreak 

Breakpoint 2, HelloWorld () at returnbreak.c:8
8		if (ShouldReturn())
(gdb) c
Continuing.

Program exited normally.</code></pre>
<p>Odd, it hits the breakpoint at line 8 but not at line 9. The breakpoint on line 9 is at address <code>0x00001fc9</code>, so let&#8217;s look at the (i386) disassembly for that:</p>
<pre><code>(gdb) disassemble 0x00001fc9
Dump of assembler code for function HelloWorld:
0x00001fb3 &lt;HelloWorld+0&gt;:	push   %ebp
0x00001fb4 &lt;HelloWorld+1&gt;:	mov    %esp,%ebp
0x00001fb6 &lt;HelloWorld+3&gt;:	push   %ebx
0x00001fb7 &lt;HelloWorld+4&gt;:	sub    $0x14,%esp
0x00001fba &lt;HelloWorld+7&gt;:	call   0x1fbf &lt;HelloWorld+12&gt;
0x00001fbf &lt;HelloWorld+12&gt;:	pop    %ebx
0x00001fc0 &lt;HelloWorld+13&gt;:	call   0x1fa6 &lt;ShouldReturn&gt;
0x00001fc5 &lt;HelloWorld+18&gt;:	test   %eax,%eax
0x00001fc7 &lt;HelloWorld+20&gt;:	jne    0x1fd7 &lt;HelloWorld+36&gt;
0x00001fc9 &lt;HelloWorld+22&gt;:	lea    0x30(%ebx),%eax
0x00001fcf &lt;HelloWorld+28&gt;:	mov    %eax,(%esp)
0x00001fd2 &lt;HelloWorld+31&gt;:	call   0x3005 &lt;dyld_stub_puts&gt;
0x00001fd7 &lt;HelloWorld+36&gt;:	add    $0x14,%esp
0x00001fda &lt;HelloWorld+39&gt;:	pop    %ebx
0x00001fdb &lt;HelloWorld+40&gt;:	leave
0x00001fdc &lt;HelloWorld+41&gt;:	ret
End of assembler dump.</code></pre>
<p>When <code>ShouldReturn()</code> returns, the return value is in the register <code>eax</code>. The <code>test</code> instruction at <code>0x00001fc5</code> performs a bitwise AND of the two operands &mdash; which in this case are the same. If the result is non-zero &mdash; and in this case the result is 1 &mdash; the Zero Flag in the <code>EFLAGS</code> register is set to 0. This instruction corresponds to evaluating the conditional on line 8 of our source code. Then the <code>jne</code> instruction at <code>0x00001fc7</code> jumps to a certain address if the Zero Flag is 0. In our source code, the flow of control should move to the <code>return</code> statement on line 9 when the conditional evaluates to non-zero. According to the machine instructions, on the other hand, it jumps to <code>0x1fd7</code> when the conditional evaluates to non-zero. This address is the beginning of the standard function epilog, which restores the stack and registers to their previous state before returning.</p>
<p>The problem here is that while the function <code>HelloWorld()</code> has two exit points in our source code, it only has one exit point in the machine instructions. In essence, the compiler has optimized for size, despite our use of the <code>-O0</code> option. Given the generated machine instructions, there is nowhere to put a breakpoint that will only be hit when the conditional at line 8 evaluates to non-zero. A breakpoint at <code>0x00001fc5</code> or <code>0x00001fc7</code> would be hit whenever the conditional is evaluated, which is always. A breakpoint at <code>0x00001fd7</code> would be hit whenever the function returns, which is always as well. Unfortunately, <code>gdb</code> places the breakpoint at <code>0x00001fc9</code>, which is actually the opposite of what we intended, because it only gets hit when the conditional evaluates to zero. This is why the program exits normally without ever hitting the breakpoint. I consider this to be a bug in <code>gdb</code>; it would be better, I think, if it would just fail and give an error when we try to set the breakpoint. Of course, it may be a bug in <code>gcc</code> that it optimizes away our multiple exit points with optimization off. But hey, what do you expect from free software?</p>
<p>There are several workarounds for this problem. One would be to re-write your source code. (No, that&#8217;s not a joke. See Part II of this series.) Another workaround, if you only want to break on the result of a conditional, is to use a conditional breakpoint:</p>
<pre><code>(gdb) delete break
Delete all breakpoints? (y or n) y
(gdb) break *0x00001fc5 if $eax != 0
Breakpoint 1 at 0x1fc5: file returnbreak.c, line 8.
(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x00001fc5 in HelloWorld at returnbreak.c:8
	stop only if $eax != 0
(gdb) run
Starting program: /Users/jeff/Desktop/returnbreak 

Breakpoint 1, 0x00001fc5 in HelloWorld () at returnbreak.c:8
8		if (ShouldReturn())
(gdb) c
Continuing.

Program exited normally.</code></pre>
<p>To summarize, if you find that your breakpoints are not getting hit, you now know who to blame. Namely, yourself. It&#8217;s almost certain that your Xcode project settings are wrong.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2009/11/16/why-did-my-breakpoint-not-get-hit/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Gruber does a snow job on Snow Leopard</title>
		<link>http://lapcatsoftware.com/blog/2009/09/05/gruber-does-a-snow-job-on-snow-leopard/</link>
		<comments>http://lapcatsoftware.com/blog/2009/09/05/gruber-does-a-snow-job-on-snow-leopard/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 17:03:30 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[Apple]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=225</guid>
		<description><![CDATA[John Gruber holds Apple blameless for shipping Mac OS X 10.6.0 with an outdated version of Adobe Flash Player, a version with known vulnerabilities that have been exploited in the wild. The essence of his absolution is the following timeline: &#8220;Adobe released version 10.0.32.18 of Flash on July 30. Snow Leopard went GM on Friday [...]]]></description>
			<content:encoded><![CDATA[<p>John Gruber <a href="http://daringfireball.net/2009/09/flash_snow_leopard" title="Regarding the Brouhaha Over the Version of Flash in Snow Leopard">holds Apple blameless</a> for shipping Mac OS X 10.6.0 with an outdated version of Adobe Flash Player, a version with known vulnerabilities that have been exploited in the wild. The essence of his absolution is the following timeline: &#8220;Adobe released version 10.0.32.18 of Flash on July 30. Snow Leopard went GM on Friday August 7&#8243;.</p>
<p>It is true that Adobe released Flash Player 10.0.32.18 to the public on July 30. However, with regard to the version of Flash Player included with Snow Leopard, that date is largely irrelevant. On July 21, Adobe released a <a href="http://blogs.adobe.com/psirt/2009/07/potential_adobe_reader_and_fla.html" title="Potential Adobe Reader, Acrobat, and Flash Player issue">security bulletin</a> to the public: &#8220;Adobe is aware of reports of a potential vulnerability in Adobe Reader and Acrobat 9.1.2 and Adobe Flash Player 9 and 10.&#8221; The next day, there was a public <a href="http://blogs.adobe.com/psirt/2009/07/update_on_adobe_reader_acrobat.html" title="Update on Adobe Reader, Acrobat and Flash Player Issue">follow-up</a>: &#8220;A critical vulnerability exists in the current versions of Flash Player (v9.0.159.0 and v10.0.22.87) for Windows, Macintosh and Linux operating systems, and the authplay.dll component that ships with Adobe Reader and Acrobat v9.x for Windows, Macintosh and UNIX operating systems. This vulnerability (CVE-2009-1862) could cause a crash and potentially allow an attacker to take control of the affected system. There are reports that this vulnerability is being actively exploited in the wild via limited, targeted attacks against Adobe Reader v9 on Windows. We are in the process of developing a fix for the issue, and expect to provide an update for Flash Player v9 and v10 for Windows, Macintosh, and Linux by July 30, 2009&#8243;.</p>
<p>It is crucial to note that these were all public releases. Since Apple ships Adobe Flash Player with Mac OS X, Apple and Adobe undoubtedly have a private relationship. One would presume that Adobe privately discloses security vulnerabilities in the OS X version of Flash Player to Apple in advance of public announcements and that Adobe privately provides Apple with versions of Flash Player to test on OS X prior to public release. If these things do not occur privately, then both companies ought to be blamed for failing to follow industry best practices.</p>
<p>I&#8217;m not sure where Gruber gets the August 7 date. (Perhaps an email from Phil?) Snow Leopard build 10A432 was seeded to eligible ADC members on August 12. If Apple had already declared build 10A432 the GM before seeding it to developers for testing, that would be completely irresponsible (though sadly, not unprecedented). In any case, if the 10A432 seed had turned up a show-stopping bug, Apple could have un-declared it GM. Is allowing an attacker to take control of a system via a web browser not a show-stopper? Gruber asks, &#8220;Should Apple have postponed Snow Leopard for another month?&#8221; Despite the rhetorical nature of the question, I&#8217;ll answer: maybe they should have. Or at least, they should have postponed it 4 days. At WWDC, Apple told us that Snow Leopard would be released in September. Last time I checked, August 28 is not in September. Even delaying Snow Leopard a month would have allowed Apple to ship on time, in September.</p>
<p>Setting aside these more relevant dates, let&#8217;s just accept Gruber&#8217;s 8-day window for the sake of argument. &#8220;Does anyone really think that Apple should have replaced the single-crashiest piece of software in Mac OS X with a new untested version just eight days before going GM?&#8221; Yes, I do. We&#8217;re not talking about a major update here &mdash; obviously Apple should not switch from Flash 9 to Flash 10 eight days before GM. Apple had already pulled the trigger on Flash 10 for Snow Leopard. When security vulnerabilities come to light, fixes must be released quickly. Eight days of testing the update from Flash Player 10.0.22.87 to 10.0.32.18 really should have been sufficient for Apple. If a critical vulnerability was discovered in Mac OS X, Apple should be able to ship a fix within 8 days, if not sooner. Indeed, there were 8 days between Adobe&#8217;s security bulletin and the release of the updated Flash Player.</p>
<p>Apple&#8217;s record for shipping timely fixes to security vulnerabilities is poor. For example, Apple &#8216;distinguished&#8217; themselves by being the only vendor in the world to fail to join the coordinated effort to release a fix for the Kaminsky DNS vulnerability on all platforms on the same day. Instead, Apple took its own sweet time, which was particularly egregious for Mac OS X Server customers. (Though I use OS X on my personal computers, I&#8217;m glad my web host does not use OS X on their servers.) I personally was aware of the &#8216;Safari RSS&#8217; vulnerability for months while it remained unpatched and could have easily exploited any visitor to my web site if I had wanted. Fortunately for you, I&#8217;m not malicious. Not criminally malicious, anyway.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2009/09/05/gruber-does-a-snow-job-on-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Apple hot-swapped Mac OS X 10.5.8</title>
		<link>http://lapcatsoftware.com/blog/2009/09/01/apple-hot-swapped-mac-os-x-10-5-8/</link>
		<comments>http://lapcatsoftware.com/blog/2009/09/01/apple-hot-swapped-mac-os-x-10-5-8/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 17:02:49 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=223</guid>
		<description><![CDATA[There has been some confusion in the net-o-sphere over the existence of two Mac OS X 10.5.8 builds: 9L30 and 9L31a. I think it&#8217;s time to clear up that confusion, now that Max OS X 10.6.0 has been released and nobody cares anymore about 10.5.x. The Max OS X build version is stored in the [...]]]></description>
			<content:encoded><![CDATA[<p>There has been some confusion in the net-o-sphere over the existence of two Mac OS X 10.5.8 builds: 9L30 and 9L31a. I think it&#8217;s time to clear up that confusion, now that Max OS X 10.6.0 has been released and nobody cares anymore about 10.5.x.</p>
<p>The Max OS X build version is stored in the following file on your hard drive:</p>
<pre><code>/System/Library/CoreServices/SystemVersion.plist</code></pre>
<p>On my machine, the build is 9L30. I installed 10.5.8, as usual, via the combo updater (because I&#8217;m a paranoid superfreak who also repairs permissions and offers sacrifices to Demeter). This was a prudent three days after 10.5.8 was released, though the combo updater was downloaded two days after release.</p>
<p>Lately (in the Holocene epoch), the Mac OS X installers have come in &#8216;flat&#8217; package format (i.e, &#8220;smooth and even; without marked lumps or indentations&#8221; or &#8220;lacking interest or emotion; dull and lifeless&#8221;). This makes them opaque to the casual observer. Fortunately, I am Klondike Kat. The installer <code>.pkg</code> file is actually a <code>xar</code> archive that can be read and extracted with <code>/usr/bin/xar</code>. To extract the package contents into the current working directory:</p>
<pre><code>$ xar -xf /Volumes/Mac\ OS\ X\ Update\ Combined/MacOSXUpdCombo10.5.8.pkg</code></pre>
<p>This gives us an &#8216;old-style&#8217; (Pleistocene) <code>.pkg</code> file whose contents we can view in Finder. The package contains, among other things, a <code>Payload</code>. Be careful not to ignite it, otherwise you may require intensive care, if not AppleCare. The <code>Payload</code> is a <code>gzip</code> archive, so I slapped a <code>.gz</code> extension on the file and <code>gunzip</code>&#8216;ed it. After extracting that archive, you&#8217;re left with &hellip; yet another archive. (Apparently the Matryoshka method of software distribution.) The new <code>Payload</code> can be read and extracted with <code>/bin/pax</code>:</p>
<pre><code>$ pax -f Payload -r *SystemVersion.plist</code></pre>
<p>The <code>SystemVersion.plist</code> from my original installer is for build 9L30, but the one from the installer I downloaded today is for build 9L31a. Thus, we have to conclude that Apple &#8216;hot swapped&#8217; Mac OS X 10.5.8. That is, they switched Mac OS X builds after release without bumping the version number.</p>
<p>Why Apple did this remains a mystery. Usually software developers do it when they discover an issue shortly after release but don&#8217;t want to go to the trouble of making a public announcement of a new version. What was the issue, and do those of us who have build 9L30 installed still suffer from the issue? For the answer to those questions, you&#8217;ll have to read the release notes. <img src='http://lapcatsoftware.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2009/09/01/apple-hot-swapped-mac-os-x-10-5-8/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Snow Leopard hidden Dock preference</title>
		<link>http://lapcatsoftware.com/blog/2009/08/28/snow-leopard-hidden-dock-preference/</link>
		<comments>http://lapcatsoftware.com/blog/2009/08/28/snow-leopard-hidden-dock-preference/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 17:02:13 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[Apple]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=221</guid>
		<description><![CDATA[In Mac OS X 10.5 and earlier, clicking and holding on an item in the Dock would bring up a contextual menu for that Dock item. In Mac OS X 10.6, popularly known as Snow Leopard, unpopularly known as Leopard Service Pack 1 or &#8220;What am I supposed to do with my Power Mac G5 [...]]]></description>
			<content:encoded><![CDATA[<p>In Mac OS X 10.5 and earlier, clicking and holding on an item in the Dock would bring up a contextual menu for that Dock item. In Mac OS X 10.6, popularly known as Snow Leopard, unpopularly known as Leopard Service Pack 1 or &#8220;What am I supposed to do with my Power Mac G5 Quad?&#8221;, this behavior has changed. One of the new non-Exchange non-features in Snow Leopard is <a href="http://www.apple.com/macosx/what-is-macosx/expose.html" title="Apple - Mac OS X - What is Mac OS X - Expos&eacute;">Dock Expos&eacute;</a>. Clicking and holding on an application icon in the Snow Leopard Dock invokes Expos&eacute; for that application. This is the same effect you see when pressing the F10 key in Tiger and later.</p>
<p>If for some bizarre reason you prefer the old behavior (you backward, reactionary, Obama-hating Luddite), it is possible to bring it back. This is a Lap Cat Software exclusive &mdash; you heard it here first, folks! Launch the Terminal application and enter the following:</p>
<pre><code>defaults write com.apple.Dock show-expose-menus -bool no; killall Dock</code></pre>
<p>You&#8217;re welcome. Remember me fondly in your will.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2009/08/28/snow-leopard-hidden-dock-preference/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>It&#8217;s over</title>
		<link>http://lapcatsoftware.com/blog/2009/08/06/its-over/</link>
		<comments>http://lapcatsoftware.com/blog/2009/08/06/its-over/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 17:01:17 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=219</guid>
		<description><![CDATA[I figured I&#8217;d cruise, at least through the Spring. However, the wheels on the bus go round and round. rdar://problem/7125338 I am still master of my domain. Although I need to renew before it expires in three weeks.]]></description>
			<content:encoded><![CDATA[<p>I figured I&#8217;d cruise, at least through the Spring. However, the wheels on the bus go round and round.</p>
<p><a href="http://lapcatsoftware.com/rdar/7125338/index.html" title="Radar Problem 7125338">rdar://problem/7125338</a></p>
<p>I am still master of my domain. Although I need to renew before it expires in three weeks.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2009/08/06/its-over/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Boycott Radar</title>
		<link>http://lapcatsoftware.com/blog/2009/08/05/boycott-radar/</link>
		<comments>http://lapcatsoftware.com/blog/2009/08/05/boycott-radar/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 17:00:33 +0000</pubDate>
		<dc:creator>Jeff</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://lapcatsoftware.com/blog/?p=217</guid>
		<description><![CDATA[Until further notice, I&#8217;m boycotting Radar. No more filing bugs, no more responding to bugs. For me, Radar is both frustrating beyond belief and also a waste of time. I recommend that my fellow Mac developers join my boycott, if for no other reason than to preserve whatever sanity and mental health you have remaining. [...]]]></description>
			<content:encoded><![CDATA[<p>Until further notice, I&#8217;m boycotting <a href="https://bugreport.apple.com/" title="Apple Bug Reporter">Radar</a>. No more filing bugs, no more responding to bugs. For me, Radar is both frustrating beyond belief and also a waste of time. I recommend that my fellow Mac developers join my boycott, if for no other reason than to preserve whatever sanity and mental health you have remaining. I&#8217;ve come to the conclusion that life without Radar will be happier and more productive.</p>
<p>In order of importance (and annoyance), here are my major complaints about Radar:</p>
<ol>
<li><b>Mindless responses to bugs from Apple zombies &hellip; err, employees</b><br />
	I expect a knowledgeable person to read and evaluate my bugs carefully. I&#8217;m sick and tired of getting stupid, sometimes irrelevant responses. It&#8217;s clear in many cases that the Apple employee was basically skimming for keywords and didn&#8217;t bother to actually read the bug. And I&#8217;m far from alone here: I&#8217;ve heard numerous examples (otherwise known as horror stories) from other developers of the same kind of maddening response to their bugs. We developers spend a lot of time discovering, investigating, and reproducing these bugs for Apple, without receiving any compensation. Inexplicably, though, Apple employees are dismissive of our help. They seem to care more about closing the Radar than fixing the bug that the Radar reports.</li>
<li><b>Duplicate bugs are second class citizens</b><br />
	If your bug gets marked as a dupe, you&#8217;re doomed. Don&#8217;t expect to ever hear about it again, not even if it&#8217;s fixed. Apple&#8217;s canned response says, &#8220;To request the status of the original bug, please update your report directly via the Apple Bug Reporter&#8221;, which is ridiculous, because you could have dozens or even hundreds of duplicates, and it can sometimes take years for a bug to get fixed, so how often are you supposed to make status requests?</li>
<li><b>No searchable bug database</b><br />
	If you&#8217;re lucky, an Apple engineer on a mailing list may tell you that your problem is a known issue. If not, you could flail around for days trying to figure out why your code that should work doesn&#8217;t work, because of a Mac OS X bug. A number of other companies provide searchable bug databases to their developers, why can&#8217;t Apple? It&#8217;s true that sometimes your bug reports contain confidential information that you don&#8217;t want to share with other developers (your competitors, for example), but often they don&#8217;t, and it would be nice to have an &#8216;opt in&#8217; option to allow other developers to see your bug. It&#8217;s also true that Apple needs to protect its secrets; however, Apple should realize that not everything is or needs to be secret, and as ADC members we&#8217;re already bound by Non-Disclosure Agreements, so what&#8217;s the point of being under an NDA with Apple if Apple never discloses anything to us? The existence of <a href="http://openradar.appspot.com/" title="Open Radar">Open Radar</a> demonstrates how ludicrous it is that Apple does not provide a searchable bug database themselves. Although I don&#8217;t post my bugs on Open Radar because I don&#8217;t have a Google account, I do have a list <a href="http://lapcatsoftware.com/rdar/" title="Radar Problems">here</a>.</li>
<li><b>Wasting my time asking me to verify unfixed bugs</b><br />
	Apple employees seem to think third party developers have nothing better to do than perform unpaid QA work for Apple. A number of times, I&#8217;ve gotten requests to verify that a bug still exists in software update X, and indeed it does still exist in software update X, as demonstrated by the very steps to reproduce that I listed in my bug report. Did anyone at Apple even bother to follow my steps? (That&#8217;s a rhetorical question &mdash; obviously, no.) What were you thinking here, that my bug would magically disappear without having to do anything? Sorry, your deus ex machina failed to show up, stop wasting my time and start fixing the bug. If Apple is understaffed, and its employees are overworked and don&#8217;t have enough time to do this themselves, that&#8217;s not my fault. If I hear one more excuse about Apple not having the resources, I&#8217;m going to puke. Or punch someone. Or puke on someone&#8217;s fist. Apple makes more than a billion dollars a quarter in <em>profit</em>. My company makes slightly less than that.</li>
</ol>
<p>What I&#8217;ve come to realize is that we developers don&#8217;t need Radar. Apple needs us, but we don&#8217;t need them (for this, anyway). The time between filing a bug and seeing a fix for the bug shipped in a Mac OS X software update is usually quite long, sometimes infinitely long. If I discover a Mac OS X bug that affects my software, I can&#8217;t wait for a fix from Apple, I have to write a workaround immediately. Thus, by the time I file a bug, I don&#8217;t really need a fix for it. The sole purpose of filing the bug is to help other developers and to make the Mac OS X platform better. Essentially, it&#8217;s charity work. If Apple makes charity work for them really difficult and annoying, then I&#8217;m going to find something better to do, like adopt a cat, or a highway.</p>
]]></content:encoded>
			<wfw:commentRss>http://lapcatsoftware.com/blog/2009/08/05/boycott-radar/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
	</channel>
</rss>
