<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8397311766319215218</id><updated>2012-01-20T00:57:44.825-08:00</updated><category term='Threading'/><category term='Mac App Store'/><category term='iPhone'/><category term='Agile'/><category term='Smalltalk'/><category term='Objective-C'/><category term='Higher Order Messaging'/><category term='Blocks'/><category term='Cocoa'/><category term='Postscript'/><category term='Objective-Smalltalk'/><category term='performance'/><category term='XML'/><category term='Design'/><category term='iOS'/><category term='iPad'/><category term='Memory management'/><category term='Refactoring'/><category term='Web'/><category term='Testing'/><title type='text'>metablog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.metaobject.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>49</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-2733413729090965438</id><published>2012-01-11T04:05:00.001-08:00</published><updated>2012-01-11T04:05:54.061-08:00</updated><title type='text'>Objective-C language of the year 2011!</title><content type='html'>  &lt;style type="text/css"&gt;
    p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; text-align: center; font: 12.0px Arial}
    p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; text-align: center}
    p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Arial; color: #2000aa}
    p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Arial}
    span.s1 {font: 12.0px Helvetica}
    table.t1 {background-color: #ffffff; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080}
    td.td1 {width: 52.0px; background-color: #bbbbff; margin: 0.5px 0.5px 0.5px 0.5px; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080; padding: 1.0px 5.0px 1.0px 5.0px}
    td.td2 {width: 100.0px; background-color: #bbbbff; margin: 0.5px 0.5px 0.5px 0.5px; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080; padding: 1.0px 5.0px 1.0px 5.0px}
    td.td3 {width: 137.0px; background-color: #bbbbff; margin: 0.5px 0.5px 0.5px 0.5px; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080; padding: 1.0px 5.0px 1.0px 5.0px}
    td.td4 {width: 37.0px; background-color: #bbbbff; margin: 0.5px 0.5px 0.5px 0.5px; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080; padding: 1.0px 5.0px 1.0px 5.0px}
    td.td5 {width: 52.0px; background-color: #eeeeff; margin: 0.5px 0.5px 0.5px 0.5px; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080; padding: 1.0px 5.0px 1.0px 5.0px}
    td.td6 {width: 100.0px; background-color: #eeeeff; margin: 0.5px 0.5px 0.5px 0.5px; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080; padding: 1.0px 5.0px 1.0px 5.0px}
    td.td7 {width: 137.0px; background-color: #eeeeff; margin: 0.5px 0.5px 0.5px 0.5px; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080; padding: 1.0px 5.0px 1.0px 5.0px}
    td.td8 {width: 37.0px; background-color: #eeeeff; margin: 0.5px 0.5px 0.5px 0.5px; border-style: solid; border-width: 1.0px 1.0px 1.0px 1.0px; border-color: #808080 #808080 #808080 #808080; padding: 1.0px 5.0px 1.0px 5.0px}
  &lt;/style&gt;


After just barely missing out 2 years in a row (first to Go and then to Python), Objective-C finally
managed to snatch &lt;a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html"&gt;Tiobe's programming language of the year award for 2011&lt;/a&gt;.  Yay!&lt;p&gt;

Objective-C managed a jump of 3.91% to 6.92% in 2011, and is now only a percent or so shy of C++ (8.06%), which
incidentally just got edged out by C# (8.78%).  It also zoomed ahead of Python (3.22%), which squeaked by
Objective-C just barely last year to take top honors, but has now fallen below PHP and Visual BASIC.&lt;p&gt;

Ruby, Perl and JavaScript are in the 1-3% range.&lt;p&gt;

As TIOBE doesn't appear to keep old posts around the above link is likely to go stale in about a month.  Here
is the table:

&lt;table cellspacing="0" cellpadding="0" class="t1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td valign="middle" class="td1"&gt;&lt;p class="p1"&gt;&lt;b&gt;Position&lt;/b&gt;&lt;/p&gt;&lt;p class="p1"&gt;&lt;b&gt;Jan 2012&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td1"&gt;&lt;p class="p1"&gt;&lt;b&gt;Position&lt;/b&gt;&lt;/p&gt;&lt;p class="p1"&gt;&lt;b&gt;Jan 2011&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td2"&gt;&lt;p class="p1"&gt;&lt;b&gt;Delta in Position&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td3"&gt;&lt;p class="p1"&gt;&lt;b&gt;Programming Language&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td1"&gt;&lt;p class="p1"&gt;&lt;b&gt;Ratings&lt;/b&gt;&lt;/p&gt;&lt;p class="p1"&gt;&lt;b&gt;Jan 2012&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td1"&gt;&lt;p class="p1"&gt;&lt;b&gt;Delta&lt;span class="Apple-converted-space"&gt; &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;p class="p1"&gt;&lt;b&gt;Jan 2011&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td4"&gt;&lt;p class="p1"&gt;&lt;b&gt;Status&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif" alt="Same.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Java.html"&gt;Java&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;17.479%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-0.29%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;2&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;2&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif" alt="Same.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/C.html"&gt;C&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;16.976%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+1.15%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;3&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;6&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/C_.html"&gt;C#&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;8.781%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+2.55%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;4&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;3&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif" alt="Down.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/C__.html"&gt;C++&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;8.063%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-0.72%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;5&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;8&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Objective-C.html"&gt;Objective-C&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;6.919%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+3.91%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;6&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;4&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif" alt="Down.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif" alt="Down.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/PHP.html"&gt;PHP&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;5.710%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-2.13%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;7&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;7&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif" alt="Same.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/(Visual)_Basic.html"&gt;(Visual) Basic&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;4.531%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-1.34%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;8&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;5&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif" alt="Down.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif" alt="Down.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif" alt="Down.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Python.html"&gt;Python&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;3.218%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-3.05%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;9&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;9&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif" alt="Same.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Perl.html"&gt;Perl&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;2.773%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-0.08%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;10&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;11&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/JavaScript.html"&gt;JavaScript&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;2.322%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+0.73%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;11&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;12&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Delphi_Object_Pascal.html"&gt;Delphi/Object Pascal&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;1.576%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+0.29%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;12&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;10&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif" alt="Down.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Down.gif" alt="Down.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Ruby.html"&gt;Ruby&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;1.441%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-0.34%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;13&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;13&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif" alt="Same.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Lisp.html"&gt;Lisp&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;1.111%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+0.00%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;14&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;14&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Same.gif" alt="Same.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Pascal.html"&gt;Pascal&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;0.798%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-0.12%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;15&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;17&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Transact-SQL.html"&gt;Transact-SQL&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;0.772%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+0.01%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;16&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;24&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/PL_SQL.html"&gt;PL/SQL&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;0.709%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+0.15%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  A&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;17&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;20&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Ada.html"&gt;Ada&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;0.634%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-0.05%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  B&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;18&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;39&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Logo.html"&gt;Logo&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;0.632%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+0.29%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  B&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;19&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;25&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/R.html"&gt;R&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;0.609%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;+0.07%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  B&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;20&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;21&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td6"&gt;&lt;p class="p2"&gt;&lt;span class="s1"&gt;&lt;img src="http://www.tiobe.com/content/paperinfo/tpci/images/Up.gif" alt="Up.gif"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td7"&gt;&lt;p class="p3"&gt;&lt;a href="http://www.tiobe.com/content/paperinfo/tpci/Lua.html"&gt;Lua&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;0.559%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td5"&gt;&lt;p class="p1"&gt;-0.08%&lt;/p&gt;&lt;/td&gt;&lt;td valign="middle" class="td8"&gt;&lt;p class="p4"&gt;  B&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;


And the graph:

&lt;img src="http://lh3.ggpht.com/-08KNvnCuNnA/Tw17HY44iVI/AAAAAAAAACI/8q0kgVgUek8/tpci_trends_January_2012.png?imgmax=800" alt="Tpci trends January 2012" title="tpci_trends_January_2012.png" border="0" width="600" height="450" /&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-2733413729090965438?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/2733413729090965438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=2733413729090965438' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2733413729090965438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2733413729090965438'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2012/01/objective-c-language-of-year-2011.html' title='Objective-C language of the year 2011!'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-08KNvnCuNnA/Tw17HY44iVI/AAAAAAAAACI/8q0kgVgUek8/s72-c/tpci_trends_January_2012.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-1737248123199641816</id><published>2012-01-11T01:30:00.001-08:00</published><updated>2012-01-11T01:30:31.673-08:00</updated><title type='text'>Convention over configuration is subclassing?</title><content type='html'>Having now &lt;a href="http://blog.metaobject.com/2011/12/ruby-and-rails-scalability.html"&gt;played&lt;/a&gt; 
a little bit with &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt;, I am starting to wonder wether 
the much touted "&lt;a href="http://en.wikipedia.org/wiki/Convention_over_configuration"&gt;convention over configuration&lt;/a&gt;" is really just a different implementation of subclassing.  That is certainly what
it felt like, and the Wikipedia article referenced states that "a developer only needs to specify unconventional aspects of the application."&lt;p&gt;

Or perhaps slightly more accurately, both subclassing and &lt;em&gt;convention over configuration&lt;/em&gt; would appear
to be a form
of programming by difference or "&lt;a href="http://www.metaobject.com/papers/Diplomarbeit.pdf"&gt;refinement&lt;/a&gt;".

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-1737248123199641816?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/1737248123199641816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=1737248123199641816' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/1737248123199641816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/1737248123199641816'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2012/01/convention-over-configuration-is.html' title='Convention over configuration is subclassing?'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-4890367200737136893</id><published>2011-12-09T10:37:00.001-08:00</published><updated>2011-12-10T01:05:11.166-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><title type='text'>Ruby (and Rails) scalability?</title><content type='html'>Recently I &lt;a href="http://blog.metaobject.com/2011/01/nodejs-performance-httpd-performance.html"&gt;wrote&lt;/a&gt; about &lt;a href="http://nodejs.org/"&gt;Node.js&lt;/a&gt;&lt;a href="http://zgadzaj.com/benchmarking-nodejs-basic-performance-tests-against-apache-php"&gt;performance&lt;/a&gt;, comparing it to my (still non-public, sorry!) Objective-C based &lt;a href="http://www.gnu.org/s/libmicrohttpd/"&gt;libµhttp&lt;/a&gt; wrapper and Apache on my main development machine of the time, a MacBook Pro.&lt;p&gt;

Node.js did really well on tasks that have lots of concurrent requests that are mostly waiting, did OK on basic static serving tasks and not so well on compute-intensive tasks.&lt;p&gt;

Having developed an interest in minimal web-servers, I wondered how &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt; and, by association, &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; would do.&lt;p&gt;

For Sinatra I used the &lt;a href="https://github.com/adamwiggins/scanty"&gt;scanty blog engine&lt;/a&gt; and the basic "Hello World" example:

&lt;hr&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;
require 'sinatra'

get '/hi' do
  "Hello World!"
end
&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;hr&gt;

For Ruby on Rails, I used the &lt;a href="http://guides.rubyonrails.org/getting_started.html"&gt;blog tutorial&lt;/a&gt; "out of the box", invoking it with "&lt;code&gt;rails s&lt;/code&gt;"
to start the server.  In addition I also had RoR just serving a static file instead of the database-backed blog.  All this on my new dev machine, a 2011 MacBook Air with 1.8 GHz Intel Core i7 and 4 GB of DRAM.  I also discovered that &lt;a href="http://www.hpl.hp.com/research/linux/httperf/"&gt;httperf&lt;/a&gt; is a much better benchmark program for
my needs than &lt;a href="http://httpd.apache.org/docs/2.0/programs/ab.html"&gt;ab&lt;/a&gt;.  I used 
it with 100 requests per connection, a burst length of 100 and a sufficient number
of connections to get stable results without taking all day.&lt;p&gt;&lt;table style="margin-top: 10px; border: 1px solid black;"&gt;&lt;tbody&gt;&lt;tr style="text-decoration: underline;"&gt;&lt;td&gt;Platform&lt;/td&gt;&lt;td&gt;# requests/sec&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Apache&lt;/td&gt;&lt;td&gt;5884&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sinatra Hello World&lt;/td&gt;&lt;td&gt;357&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ruby on Rails static page&lt;/td&gt;&lt;td&gt;312&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sinatra scanty blog&lt;/td&gt;&lt;td&gt;101&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ruby on Rails blog&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

This seems really slow, even when doing virtually nothing, and absolutely abysmal 
when using the typical RoR setup, serving records from an SQL database via templates.
I distinctly remember my G5 serving in the thousands of requests/s using WebObjects
6-7 years ago.&lt;p&gt;

Is this considered normal or am I doing something wrong?&lt;p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-4890367200737136893?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/4890367200737136893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=4890367200737136893' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4890367200737136893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4890367200737136893'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/12/ruby-and-rails-scalability.html' title='Ruby (and Rails) scalability?'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-5054503839632556430</id><published>2011-07-21T02:19:00.001-07:00</published><updated>2011-07-21T02:19:09.183-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><title type='text'>The surprising thing about Objective-C...</title><content type='html'>Alex Payne, when asked "Are you surprised at the popularity of any current languages?" during the &lt;a href="http://radar.oreilly.com/2011/07/emerging-programming-languages-wheeler-faust-loke.html?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+oreilly%2Fradar%2Fatom+%28O%27Reilly+Radar%29"&gt;preview&lt;/a&gt; of emerging languages for &lt;a href="http://www.oscon.com/oscon2011"&gt;OSCON 2011's&lt;/a&gt;&lt;a href="http://www.oscon.com/oscon2011/public/schedule/topic/Emerging%20Languages"&gt;emerging languages&lt;/a&gt; track:

&lt;blockquote&gt;&lt;b&gt;Alex Payne&lt;/b&gt;: I'm constantly surprised at the popularity and success of Objective-C. Almost everyone I know tells the same story about Objective-C: they started learning it and they hated it. They thought it was the worst of C and the worst of dynamic languages. And then eventually, they learned to love it.

Most of the time, that software works pretty darn well, so who am I to judge? I'm pleasantly surprised by the continued success of Objective-C, and I think it should be taken as a lesson for the language designers out there.
&lt;/blockquote&gt;

This is echoed by the the first (and as of this writing only) comment to the post:

&lt;blockquote&gt;&lt;a href="http://www.oreillynet.com/pub/au/3904"&gt;Alasdair Allan&lt;/a&gt; [18 July 2011 10:09 AM]

I certainly agree with Alex about Objective-C, when I was initially learning the language I deeply despised it. Now I love it, and think it's one of the more elegant and powerful of the (many) languages I know.
Definitely a lesson to language designers, do what you think is right and ignore the crowds. If you are right people will grow to love your language, just as soon as they figure it out.

&lt;/blockquote&gt;

I actually liked Objective-C pretty much from the start, but then again at that time (1986) there simply wasn't anything close that I had access to, and writing an Objective-C pre-processor and runtime on my Amiga was simply more feasible than a C++ frontend or a complete Smalltalk VM.&lt;p&gt;

Modifying the sentiment expressed slightly, I'd say that from a &lt;em&gt;theoretical&lt;/em&gt; point of view, I hate Objective-C and think it's a bad joke, a trainwreck.  However, from &lt;em&gt;practical&lt;/em&gt; experience, I love it and find it's one of the most productive languages out there for actually building stuff.  And no, it's not just about the frameworks, as I've used Objective-C in non-NeXT, non-Apple environments where we had to build most of our own frameworks.&lt;p&gt;

So while I support Alasdair's comment, my lesson for language designers is that our theory appears to not be particularly good at predicting reality.   In other words:  our theory &lt;strike&gt;sucks&lt;/strike&gt; has many research opportunities.&lt;p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-5054503839632556430?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/5054503839632556430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=5054503839632556430' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5054503839632556430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5054503839632556430'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/07/surprising-thing-about-objective-c.html' title='The surprising thing about Objective-C...'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-2535773297331161695</id><published>2011-04-27T11:22:00.001-07:00</published><updated>2011-04-27T11:22:51.458-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>Lazy Initialization</title><content type='html'>&lt;a href="http://objology.blogspot.com/"&gt;Travis&lt;/a&gt;&lt;a href="http://objology.blogspot.com/2011/04/burned-by-bein-lazy-again.html"&gt;cautions&lt;/a&gt; against &lt;a href="http://en.wikipedia.org/wiki/Lazy_initialization"&gt;lazy initialization&lt;/a&gt;.  Spooky coincidence: I just
managed to fix an extremely mysterious memory smasher in an Objective-C
program's exception handling code by moving the lazily initialized localization
code to app startup.  Not sure wether localizing exceptions is really such a good idea
in the first place, but having the localization code run inside the exception handling
code does seem pushing it a bit.&lt;p&gt;

So couldn't agree more.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-2535773297331161695?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/2535773297331161695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=2535773297331161695' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2535773297331161695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2535773297331161695'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/04/lazy-initialization.html' title='Lazy Initialization'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-5310228992385899017</id><published>2011-03-12T03:25:00.001-08:00</published><updated>2011-03-12T03:25:20.568-08:00</updated><title type='text'>Speed matters</title><content type='html'>Greg Linden &lt;a href="http://glinden.blogspot.com/2006/11/marissa-mayer-at-web-20.html"&gt;recounts&lt;/a&gt; Marissa Mayer's talk at Web 2.0 showing how even very small decreases in performance have highly measurable impacts on users, and for web businesses on the bottom line.&lt;p&gt;

The change was an increase from 10 to 30 search results, which was expected to produce an increase in user satisfaction, because users had asked for more search results.  Instead, there was a completely unexpected and at first inexplicable 20% drop in traffic after the change was implemented.  Only after some time did the team discover that the new results page took half a second longer to display, and in further testing they found that every 100 ms delay caused a measurable drop in clicks.&lt;p&gt;

While I am not aware of similar research on desktop apps, I am sure that the same principle applies:   speed matters, a lot; and it matters pre-consciously, that is, long before users will mention speed as an issue.&lt;p&gt;


&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-5310228992385899017?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/5310228992385899017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=5310228992385899017' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5310228992385899017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5310228992385899017'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/03/speed-matters.html' title='Speed matters'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-3253005103813286404</id><published>2011-02-17T09:15:00.001-08:00</published><updated>2011-02-17T10:16:08.726-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><title type='text'>The experienced craftsman plans less</title><content type='html'>Christopher Alexander via &lt;a href="http://www.37signals.com/svn/posts/1042-christopher-alexander-on-the-difference-between-a-fifty-year-old-carpenter-and-a-novice"&gt;37 signals&lt;/a&gt;:  

&lt;blockquote&gt;
The essence of this process is very fundamental indeed. We may understand it best by comparing the work of a fifty-year-old carpenter with the work of a novice. The experienced carpenter keeps going. He doesn’t have to keep stopping, because every action he performs, is calculated in such a way that some later action can put it right to the extent that it is imperfect now. What is critical here, is the sequence of events. The carpenter never takes a step which he cannot correct later; so he can keep working, confidently, steadily.&lt;p&gt;

The novice by comparison, spends a great deal of his time trying to figure out what to do. He does this essentially because he knows that an action he takes now may cause unretractable problems a little further down the line; and if he is not careful, he will find himself with a joint that requires the shortening of some crucial member – at a stage when it is too late to shorten that member. The fear of these kinds of mistakes forces him to spend hours trying to figure ahead: and it forces him to work as far as possible to exact drawings because they will guarantee that he avoids these kinds of mistakes.&lt;p&gt;

The difference between the novice and the master is simply that the novice has not learnt, yet, how to do things in such a way that he can afford to make small mistakes. The master knows that the sequence of his actions will always allow him to cover his mistakes a little further down the line. It is this simple but essential knowledge which gives the work of a master carpenter its wonderful, smooth, relaxed, and almost unconcerned simplicity.&lt;p&gt;&lt;/blockquote&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-3253005103813286404?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/3253005103813286404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=3253005103813286404' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/3253005103813286404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/3253005103813286404'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/02/experienced-craftsman-plans-less.html' title='The experienced craftsman plans less'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-2619709143686086665</id><published>2011-02-16T22:05:00.001-08:00</published><updated>2011-02-16T22:05:22.702-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac App Store'/><title type='text'>Mac App Store won't let me buy apps: solution</title><content type='html'>Just tried to buy an app via the Mac App Store and it was absolutely refusing to take my money.  Various suggestions I've seen on the web such as clearing caches,resetting them via iTunes advanced preferences, rebooting, retrying, using slight variations of my account name all made no difference whatsoever.&lt;p&gt;

The solution turned out to be manually signing in using the Store menu (manually sign out if you are already signed in).  At that point I was allowed to update/verify my billing information and subsequent purchase attempts worked.&lt;p&gt;

In previous attempts, I had not signed in manually, but rather had the App Store do the sign-in after I attempted to purchase.&lt;p&gt;

So needs a little more work...&lt;p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-2619709143686086665?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/2619709143686086665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=2619709143686086665' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2619709143686086665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2619709143686086665'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/02/mac-app-store-won-let-me-buy-apps.html' title='Mac App Store won&amp;#39;t let me buy apps: solution'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-2079743566888883068</id><published>2011-02-16T20:34:00.001-08:00</published><updated>2011-02-16T20:34:38.066-08:00</updated><title type='text'>Train wreck management</title><content type='html'>I wish the article &lt;a href="http://www.poppendieck.com/blame.htm"&gt; Train Wreck Management &lt;/a&gt; by Poppendieck didn't strike such a chord.&lt;p&gt;



&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-2079743566888883068?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/2079743566888883068/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=2079743566888883068' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2079743566888883068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2079743566888883068'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/02/train-wreck-management.html' title='Train wreck management'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-806624176282161258</id><published>2011-02-14T21:15:00.001-08:00</published><updated>2011-02-14T23:09:51.249-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><title type='text'>Only 1 GHz</title><content type='html'>Tim Bray &lt;a href="http://www.tbray.org/ongoing/When/201x/2010/04/06/Yet-More-iPad"&gt;wonders&lt;/a&gt; (well, wondered a while ago) about the excellent perceived performance of the iPad at 'only' 1Ghz.&lt;p&gt;

Hmm&lt;p&gt;

1 GHz actually seems like quite a lot to me.  1000 times an Apple ][, 40 times a NeXT, and the latter was driving a megapixel display.  I guess we have gotten used to &lt;a href="http://cacm.acm.org/magazines/2009/5/24648-spending-moores-dividend/fulltext"&gt;wasted cycles&lt;/a&gt;.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-806624176282161258?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/806624176282161258/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=806624176282161258' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/806624176282161258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/806624176282161258'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/02/only-1-ghz.html' title='Only 1 GHz'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-4571068023823949838</id><published>2011-02-14T18:02:00.001-08:00</published><updated>2011-02-14T23:13:15.148-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='Cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='iOS'/><title type='text'>PhoneGeometry.h</title><content type='html'>One things that's been tripping me up a bit when writing code that's supposed to be portable between iOS and Cocoa is the removal of NSPoint, NSSize, NSRect and their associated functions from Foundation in iOS.  This is a real shame, because otherwise the Foundations are highly compatible.&lt;p&gt;

One way to rectify this situation would be to start using CG* structs and functions on the desktop as well.  However, this introduces a dependency on CoreGraphics that shouldn't be there for Foundation-based code.&lt;p&gt;

My alternative is to standardize on NSPoint and friends, and map those to their CG alternatives on iOS.  That way, I have minimized my dependencies, with only a small header file to pay for it:   PhoneGeomtry.h &lt;p&gt;

This is now part of MPWFoundation  (on &lt;a href="https://github.com/mpw/MPWFoundation"&gt;github&lt;/a&gt;).  


&lt;hr&gt;&lt;pre&gt;
//
//  PhoneGeometry.h
//  MPWFoundation
//
//  Created by Marcel Weiher on 11/11/10.
//  Copyright 2010-2011 Marcel Weiher. All rights reserved.
//


#if TARGET_OS_IPHONE
#ifndef PHONE_GEOMETRY
#define PHONE_GEOMETRY
#import &amp;lt;CoreGraphics/CoreGraphics.h&amp;gt;
typedef CGRect NSRect;
typedef CGPoint NSPoint;
typedef CGSize NSSize;
#define NSMakeRect  CGRectMake
#define NSMakePoint CGPointMake
#define NSMakeSize  CGSizeMake
#define NSEqualPoints  CGPointEqualToPoint
#define NSEqualRects   CGRectEqualToRect
#define NSIntersectsRect  CGRectIntersectsRect
static inline NSString *NSStringFromRect( CGRect r ) { return [NSString stringWithFormat:@"(%g,%g - %g,%g)",r.origin.x,r.origin.y,r.size.width,r.size.height]; }
static inline NSString *NSStringFromPoint( CGPoint p ) { return [NSString stringWithFormat:@"(%g,%g)",p.x,p.y]; }
static inline NSString *NSStringFromSize( CGSize s ) { return [NSString stringWithFormat:@"(%g,%g)",s.width,s.height]; }



#endif
#endif

&lt;/pre&gt;&lt;/hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-4571068023823949838?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/4571068023823949838/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=4571068023823949838' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4571068023823949838'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4571068023823949838'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/02/phonegeometryh.html' title='PhoneGeometry.h'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-2358024572342728819</id><published>2011-01-31T17:26:00.001-08:00</published><updated>2011-01-31T17:26:10.516-08:00</updated><title type='text'>Objective-XML and MPWFoundation now available on github</title><content type='html'>By popular demand, both &lt;a href="https://github.com/mpw/Objective-XML"&gt;Objective-XML&lt;/a&gt; and &lt;a href="https://github.com/mpw/MPWFoundation"&gt;MPWFoundation&lt;/a&gt; are now available on &lt;a href="https://github.com/"&gt;github&lt;/a&gt;.  Which means I am finally learning &lt;a href="http://git-scm.com/"&gt;git&lt;/a&gt;...so far it looks very nice and I am impressed with the performance.&lt;p&gt;

Thanks to &lt;a href="http://homepage.mac.com/tblanchard/Menu11.html"&gt;Todd Blanchard&lt;/a&gt; for providing the necessary impetus to learn git.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-2358024572342728819?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/2358024572342728819/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=2358024572342728819' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2358024572342728819'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2358024572342728819'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/01/objective-xml-and-mpwfoundation-now.html' title='Objective-XML and MPWFoundation now available on github'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-3466761804264509662</id><published>2011-01-18T08:38:00.001-08:00</published><updated>2011-01-18T08:38:35.042-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='Cocoa'/><title type='text'>On switching away from CoreData</title><content type='html'>Like Brent Simmons, I have a project where I am currently in the process
of &lt;a href="http://inessential.com/2010/02/26/on_switching_away_from_core_data"&gt;switching away from CoreData&lt;/a&gt;.  
Unlike Brent, and somewhat surprisingly given my proclivities, the
reason is not performance.&lt;p&gt;

Rather, the issues we have had with CoreData were additional
complexity and more importantly gratuitous dependencies that, at least for
our application, were not offset by noticeable benefits.&lt;p&gt;

One of the most significant structural dependencies is that
CoreData requires all your model classes to be subclasses of
NSManagedObject, a class provided by CoreData.  This may not
seem like a big problem at first, but it gets in the way
of defining a proper DomainModel, which should always be
independent.  The Java community actually figured this out
a while ago, which is why there was a recent move to 
persistence frameworks supporting &lt;a href="http://java.sys-con.com/node/180374"&gt;POJOs&lt;/a&gt;.  (Of course, POOO
doesn't have quite the same ring to it, and also the Java frameworks
were a lot more heavy-handed than CoreData).  The model is
where your value is, it should be unencumbered.  For
example, when we started looking at the iPhone, there was
no CoreData there, so we faced the prospect of duplicating
all our model code.&lt;p&gt;

In addition to initially not having CoreData, the iPhone
app also used (and still uses) a completely different persistence
mechanism (more feed oriented), and there were other applications
where yet a third persistence mechanism was used (more 
document centric than DB-centric, with an externally defined
file format).  A proper class hierarchy would have had an
abstract superclass without any reference to a specific 
persistence mechanism, but capturing the domain knowledge of
our model.   With CoreData, this hierarchy was impossible.&lt;p&gt;

Since we had externally defined file formats in every case,
we had to write an Atomic Store adapter and thus also couldn't
really benefit from CoreData's change management.  When we
did the move, it turned out that the Atomic Store adapter
we had written was significantly more code than just serializing
and de-serializing the XML ourselves.&lt;p&gt;

Another benefit of CoreData is its integration with Bindings,
but that also turned out to be of little use to us.  The code
we managed to save with Bindings was small and trivial, whereas
the time and effort to debug bindings when they went wrong or to
customize them for slightly specialized needs was very, very large.
So we actually ditched Bindings a long time before we got rid
of CoreData.&lt;p&gt;

So why was CoreData chosen in the first place?   Since I wasn't
around for that decision, I don't know 100%, but as far as I can tell it was
mostly "Shiny Object Syndrome".  CoreData and Bindings were new Apple
technologies at the time, therefore they had to be used.&lt;p&gt;

So are there any lessons here?  The first would be to avoid Shiny
Object Syndrome.  By all means have fun and play around, but not
in production code.  Second and related is to really examine
your needs.  CoreData is probably highly appropriate in many
contexts, it just wasn't in ours.  Finally, it would be a 
huge improvement if CoreData were to support Plain Old
Objective-C Objects.  In fact, if that were the case we
probably would not have to ditch it.&lt;p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-3466761804264509662?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/3466761804264509662/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=3466761804264509662' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/3466761804264509662'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/3466761804264509662'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/01/on-switching-away-from-coredata.html' title='On switching away from CoreData'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-6617342966895611863</id><published>2011-01-10T12:55:00.001-08:00</published><updated>2011-01-10T13:17:06.058-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Threading'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='Cocoa'/><title type='text'>Little Message Dispatch</title><content type='html'>&lt;a href="http://inessential.com/"&gt;Brent Simmons's&lt;/a&gt; recent &lt;a href="http://inessential.com/2010/12/05/some_notes_on_threading"&gt;notes on threading&lt;/a&gt; show a great, limited approach to threading that appears
to work well in practice.  If you haven't read it and are at all
interested in threading on OS X or iOS, I suggest you head over there right now.&lt;p&gt;

I feel much the same way, that is although I think &lt;a href="http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html"&gt;Grand Central Dispatch&lt;/a&gt; is awesome, I simply haven't been able to justify spending much
time with it, because it usually turns out 
that my own threading needs so far have been far more modest than
what GCD provides.
 In fact,
I find that an approach that's even more constrained than the 
one based on NSOperationQueue that Brent describes has been working really well in a number of projects.&lt;p&gt;

Instead of queueing up operations and letting them unwind however, I just 
spawn a single I/O thread (at most a few) and then have that perform 
the I/O deterministically.  This is paired with a downloader that uses
the NSURL loading system to download any number of requests in parallel.&lt;p&gt;

&lt;blockquote&gt;
&lt;hr&gt;
&lt;pre&gt;
- (void)downloadNewsContent
{       
        id pool=[NSAutoreleasePool new];
        
        [[self downloader] downloadRequests:[self thumbnailRequests]];
        [[self downloader] downloadRequests:[self contentRequests]];
        [[self downloader] downloadOnlyRequests:[self imageRequests]];
        [pool release];
}

&lt;/pre&gt;
&lt;hr&gt;
&lt;/blockquote&gt;

This loads 3 types of objects:  first the thumbnails, then article
content, then images associated with the articles.   The sequencing
is both deliberate (thumbs first, article images cannot be loaded 
before the article content is present) and simply expressed in the code by the well-known
means of just writing the actions one after the other, rather than
having those dependencies expressed in call-backs, completion
blocks or NSOperation subclasses.  &lt;p&gt;


So work is done semi-sequentially in the background, while coordination is
done on the main thread, with liberal use of performSelectorOnMainThread.  Of course, I make that a little simpler with a couple of HOMs that dispatch messages to threads:
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;async&lt;/b&gt; runs the message on a new thread, I use it for long-running,
	intrinsically self contained work.  It is equivalent to
	performSelectorInBackground: except for being able to take
	an arbitrary message.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;asyncOnMainThread&lt;/b&gt; and &lt;b&gt;syncOnMainThread&lt;/b&gt; are the equivalents of
	performSelectorOnMainThread, with the waitUntilDone flag set to YES or NO&lt;/li&gt;
&lt;li&gt;&lt;b&gt;afterDelay:&lt;/b&gt;  sends he message after the specified delay&lt;/li&gt;

&lt;/ul&gt;

Here is a bit of code that shows how to have a dispatch a long-running
thread and have it communicate status to the main thread.

&lt;blockquote&gt;
&lt;hr&gt;
&lt;pre&gt;
-(void)loadSections {
	[[self asyncOnMainThread] showSyncing];
	[[[self sections] do] downloadNewsContent];
	[[self asyncOnMainThread] showDoneSyncing];
}
 ...
 -(IBAction)syncButtonClicked {
	[[self async] loadSections];
}

&lt;/pre&gt;
&lt;hr&gt;
&lt;/blockquote&gt;


Brent sums it up quite well in his post:

&lt;blockquote&gt;
Here’s the thing about code: the better it is, the more it looks and reads like a children’s book. 
&lt;/blockquote&gt;

Yep.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-6617342966895611863?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/6617342966895611863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=6617342966895611863' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6617342966895611863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6617342966895611863'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/01/little-message-dispatch.html' title='Little Message Dispatch'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-7877522647032607151</id><published>2011-01-03T17:12:00.001-08:00</published><updated>2011-01-10T13:23:23.427-08:00</updated><title type='text'>Node.js performance?  µhttpd performance!</title><content type='html'>There's been a lot of &lt;a href="http://www.synchrosinteractive.com/blog/9-nodejs/22-nodejs-has-a-bright-future"&gt;hoopla&lt;/a&gt; recently about &lt;a href="http://nodejs.org/"&gt;node.js&lt;/a&gt;.  Being an object-head, I've always liked the idea of reactive (event-driven) web servers, after all, that means it's just like a typical object, sitting there waiting for something to happen and then reacting to it.&lt;p&gt;

Of course, there is also a significant body of research on this topic,
showing for example that user-level thread implementations tend to
get very similar performance to event-based servers.  There is also
the issue that the purity of "no blocking APIs" is somewhat naive on
a modern Unix, because blocking on I/O can happen in lots of different
non-obvious places.   At the very least, you may encounter a page-fault,
and this may even be desirable in order to use memory mapped files.&lt;p&gt;

In those cases, the fact that you have purified all your APIs makes
no difference, you are still blocked on I/O, and if you've completely
foregone kernel threads like node.js appears to do, then your entire
server is now blocked!&lt;p&gt;

Anyway, baving seen some interesting &lt;a href="http://www.synchrosinteractive.com/blog/9-nodejs/22-nodejs-has-a-bright-future"&gt;node.js benchmarking&lt;/a&gt;, I was obviously curious to see how
my little embedded Objective-C http-server based on the awesome &lt;a href="http://www.gnu.org/software/libmicrohttpd/"&gt;GNU microhttp &lt;/a&gt; stacked up.&lt;p&gt;

The baseline is a typical static serving test, where Apache
(out-of-the box configuration on Mac OS X client)
serves a small static file and the two app servers serve a small
static string.
&lt;table style="margin-top: 10px; border: 1px solid black;"&gt;
&lt;tbody&gt;
&lt;tr style="text-decoration: underline;"&gt;
&lt;td&gt;Platform&lt;/td&gt;   &lt;td&gt;# requests/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static (via Apache)&lt;/td&gt; &lt;td&gt;6651.58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;  &lt;td&gt;5793.44&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MPWHttp&lt;/td&gt; &lt;td&gt;8557.83&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

The sleep(2) example showed node.js at it's best.  Here,
each requests sleeps for 2 seconds before returning a
small result.

&lt;table style="margin-top: 10px; border: 1px solid black;"&gt;
&lt;tbody&gt;
&lt;tr style="text-decoration: underline;"&gt;
&lt;td&gt;Platform&lt;/td&gt;   &lt;td&gt;# requests/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static (via Apache)&lt;/td&gt; &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;  &lt;td&gt;88.48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MPWHttp&lt;/td&gt; &lt;td&gt;47.04&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

The compute example is where MPWHTTP shines.  The task is trivial,
just counting up from 1 to 10000000 (ten million).

&lt;table style="margin-top: 10px; border: 1px solid black;"&gt;
&lt;tbody&gt;
&lt;tr style="text-decoration: underline;"&gt;
&lt;td&gt;Platform&lt;/td&gt;   &lt;td&gt;# requests/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static (via Apache)&lt;/td&gt; &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;  &lt;td&gt;9.62&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MPWHttp&lt;/td&gt; &lt;td&gt;7698.65&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

So counting up, libµhttp with MPWHTTP is almost a thousand times faster?  The reason is of course that such a simple task is taken care of by
strength reduction in the 
optimizer, which replaces the loop 10 million increments with a single addition of 10 million.  Cheating?  On a benchmark, probably, but 
on the other hand that's the sort of 
benefit you get from a good optimizing compiler.&lt;p&gt;

To make the comparison a little bit more fair, I added an xor with
a randomly initialized value so that the optimizer could not remove
the loop (verified by varying the loop count).
&lt;table style="margin-top: 10px; border: 1px solid black;"&gt;
&lt;tbody&gt;
&lt;tr style="text-decoration: underline;"&gt;
&lt;td&gt;Platform&lt;/td&gt;   &lt;td&gt;# requests/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static (via Apache)&lt;/td&gt; &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;  &lt;td&gt;9.62&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MPWHttp&lt;/td&gt; &lt;td&gt;222.9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
So still around 20 times faster.  It was also using both cores of my
Mac Book Pro, whereas node.js was limited to 1 core (so 10x single core
speed difference). &lt;p&gt;

Cross-checking on my 8 core Mac Pro gave the following results:
&lt;table style="margin-top: 10px; border: 1px solid black;"&gt;
&lt;tbody&gt;
&lt;tr style="text-decoration: underline;"&gt;
&lt;td&gt;Platform&lt;/td&gt;   &lt;td&gt;# requests/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static (via Apache)&lt;/td&gt; &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;  &lt;td&gt;10.72&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MPWHttp&lt;/td&gt; &lt;td&gt;1011.86&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
Due to utilzing the available cores, MPWHTTP/µhttp is now
100 times faster than node.js on the compute-bound task.&lt;p&gt;

In conclusion, I think it is fair to say that node.js succeeds
admirably in a certain category of tasks:  lots of concurrency,
lots of blocked I/O, very little computation, very little memory
use so we don't page fault.  In more typical mixes with some
concurrency, some computation some I/O and a bit of memory use
(so chances of paging), a more balanced approach may be better.&lt;p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-7877522647032607151?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/7877522647032607151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=7877522647032607151' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/7877522647032607151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/7877522647032607151'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2011/01/nodejs-performance-httpd-performance.html' title='Node.js performance?  µhttpd performance!'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-229366204971713573</id><published>2010-12-02T09:50:00.001-08:00</published><updated>2010-12-02T09:50:50.185-08:00</updated><title type='text'>This is not LISP</title><content type='html'>Patrick Logan &lt;a href="http://patricklogan.blogspot.com/2010/11/this-is-not-one-on-lisp.html"&gt;counters&lt;/a&gt; complaints about LISP's syntax with the following example of LISP's power:
&lt;blockquote&gt;
	(apply + (take 1000 (iterate inc 1)))
&lt;/blockquote&gt;
Hmm..I find the following preferable:
&lt;blockquote&gt;
	 (1 to: 1000 ) reduce + 0
&lt;/blockquote&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-229366204971713573?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/229366204971713573/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=229366204971713573' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/229366204971713573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/229366204971713573'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2010/12/this-is-not-lisp.html' title='This is not LISP'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-307030896494066705</id><published>2010-05-09T14:47:00.001-07:00</published><updated>2010-05-09T23:08:31.853-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>iPhone XML:  from DOM to incremental SAX</title><content type='html'>&lt;p&gt;In my &lt;a href="http://blog.metaobject.com/2010/05/xml-performance-revisited.html"&gt;last post&lt;/a&gt;, I extended &lt;a href="http://www.raywenderlich.com/"&gt;Ray Wenderlich's&lt;/a&gt; XML &lt;a href="http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project"&gt;parser comparison&lt;/a&gt; to MAX, and performance seemed to be about 2x better than the nearest competitor, TBXML and around 7x faster than NSXMLParser, Cocoa Touch's built-in XML parser.&lt;/p&gt;&lt;p&gt;While the resulting code has been posted &lt;a href="http://www.metaobject.com/downloads/Objective-C/XMLPerformance.tgz"&gt;here&lt;/a&gt;, I haven't yet explained how I got there.  First, I downloaded both &lt;a href="http://www.raywenderlich.com/downloads/XMLPerformance.zip"&gt;Ray's project&lt;/a&gt; and &lt;a href="http://www.metaobject.com/downloads/Objective-C/Objective-XML-5.3.tgz"&gt;Objective-XML 5.3&lt;/a&gt;.   iPhone OS requires a bit of trickiness to get a reusable library, partly due to the fact that frameworks or dynamic libraries are not supported, partly due to the fact that simulator and device are not just different Xcode architectures of a single SDK, and not even just different SDKs, but actually different platforms.  If anyone can tell me how to create a normal target that can compile/links for both the simulator and the device, I'd love to hear about it!&lt;/p&gt;&lt;p&gt;So, in order to compile for iPhoneOS, you'll need to select the iXmlKit target:&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh4.ggpht.com/_1CNUnoa6BME/S-ctbvRWUgI/AAAAAAAAABM/RDQG6oYArt0/selector.png?imgmax=800" border="0" alt="selector.png" width="302" height="518" /&gt;&lt;/p&gt;&lt;p&gt;You'll need to build twice, changing Active SDK settings once to the the device and once to the simulator.  You will then have two copies of the &lt;span style="font-family: monospace; white-space: pre;"&gt;libiXmlKit.a ﻿&lt;span style="font-family: Helvetica; white-space: normal;"&gt;library, one in the directory Release-iphoneos﻿, the other in Release-iphonsimulator (both relative to your base build directory): &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;pre&gt;marcel@mpwls[LD]ls -la ~/programming/Build/Release-iphoneos/ &lt;/pre&gt;&lt;pre&gt;&lt;span style="font-family: monospace; white-space: pre;"&gt;-rw-r--r--   1 marcel  staff   521640 May  9 19:52 libiXmlKit.a﻿&lt;/span&gt;&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;&lt;span style="font-family: monospace; white-space: pre;"&gt;&lt;span style="font-family: Helvetica; white-space: normal;"&gt;These two copies then need to be joined together using the  &lt;span style="font-family: monospace; white-space: pre;"&gt;lipo &lt;/span&gt;command to create a single library that can be used both with the simulator and the device.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;lipo -create Release-iphoneos/libiXmlKit.a Release-iphonesimulator/libiXmlKit.a -output  Release/libiXmlKit.a&lt;/pre&gt;&lt;p&gt;(Newer Objective-XML versions will have a shell-script target that automates this process).  Once I had the fat &lt;span style="font-family: monospace; white-space: pre;"&gt;libiXmlKit.a﻿&lt;/span&gt; library, I created a new MAX group in the XMLPerformance project, and copied both the library and the MAX header file into that group:&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh5.ggpht.com/_1CNUnoa6BME/S-ctczUijpI/AAAAAAAAABU/iaCXFmXQZwE/MAX-in-XMLPerf.png?imgmax=800" border="0" alt="MAX-in-XMLPerf.png" width="500" height="386" /&gt;&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;I then created a new MAX Song parser class:&lt;/p&gt;&lt;pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; min-height: 13.0px;"&gt;&lt;span style="color: #da2e24;"&gt;&lt;span style="color: #7c492e;"&gt;#import &lt;/span&gt;"iTunesRSSParser.h"&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt; &lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="color: #c22a9f;"&gt;@interface&lt;/span&gt; MAXSongParser : iTunesRSSParser {&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #478186;"&gt;&lt;span style="color: #000000;"&gt;&lt;span style="white-space: pre;"&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;id&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;span style="white-space: pre;"&gt; &lt;/span&gt;&lt;/span&gt;parser&lt;span style="color: #000000;"&gt;;&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #743ca6;"&gt;&lt;span style="color: #000000;"&gt;&lt;span style="white-space: pre;"&gt;   &lt;/span&gt;&lt;/span&gt;NSDateFormatter&lt;span style="color: #000000;"&gt; *&lt;/span&gt;&lt;span style="color: #478186;"&gt;parseFormatter&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #c22a9f;"&gt;@end&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;The implementation is also fairly straightforward, with your basic init method:&lt;/p&gt;&lt;pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; min-height: 13.0px;"&gt; &lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt;&lt;span style="color: #7c492e;"&gt;#import &lt;/span&gt;"MAXSongParser.h"&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt;&lt;span style="color: #7c492e;"&gt;#import &lt;/span&gt;"MPWMAXParser.h"&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #7c492e;"&gt;#import &lt;span style="color: #da2e24;"&gt;"Song.h"&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; min-height: 13.0px;"&gt;&lt;span style="font-family: monospace;"&gt;&lt;span style="font-size: medium;"&gt;&lt;span style="font-family: Menlo; font-size: small;"&gt;&lt;span style="font-size: 11px;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #c22a9f;"&gt;@implementation&lt;span style="color: #000000;"&gt; MAXSongParser&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; min-height: 13.0px;"&gt; &lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;-init&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;{&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #c22a9f;"&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;self&lt;span style="color: #000000;"&gt;=[&lt;/span&gt;super&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;init&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #743ca6;"&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #478186;"&gt;parseFormatter&lt;/span&gt;&lt;span style="color: #000000;"&gt; = [[&lt;/span&gt;NSDateFormatter&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;alloc&lt;/span&gt;&lt;span style="color: #000000;"&gt;] &lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;init&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;    [&lt;/span&gt;&lt;span style="color: #478186;"&gt;parseFormatter&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;setDateStyle&lt;span style="color: #000000;"&gt;:&lt;/span&gt;NSDateFormatterLongStyle&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;    [&lt;/span&gt;&lt;span style="color: #478186;"&gt;parseFormatter&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;setTimeStyle&lt;span style="color: #000000;"&gt;:&lt;/span&gt;NSDateFormatterNoStyle&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;&lt;span style="white-space: normal; color: #478186;"&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;MPWMAXParser&lt;span style="color: #000000;"&gt; *newParser=[&lt;/span&gt;MPWMAXParser&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parser&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;span style="font-family: Helvetica; white-space: normal;"&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;    [newParser &lt;/span&gt;setUndefinedTagAction&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #7c492e;"&gt;MAX_ACTION_NONE&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt;&lt;span style="color: #000000;"&gt;    [newParser &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;setHandler&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;forElements&lt;/span&gt;&lt;span style="color: #000000;"&gt;:[&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSArray &lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;arrayWithObjects&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;span style="color: #da2e24;"&gt;                 @"item"&lt;span style="color: #000000;"&gt;,&lt;/span&gt;@"album"&lt;span style="color: #000000;"&gt;,&lt;span style="color: #da2e24;"&gt;@"title"&lt;span style="color: #000000;"&gt;,&lt;/span&gt;@"channel"&lt;span style="color: #000000;"&gt;,&lt;/span&gt;@"rss"&lt;span style="color: #000000;"&gt;,&lt;/span&gt;@"category"&lt;span style="color: #000000;"&gt;,&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;nil&lt;/span&gt;&lt;span style="color: #000000;"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt;&lt;span style="color: #000000;"&gt;&lt;span style="color: #da2e24;"&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;              inNamespace&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;nil&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;prefix&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;@""&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;map&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;nil&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt;&lt;span style="color: #000000;"&gt;    [newParser &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;setHandler&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;forElements&lt;/span&gt;&lt;span style="color: #000000;"&gt;:[&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSArray&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;arrayWithObjects&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;                @"releasedate"&lt;span style="color: #000000;"&gt;,&lt;/span&gt;@"artist"&lt;span style="color: #000000;"&gt;,&lt;/span&gt;@"album"&lt;span style="color: #000000;"&gt;,&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;nil&lt;/span&gt;&lt;span style="color: #000000;"&gt;]&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt;&lt;span style="color: #000000;"&gt;&lt;span style="color: #da2e24;"&gt;&lt;span style="color: #2b595d;"&gt;             inNamespace&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;@"http://phobos.apple.com/rss/1.0/modules/itms/"&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #da2e24;"&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;            prefix&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;@""&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #2b595d;"&gt;map&lt;/span&gt;&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;nil&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="color: #478186;"&gt;    &lt;/span&gt;&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;=[newParser &lt;span style="color: #3f1c7e;"&gt;retain&lt;/span&gt;];&lt;/p&gt;&lt;div&gt;&lt;span style="font-family: Menlo; font-size: small;"&gt;&lt;span style="font-size: 11px;"&gt;﻿   &lt;span style="color: #c22a9f;"&gt;return&lt;span style="color: #000000;"&gt; &lt;/span&gt;self&lt;span style="color: #000000;"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/p&gt;&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;The MAX parser is initialized in this init method.  We define the elements we care about using the two "setHandler:forElements:inNamespace:prefix:map:" messages, one for each namespace we will be dealing with.  In the default (RSS) namespace, we are interested in the "item", "album", "title", "channel", "rss" and "category" elements.  In Apple's special "itms" namespace, we will handle "releasdate", "artist" and "album".  Setting &lt;span style="color: #7c492e; font-family: Menlo; font-size: 11px;"&gt;MAX_ACTION_NONE&lt;span style="color: #000000; font-family: Helvetica; font-size: medium;"&gt; as the undefined tag action means that the parser will ignore elements not listed as interesting and all their sub-elements.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Songs are created in the -itemElement:... method, which turns the relevant child-elements of the item element into Song attributes:&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;-itemElement:children attributes:attributes parser:&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;&lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="color: #478186;"&gt;   Song&lt;/span&gt; *song=[[&lt;span style="color: #478186;"&gt;Song&lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;alloc&lt;/span&gt;] &lt;span style="color: #3f1c7e;"&gt;init&lt;/span&gt;];&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;   [song &lt;/span&gt;setAlbum&lt;span style="color: #000000;"&gt;:[children &lt;/span&gt;objectForUniqueKey&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #da2e24;"&gt;@"album"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;   [song &lt;span style="color: #3f1c7e;"&gt;setTitle&lt;/span&gt;:[children &lt;span style="color: #2b595d;"&gt;objectForUniqueKey&lt;/span&gt;:&lt;span style="color: #da2e24;"&gt;@"title"&lt;/span&gt;]];&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;   [song &lt;/span&gt;setArtist&lt;span style="color: #000000;"&gt;:[children &lt;/span&gt;objectForUniqueKey&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #da2e24;"&gt;@"artist"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;   [song &lt;/span&gt;setAlbum&lt;span style="color: #000000;"&gt;:[children &lt;/span&gt;objectForUniqueKey&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #da2e24;"&gt;@"album"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;   [song &lt;/span&gt;setCategory&lt;span style="color: #000000;"&gt;:[children &lt;/span&gt;objectForUniqueKey&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #da2e24;"&gt;@"category"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;   [song &lt;/span&gt;setReleaseDate&lt;span style="color: #000000;"&gt;:[&lt;/span&gt;&lt;span style="color: #478186;"&gt;parseFormatter&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;dateFromString&lt;/span&gt;&lt;span style="color: #000000;"&gt;:[children &lt;/span&gt;objectForUniqueKey&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #da2e24;"&gt;@"releasedate"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]] ];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008419;"&gt;&lt;span style="color: #000000;"&gt;&lt;span style="color: #c22a9f;"&gt;   return&lt;/span&gt; song;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/pre&gt;&lt;p&gt;Two more methods make the actual parse process complete:  &amp;lt;channel&amp;gt; elements have one or more &amp;lt;item&amp;gt; elements, so we want to return all of them, using "objectsForKey:":&lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;-channelElement:children attributes:attributes parser:&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="color: #c22a9f;"&gt;  return&lt;/span&gt; [[children &lt;span style="color: #2b595d;"&gt;objectsForKey&lt;/span&gt;:&lt;span style="color: #da2e24;"&gt;@"item"&lt;/span&gt;] &lt;span style="color: #3f1c7e;"&gt;retain&lt;/span&gt;];&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/pre&gt;&lt;p&gt;Finally, there are a bunch of elements that we have defined interest in but treat identically...these can be handled using the "default" element handler:&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;-defaultElement:children attributes:attributes parser:&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;{&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="color: #c22a9f;"&gt;  return&lt;/span&gt; [[children &lt;span style="color: #3f1c7e;"&gt;lastObject&lt;/span&gt;] &lt;span style="color: #3f1c7e;"&gt;retain&lt;/span&gt;];&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;That concludes the routines that actually parse the XML into objects, now for kicking off the parser. With the timing code removed, the method is fairly straightforward:&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;- (&lt;span style="color: #c22a9f;"&gt;void&lt;/span&gt;)downloadAndParse:(&lt;span style="color: #743ca6;"&gt;NSURL&lt;/span&gt; *)url {&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #743ca6;"&gt;&lt;span style="color: #000000;"&gt;   &lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;id&lt;/span&gt;&lt;span style="color: #000000;"&gt; pool=[&lt;/span&gt;NSAutoreleasePool&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   [&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parse&lt;/span&gt;:&lt;span style="color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt; [&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSData&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;dataWithContentsOfURL&lt;span style="color: #000000;"&gt;:url]&lt;/span&gt;&lt;/span&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="color: #c22a9f;"&gt;   for&lt;/span&gt; ( &lt;span style="color: #c22a9f;"&gt;id&lt;/span&gt; song &lt;span style="color: #c22a9f;"&gt;in&lt;/span&gt; [&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parseResult&lt;/span&gt;]﻿ ) {&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;       [&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;performSelectorOnMainThread&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;@selector&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parsedSong&lt;/span&gt;&lt;span style="color: #000000;"&gt;:)&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;            withObject&lt;span style="color: #000000;"&gt;:song &lt;/span&gt;waitUntilDone&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;NO&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;   }&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   [pool &lt;span style="color: #3f1c7e;"&gt;release&lt;/span&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/pre&gt;&lt;div&gt;&lt;span style="font-family: Menlo; font-size: small;"&gt;&lt;span style="font-size: 11px;"&gt;&lt;span style="font-family: Helvetica; font-size: medium;"&gt;&lt;pre&gt;With the timing code, it all gets a bit messier:&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;- (&lt;span style="color: #c22a9f;"&gt;void&lt;/span&gt;)downloadAndParse:(&lt;span style="color: #743ca6;"&gt;NSURL&lt;/span&gt; *)url {&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #743ca6;"&gt;&lt;span style="color: #000000;"&gt;   &lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;id&lt;/span&gt;&lt;span style="color: #000000;"&gt; pool=[&lt;/span&gt;NSAutoreleasePool&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   [&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;performSelectorOnMainThread&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;@selector&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;downloadStarted&lt;/span&gt;&lt;span style="color: #000000;"&gt;) &lt;/span&gt;withObject&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;nil&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;waitUntilDone&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;NO&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   &lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSData&lt;/span&gt;&lt;span style="color: #000000;"&gt; *data=[&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSData&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;dataWithContentsOfURL&lt;span style="color: #000000;"&gt;:url];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008419;"&gt;&lt;span style="color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   [&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;performSelectorOnMainThread&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;@selector&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;downloadEnded&lt;/span&gt;&lt;span style="color: #000000;"&gt;) &lt;/span&gt;withObject&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;nil&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;waitUntilDone&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;NO&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   &lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSTimeInterval&lt;/span&gt;&lt;span style="color: #000000;"&gt; start = [&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSDate&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;timeIntervalSinceReferenceDate&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;   [&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parse&lt;/span&gt;:data];&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="color: #c22a9f;"&gt;   for&lt;/span&gt; ( &lt;span style="color: #c22a9f;"&gt;id&lt;/span&gt; song &lt;span style="color: #c22a9f;"&gt;in&lt;/span&gt; [&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parseResult&lt;/span&gt;]﻿ ) {&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;      [&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self&lt;/span&gt;performSelectorOnMainThread&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;@selector&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parsedSong&lt;/span&gt;&lt;span style="color: #000000;"&gt;:) &lt;/span&gt;withObject&lt;span style="color: #000000;"&gt;:song &lt;/span&gt;waitUntilDone&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;NO&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;   }&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #743ca6;"&gt;   NSTimeInterval&lt;/span&gt;&lt;span style="color: #000000;"&gt; duration = [&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSDate&lt;/span&gt;timeIntervalSinceReferenceDate&lt;span style="color: #000000;"&gt;] - start;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   [&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self&lt;/span&gt;performSelectorOnMainThread&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;@selector&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;addToParseDuration&lt;/span&gt;&lt;span style="color: #000000;"&gt;:) &lt;/span&gt;withObject&lt;span style="color: #000000;"&gt;:[&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSNumber&lt;/span&gt;numberWithDouble&lt;span style="color: #000000;"&gt;:duration] &lt;/span&gt;waitUntilDone&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;NO&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   [&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self&lt;/span&gt;performSelectorOnMainThread&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;@selector&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parseEnded&lt;/span&gt;&lt;span style="color: #000000;"&gt;) &lt;/span&gt;withObject&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;nil&lt;/span&gt;waitUntilDone&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;NO&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt; [[&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSURLCache&lt;/span&gt;sharedURLCache&lt;span style="color: #000000;"&gt;] &lt;/span&gt;removeAllCachedResponses&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008419;"&gt;&lt;span style="color: #000000; white-space: pre;"&gt;﻿﻿ [pool &lt;span style="color: #3f1c7e;"&gt;release&lt;/span&gt;];&lt;/span&gt;&lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;This produces a non-incremental DOM-style parser, so we first download the data, then process it into a DOM and finally transfer the processed objects to the main thread for display.  It differs from other DOM-syle XML parsers in that it actually produces domain objects (as a Domain Object Model parser arguably should), rather than a generic XML DOM that must then be converted to objects.&lt;/p&gt;&lt;p&gt;Turning the DOM-style parser into a SAX-stye parser is almost completely trivial.  Instead of returning the Song objects at the end of itemElement:..&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;   [song &lt;/span&gt;setReleaseDate&lt;span style="color: #000000;"&gt;:[&lt;/span&gt;&lt;span style="color: #478186;"&gt;parseFormatter&lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;dateFromString&lt;/span&gt;&lt;span style="color: #000000;"&gt;:[children &lt;/span&gt;objectForUniqueKey&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #da2e24;"&gt;@"releasedate"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]] ];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008419;"&gt;&lt;span style="color: #000000;"&gt;﻿  &lt;span style="color: #c22a9f;"&gt;return&lt;/span&gt; song;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;we instead pass them to the delegate there and return nil so no tree is constructed:&lt;/p&gt;&lt;div&gt;&lt;span style="font-family: Menlo; font-size: small;"&gt;&lt;span style="font-size: 11px;"&gt;&lt;span style="font-family: Helvetica; font-size: medium;"&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;   [song &lt;/span&gt;setReleaseDate&lt;span style="color: #000000;"&gt;:[&lt;/span&gt;&lt;span style="color: #478186;"&gt;parseFormatter&lt;/span&gt;&lt;span style="color: #3f1c7e;"&gt;dateFromString&lt;/span&gt;&lt;span style="color: #000000;"&gt;:[children &lt;/span&gt;objectForUniqueKey&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #da2e24;"&gt;@"releasedate"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]] ];&lt;/span&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt; [&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;self &lt;/span&gt;performSelectorOnMainThread&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;@selector&lt;/span&gt;&lt;span style="color: #000000;"&gt;(&lt;/span&gt;&lt;span style="color: #2b595d;"&gt;parsedSong&lt;/span&gt;&lt;span style="color: #000000;"&gt;:)&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;withObject&lt;span style="color: #000000;"&gt;:song &lt;/span&gt;waitUntilDone&lt;span style="color: #000000;"&gt;:&lt;/span&gt;&lt;span style="color: #c22a9f;"&gt;NO&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;[song &lt;span style="color: #3f1c7e;"&gt;release&lt;/span&gt;];&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="color: #c22a9f;"&gt; return&lt;/span&gt; nil;&lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008419;"&gt;&lt;span style="color: #000000;"&gt;﻿}&lt;/span&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;This means we can also remove the "channelElement" method and the for loop in downloadAndParse: that passed the Song objects to the main thread.  This is a SAX-style parser (though it doesn't use the SAX methods and does produce domain objects), but it is still non-incremental because it first downloads all the data and then parses it.  If we want to turn the SAX parser into an incremental parser that overlaps processing with downloading, there is one final tweak that fortunately further simplifies the downloadAndParse method (again with timing code removed):&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;&lt;span style="font-family: Helvetica;"&gt;&lt;span style="font-size: medium;"&gt;&lt;span style="font-family: Menlo; font-size: small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;- (&lt;span style="color: #c22a9f;"&gt;void&lt;/span&gt;)downloadAndParse:(&lt;span style="color: #743ca6;"&gt;NSURL&lt;/span&gt; *)url {&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #743ca6;"&gt;&lt;span style="color: #c22a9f;"&gt;   id&lt;/span&gt;&lt;span style="color: #000000;"&gt; pool=[&lt;/span&gt;NSAutoreleasePool&lt;span style="color: #3f1c7e;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #743ca6;"&gt;&lt;span style="color: #2b595d;"&gt;&lt;span style="color: #000000;"&gt;﻿  [&lt;/span&gt;&lt;span style="color: #478186;"&gt;parser&lt;/span&gt;parseDataFromURL&lt;span style="color: #000000;"&gt;:url];&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3f1c7e;"&gt;&lt;span style="color: #000000;"&gt;   [[&lt;/span&gt;&lt;span style="color: #743ca6;"&gt;NSURLCache&lt;/span&gt;sharedURLCache&lt;span style="color: #000000;"&gt;] &lt;/span&gt;removeAllCachedResponses&lt;span style="color: #000000;"&gt;];&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;   [pool &lt;span style="color: #3f1c7e;"&gt;release&lt;/span&gt;];&lt;/pre&gt;&lt;pre style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo;"&gt;}&lt;/pre&gt;&lt;div&gt;&lt;span style="font-family: Helvetica; font-size: medium;"&gt;&lt;p&gt;While this is probably best not only in terms of performance and responsiveness, but also in terms of code size, it doesn't play well with the XMLPerformance example, because there are no external measurement hooks that allow us to separate downloading from parsing for performance measurement purposes.&lt;/p&gt;&lt;p&gt;In addition, the XMLPerformance example is odd in that is both multi-threaded and measure real time rather than CPU time when measuring parse performance.  The reason this is odd is that when both parsing and display are active, the scheduler can take the CPU away from the XML parsing thread at any time and switch to the display thread, but this time will be counted as XML parsing time by the measurement routines.  This is obviously incorrect and penalizes all the incremental parsers, which is why Ray's comparison showed all the DOM parsers as performing better than the SAX parsers.&lt;/p&gt;&lt;p&gt;I hope these explanations show how to create different styles of parsers using MAX.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p style="font: normal normal normal 11px/normal Menlo; margin: 0px;"&gt;&lt;span style="font-family: Helvetica;"&gt;&lt;span style="font-size: medium;"&gt;&lt;span style="font-family: Menlo; font-size: small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-307030896494066705?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/307030896494066705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=307030896494066705' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/307030896494066705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/307030896494066705'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2010/05/iphone-xml-from-dom-to-incremental-sax.html' title='iPhone XML:  from DOM to incremental SAX'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_1CNUnoa6BME/S-ctbvRWUgI/AAAAAAAAABM/RDQG6oYArt0/s72-c/selector.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-5477171120024299657</id><published>2010-05-02T12:12:00.001-07:00</published><updated>2010-05-08T13:34:33.102-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Threading'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>iPhone XML Performance Revisited</title><content type='html'>&lt;p&gt;&lt;a href="http://www.raywenderlich.com/"&gt;Ray Wenderlich&lt;/a&gt; has done a great &lt;a href="http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project"&gt;comparison&lt;/a&gt; of iPhone XML parsers, using the same &lt;a href="http://developer.apple.com/iphone/library/samplecode/XMLPerformance/Introduction/Intro.html"&gt;sample&lt;/a&gt; I had &lt;a href="http://blog.metaobject.com/2009/01/iphone-xml-performance.html"&gt;looked at earlier&lt;/a&gt; in the context of responsiveness.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;As Ray was comparing performance, my hobby-horse, I was obviously curious as to how MAX stacked up against all the upstart competition.  Quite well, as it turns out (average of 5 runs on an iPad with 3.2):&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Figure 1: total times (seconds) &lt;img src="http://chart.apis.google.com/chart?chs=570x200&amp;amp;chco=FF0000&amp;amp;chxt=y&amp;amp;chxr=0,0,1.4&amp;amp;chbh=20,30,35&amp;amp;chds=0,1.4&amp;amp;chd=t:0.2226,0.425,0.6844,0.723,0.856,0.987,0.776,0.613,0.848,1.64&amp;amp;cht=bvg&amp;amp;chl=Dummy|MAX|LibXML|GDataXML|TinyXML|KissXML|TouchXML|TBXML|libxml2|NSXML" alt="" /&gt;&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;MAX was about 50% faster than the closest competition, TBXML, at 0.43s vs. 0.61s.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;However, the XMLPerformance sample is a bit weird in that it measures elapsed time, not CPU time, and is multi-threaded, updating the display as results come in.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;In order to account for this overhead that has nothing to do with the XML parsers, I added a "Dummy" parser that doesn't actually parse anything, but rather just generates dummy Song entries as quickly as possible.   This takes around 0.2 seconds all by itself.  Subtracting this non-XML overhead from the total times yields the following results:&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Figure 2: XML parse times (seconds) &lt;img src="http://chart.apis.google.com/chart?chs=570x200&amp;amp;chco=FF0000&amp;amp;chxt=y&amp;amp;chxr=0,0,1.4&amp;amp;chbh=20,30,35&amp;amp;chds=0,1.4&amp;amp;chd=t:0.202,0.461,0.500,0.633,0.7641,0.553,0.39,0.625,1.2417&amp;amp;cht=bvg&amp;amp;chl=MAX|LibXML|GDataXML|TinyXML|KissXML|TouchXML|TBXML|libxml2|NSXML" alt="" /&gt;&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;When measuring just the XML parsers themselves, MAX is around twice as fast as the closest competition and seven times as fast as the built in NSXMLParser.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;Sweet.&lt;/p&gt;&lt;p&gt;[Update]  I forgot the link to where I had uploaded the source﻿: &lt;a href="http://www.metaobject.com/downloads/Objective-C/XMLPerformance.tgz"&gt;XMLPerformance.tgz﻿&lt;/a&gt; at http://www.metaobject.com/downloads/Objective-C/﻿&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-5477171120024299657?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/5477171120024299657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=5477171120024299657' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5477171120024299657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5477171120024299657'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2010/05/xml-performance-revisited.html' title='iPhone XML Performance Revisited'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-2472086524060473832</id><published>2010-04-22T19:31:00.001-07:00</published><updated>2010-04-22T19:33:26.970-07:00</updated><title type='text'>This blog has moved</title><content type='html'>
       This blog is now located at http://metaoblog.blogspot.com/.
       You will be automatically redirected in 30 seconds, or you may click &lt;a href='http://metaoblog.blogspot.com/'&gt;here&lt;/a&gt;.

       For feed subscribers, please update your feed subscriptions to
       http://metaoblog.blogspot.com/feeds/posts/default.
  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-2472086524060473832?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://metaoblog.blogspot.com/' title='This blog has moved'/><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/2472086524060473832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=2472086524060473832' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2472086524060473832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2472086524060473832'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2010/04/this-blog-has-moved.html' title='This blog has moved'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-7606244019279706390</id><published>2010-01-24T15:54:00.001-08:00</published><updated>2010-03-06T16:35:49.358-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Objective-XML 5.3</title><content type='html'>New in this release:&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.cocotron.org/"&gt;Cocotron&lt;/a&gt; targets for Windows support.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.xmlrpc.com/"&gt;XMLRPC&lt;/a&gt; support.&lt;/li&gt;
&lt;li&gt;No longer uses 'private' API that was causing AppStore rejections for some iPhone apps using Objective-XML.&lt;/li&gt;
&lt;li&gt;Support for numeric entitites.&lt;/li&gt;

&lt;/ul&gt;
&lt;a href="http://www.metaobject.com/downloads/Objective-C/Objective-XML-5.3.tgz"&gt;http://www.metaobject.com/downloads/Objective-C/Objective-XML-5.3.tgz&lt;/a&gt;.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-7606244019279706390?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/7606244019279706390/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=7606244019279706390' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/7606244019279706390'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/7606244019279706390'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2010/01/objective-xml-53.html' title='Objective-XML 5.3'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-130262743736304653</id><published>2009-12-13T13:05:00.001-08:00</published><updated>2009-12-13T13:38:45.627-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Higher Order Messaging'/><category scheme='http://www.blogger.com/atom/ns#' term='Cocoa'/><title type='text'>The Responder Chain is a Collection</title><content type='html'>Timothy Wood voices some great ideas on &lt;a href="http://www.cocoatoa.com/posts/2009/12/08/modernizing_the_responder_chain/"&gt;modernizing the Cocoa responder chain&lt;/a&gt;.   

I'd like to venture that if we treat the &lt;a href="http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html"&gt;Responder Chain&lt;/a&gt; as a simple collection, a singly-linked list, then such alternatives become easier to model and reason about.&lt;p&gt; 
&lt;hr&gt;
&lt;pre&gt;
NSEnumerator *responderEnumerator = [[firstResponder mapToNextObjectFromMessage] nextResponder];
&lt;/pre&gt;
&lt;hr&gt;
I am currently abstracting from the intricate delegate mapping and other ops, these could be handled in an analog fashion.
With the enumerator in place, we can obviously snapshot it to get the current state of the responder chain, and also log that.
&lt;hr&gt;
&lt;pre&gt;
NSArray *responders = [responderEnumerator allObjects];
NSLog(@"full responder chain:  %@",responders);
&lt;/pre&gt;
&lt;hr&gt;

Now we can express both current features and possible variations of the Responder Chain architecture compactly as common collection operations.  The current dispatch mechanism simply sends the message to the first object that is capable of responding.  This corresponds to using the first object of a &lt;em&gt;-select&lt;/em&gt;, which is expressed in the -selectFirst convenience method.

&lt;hr&gt;
&lt;h4&gt;Current dispatch&lt;/h4&gt; 
&lt;pre&gt;
[[[responders selectFirst] respondsToSelector:action] performSelector:action withObject:sender];

&lt;/pre&gt;
&lt;hr&gt;

If I understood him correctly, Tim wants the objects in the responder chain to return an object that they would like to respond to the message.  This turns the &lt;em&gt;-select&lt;/em&gt; into a &lt;em&gt;-collect&lt;/em&gt; (without a &lt;em&gt;-collectFirst&lt;/em&gt;), but is otherwise very similar.

&lt;hr&gt;
&lt;h4&gt;Tim's dispatch&lt;/h4&gt; 
&lt;pre&gt;
possibleResponders = [[responders collect] responsibleTargetForAction:theAction sender:sender]];
[[possibleResponders objectAtIndex:0] performSelector:action withObject:sender];
&lt;/pre&gt;
&lt;hr&gt;
I hope this does Tim's ideas justice, but I think the succinct formulation should make it easy to tell wether it does or not.&lt;p&gt;
In terms of combining validation with target/action, I'd be somewhat
wary of accidentally triggering actions when validation was meant, though I do appreciate the advantages of combining the two operations.
I am not sure what value the block is adding over just having an additional BOOL parameter in the target/action method.&lt;hr&gt;
&lt;h4&gt;Combined action and validation&lt;/h4&gt; 
&lt;pre&gt;
typedef BOOL IBAction;
-(IBAction)delete:sender  :(BOOL)onlyValidate
{
    NSArray *selection = [self selectedItems];

   if ( onlyValidate || [selection count] == 0 ) {
        return NO;
   }
   // perform the action
}
// or if you're worried about the naming issues
-(IBAction)delete:sender
{
}
&lt;/pre&gt;
&lt;hr&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-130262743736304653?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/130262743736304653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=130262743736304653' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/130262743736304653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/130262743736304653'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/12/responder-chain-is-collection.html' title='The Responder Chain is a Collection'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-6769487009675904692</id><published>2009-12-09T08:38:00.001-08:00</published><updated>2009-12-09T08:38:52.656-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><category scheme='http://www.blogger.com/atom/ns#' term='Cocoa'/><title type='text'>Simple Objective-XML example</title><content type='html'>&lt;html&gt;
&lt;head&gt;

  &lt;style type="text/css"&gt;
    p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Menlo}
    p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Menlo; color: #3f1c7e}
    p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Menlo; color: #da2e24}
    p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Menlo; color: #c22a9f}
    p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Menlo; color: #743ca6}
    p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 10.0px Menlo; color: #008419}
    span.s1 {color: #c22a9f}
    span.s2 {color: #743ca6}
    span.s3 {color: #000000}
    span.s4 {color: #da2e24}
    span.s5 {color: #3f1c7e}
    span.s6 {color: #7c492e}
    span.s7 {color: #2b595d}
    span.Apple-tab-span {white-space:pre}
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

Many times now, I've been asked about more Objective-XML examples.  Here's a very simple one.  It is adapted from Marcus Zarra's very helpful &lt;a href="http://www.cimgf.com/2008/08/18/cocoa-tutorial-libxml-and-xmlreader/"&gt;libxml and xmlreader tutorial&lt;/a&gt;.  That tutorial shows how to parse a very simple XML format using libxml2.&lt;p&gt;

The XML file parsed is the following:

&lt;blockquote&gt;
&lt;p class="p3"&gt;&amp;lt;?xml version=&lt;span class="s1"&gt;"1.0"&lt;/span&gt; encoding=&lt;span class="s1"&gt;"UTF-8"&lt;/span&gt;?&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&amp;lt;root&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;  &lt;/span&gt;&lt;/span&gt;&amp;lt;person&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;    &lt;/span&gt;&lt;/span&gt;&amp;lt;name&amp;gt;&lt;span class="s2"&gt;John Doe&lt;/span&gt;&amp;lt;/name&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;    &lt;/span&gt;&lt;/span&gt;&amp;lt;age&amp;gt;&lt;span class="s2"&gt;14&lt;/span&gt;&amp;lt;/age&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;  &lt;/span&gt;&lt;/span&gt;&amp;lt;/person&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;  &lt;/span&gt;&lt;/span&gt;&amp;lt;person&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;    &lt;/span&gt;&lt;/span&gt;&amp;lt;name&amp;gt;&lt;span class="s2"&gt;Mary Doe&lt;/span&gt;&amp;lt;/name&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;    &lt;/span&gt;&lt;/span&gt;&amp;lt;age&amp;gt;&lt;span class="s2"&gt;14&lt;/span&gt;&amp;lt;/age&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;  &lt;/span&gt;&lt;/span&gt;&amp;lt;/person&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;  &lt;/span&gt;&lt;/span&gt;&amp;lt;person&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;    &lt;/span&gt;&lt;/span&gt;&amp;lt;name&amp;gt;&lt;span class="s2"&gt;John Smith&lt;/span&gt;&amp;lt;/name&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;    &lt;/span&gt;&lt;/span&gt;&amp;lt;age&amp;gt;&lt;span class="s2"&gt;15&lt;/span&gt;&amp;lt;/age&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;&lt;span class="Apple-converted-space"&gt;  &lt;/span&gt;&lt;/span&gt;&amp;lt;/person&amp;gt;&lt;/p&gt;
&lt;p class="p3"&gt;&amp;lt;/root&amp;gt;&lt;/p&gt;


&lt;/blockquote&gt;

It is parsed using at application startup using the following code:

&lt;blockquote&gt;
&lt;p class="p1"&gt;- (&lt;span class="s1"&gt;void&lt;/span&gt;)applicationDidFinishLaunching:(&lt;span class="s2"&gt;NSNotification&lt;/span&gt;*)notification&lt;/p&gt;
&lt;p class="p1"&gt;{&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s2"&gt;NSString&lt;/span&gt;&lt;span class="s3"&gt; *path = [[&lt;/span&gt;&lt;span class="s2"&gt;NSBundle&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;mainBundle&lt;span class="s3"&gt;] &lt;/span&gt;pathForResource&lt;span class="s3"&gt;:&lt;/span&gt;&lt;span class="s4"&gt;@"xmlExample"&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;ofType&lt;span class="s3"&gt;:&lt;/span&gt;&lt;span class="s4"&gt;@"xml"&lt;/span&gt;&lt;span class="s3"&gt;];&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s2"&gt;NSData&lt;/span&gt; *xmlData = [&lt;span class="s2"&gt;NSData&lt;/span&gt; &lt;span class="s5"&gt;dataWithContentsOfFile&lt;/span&gt;:path];&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s2"&gt;xmlTextReaderPtr&lt;/span&gt; reader = &lt;span class="s5"&gt;xmlReaderForMemory&lt;/span&gt;([xmlData &lt;span class="s5"&gt;bytes&lt;/span&gt;],&lt;/p&gt;
&lt;p class="p1"&gt;&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;[xmlData &lt;span class="s5"&gt;length&lt;/span&gt;],&lt;span class="Apple-converted-space"&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;[path &lt;span class="s5"&gt;UTF8String&lt;/span&gt;], &lt;span class="s1"&gt;nil&lt;/span&gt;,&lt;span class="Apple-converted-space"&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;(&lt;/span&gt;XML_PARSE_NOBLANKS&lt;span class="s3"&gt; | &lt;/span&gt;XML_PARSE_NOCDATA&lt;span class="s3"&gt; | &lt;/span&gt;XML_PARSE_NOERROR&lt;span class="s3"&gt; | &lt;/span&gt;XML_PARSE_NOWARNING&lt;span class="s3"&gt;));&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s1"&gt;if&lt;/span&gt; (!reader) {&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s5"&gt;NSLog&lt;/span&gt;&lt;span class="s3"&gt;(&lt;/span&gt;@"Failed to load xmlreader"&lt;span class="s3"&gt;);&lt;/span&gt;&lt;/p&gt;
&lt;p class="p4"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;return&lt;span class="s3"&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s2"&gt;NSString&lt;/span&gt; *currentTagName = &lt;span class="s1"&gt;nil&lt;/span&gt;;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s2"&gt;NSDictionary&lt;/span&gt; *currentPerson = &lt;span class="s1"&gt;nil&lt;/span&gt;;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s2"&gt;NSString&lt;/span&gt; *currentTagValue = &lt;span class="s1"&gt;nil&lt;/span&gt;;&lt;/p&gt;
&lt;p class="p5"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;NSMutableArray&lt;span class="s3"&gt; *people = [&lt;/span&gt;NSMutableArray&lt;span class="s3"&gt; &lt;/span&gt;&lt;span class="s5"&gt;array&lt;/span&gt;&lt;span class="s3"&gt;];&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s1"&gt;char&lt;/span&gt;* temp;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s1"&gt;while&lt;/span&gt; (&lt;span class="s6"&gt;true&lt;/span&gt;) {&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s1"&gt;if&lt;/span&gt;&lt;span class="s3"&gt; (!&lt;/span&gt;xmlTextReaderRead&lt;span class="s3"&gt;(reader)) &lt;/span&gt;&lt;span class="s1"&gt;break&lt;/span&gt;&lt;span class="s3"&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s1"&gt;switch&lt;/span&gt;&lt;span class="s3"&gt; (&lt;/span&gt;xmlTextReaderNodeType&lt;span class="s3"&gt;(reader)) {&lt;/span&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;XML_READER_TYPE_ELEMENT&lt;span class="s3"&gt;:&lt;/span&gt;&lt;/p&gt;
&lt;p class="p6"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;//We are starting an element&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;temp =&lt;span class="Apple-converted-space"&gt;  &lt;/span&gt;(&lt;span class="s1"&gt;char&lt;/span&gt;*)&lt;span class="s5"&gt;xmlTextReaderConstName&lt;/span&gt;(reader);&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;currentTagName = [&lt;span class="s2"&gt;NSString&lt;/span&gt; &lt;span class="s5"&gt;stringWithCString&lt;/span&gt;:temp&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;encoding&lt;span class="s3"&gt;:&lt;/span&gt;NSUTF8StringEncoding&lt;span class="s3"&gt;];&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s1"&gt;if&lt;/span&gt; ([currentTagName &lt;span class="s5"&gt;isEqualToString&lt;/span&gt;:&lt;span class="s4"&gt;@"person"&lt;/span&gt;]) {&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;currentPerson = [&lt;span class="s2"&gt;NSMutableDictionary&lt;/span&gt; &lt;span class="s5"&gt;dictionary&lt;/span&gt;];&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;[people &lt;span class="s5"&gt;addObject&lt;/span&gt;:currentPerson];&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;}&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/p&gt;
&lt;p class="p4"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;continue&lt;span class="s3"&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s1"&gt;case&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;XML_READER_TYPE_TEXT&lt;span class="s3"&gt;:&lt;/span&gt;&lt;/p&gt;
&lt;p class="p6"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;//The current tag has a text value, stick it into the current person&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;temp = (&lt;span class="s1"&gt;char&lt;/span&gt;*)&lt;span class="s5"&gt;xmlTextReaderConstValue&lt;/span&gt;(reader);&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;currentTagValue = [&lt;span class="s2"&gt;NSString&lt;/span&gt; &lt;span class="s5"&gt;stringWithCString&lt;/span&gt;:temp&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;encoding&lt;span class="s3"&gt;:&lt;/span&gt;NSUTF8StringEncoding&lt;span class="s3"&gt;];&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="s1"&gt;if&lt;/span&gt; (!currentPerson) &lt;span class="s1"&gt;return&lt;/span&gt;;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;[currentPerson &lt;span class="s5"&gt;setValue&lt;/span&gt;:currentTagValue &lt;span class="s5"&gt;forKey&lt;/span&gt;:currentTagName];&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;currentTagValue = &lt;span class="s1"&gt;nil&lt;/span&gt;;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;currentTagName = &lt;span class="s1"&gt;nil&lt;/span&gt;;&lt;/p&gt;
&lt;p class="p4"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;default&lt;span class="s3"&gt;: &lt;/span&gt;continue&lt;span class="s3"&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;}&lt;/p&gt;
&lt;p class="p3"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s5"&gt;NSLog&lt;/span&gt;&lt;span class="s3"&gt;(&lt;/span&gt;@"%@:%s Final data: %@"&lt;span class="s3"&gt;, [&lt;/span&gt;&lt;span class="s1"&gt;self&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;&lt;span class="s5"&gt;class&lt;/span&gt;&lt;span class="s3"&gt;], &lt;/span&gt;&lt;span class="s1"&gt;_cmd&lt;/span&gt;&lt;span class="s3"&gt;, people);&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;[&lt;span class="s1"&gt;self&lt;/span&gt; &lt;span class="s7"&gt;setRecords&lt;/span&gt;:people];&lt;/p&gt;
&lt;p class="p1"&gt;}&lt;/p&gt;

&lt;/blockquote&gt;

To parse it using MAX you need to add MPWXmlKit and MPWFoundation to your project, and then replace the code above with the following:

&lt;blockquote&gt;

&lt;p class="p1"&gt;- (&lt;span class="s1"&gt;void&lt;/span&gt;)applicationDidFinishLaunching:(&lt;span class="s2"&gt;NSNotification&lt;/span&gt;*)notification&lt;/p&gt;
&lt;p class="p1"&gt;{&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s2"&gt;NSString&lt;/span&gt;&lt;span class="s3"&gt; *path = [[&lt;/span&gt;&lt;span class="s2"&gt;NSBundle&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;mainBundle&lt;span class="s3"&gt;] &lt;/span&gt;pathForResource&lt;span class="s3"&gt;:&lt;/span&gt;&lt;span class="s4"&gt;@"xmlExample"&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;ofType&lt;span class="s3"&gt;:&lt;/span&gt;&lt;span class="s4"&gt;@"xml"&lt;/span&gt;&lt;span class="s3"&gt;];&lt;/span&gt;&lt;/p&gt;
&lt;p class="p2"&gt;&lt;span class="s3"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;&lt;/span&gt;&lt;span class="s2"&gt;NSArray&lt;/span&gt;&lt;span class="s3"&gt; *people=[[&lt;/span&gt;&lt;span class="s2"&gt;MPWMAXParser&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;parser&lt;span class="s3"&gt;] &lt;/span&gt;parsedDataFromURL&lt;span class="s3"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;NSURL&lt;/span&gt;&lt;span class="s3"&gt; &lt;/span&gt;fileURLWithPath&lt;span class="s3"&gt;:path]];&lt;/span&gt;&lt;/p&gt;
&lt;p class="p1"&gt;&lt;span class="Apple-tab-span"&gt;	&lt;/span&gt;[&lt;span class="s1"&gt;self&lt;/span&gt; &lt;span class="s5"&gt;setRecords&lt;/span&gt;:people];&lt;/p&gt;
&lt;p class="p1"&gt;}&lt;/p&gt;

&lt;/blockquote&gt;

&lt;/body&gt;

&lt;/html&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-6769487009675904692?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/6769487009675904692/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=6769487009675904692' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6769487009675904692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6769487009675904692'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/12/simple-objective-xml-example.html' title='Simple Objective-XML example'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-2839549565849301289</id><published>2009-12-09T02:54:00.001-08:00</published><updated>2009-12-09T06:03:23.106-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><title type='text'>Some test-driven-development notes</title><content type='html'>A couple of random points that might be of interest:

&lt;h4&gt;Code coverage tools&lt;/h4&gt;


&lt;pre&gt;
  if ( rare-condition ) {
      -is this code tested?-
  }
&lt;/pre&gt;

If you actually followed test-first, then the code in the rare if is definitely tested, because if there isn't failing test case for the rare condition, then there is no reason for the code or the test to exist.&lt;p&gt;

Another objection could be that people won't follow the techniques.  I haven't found this to be a big or recurring practical problem so far,
and agile techniqes tend to be empirically driven.  If you suspect that this is a problem you are seeing in your environment, running a code-coverage tool to put some data behind your suspicion may be a good idea.&lt;p&gt;

&lt;h4&gt;Test before or test after?&lt;/h4&gt;

Note that the solution to the code-coverage question above does not
work if tests are written after the fact:  in this case, the rare-case is likely &lt;em&gt;not&lt;/em&gt; to be covered because it was written without being forced by a failing unit test.&lt;p&gt;

Many if not most of the benefits of TDD are related to the way they shape the
design of the code, all of these benefits obviously don't accrue
if you've already designed or even written the code.  In fact, if you ask the XP
folks about it, they will tell you that TDD is not for ensuring
quality, it is exclusively for helping with coding and design.&lt;p&gt;


For example, figuring out
how to test something will force you to come to a clarity about
what the code is supposed to do that just writing the code usually
does not.&lt;p&gt;

Knowing that your tests cover your code (see above) allows you to do extremely
radical refactorings at any point in the development process.  The
ability to refactor at any time in turn allows you to keep your
initial designs simple without coding for anticipated changes.  Not
coding for anticipated changes that may not occur or may occur
differently than you expect in turns allows you to move more quickly,
which more than pays for the expense of the tests.&lt;p&gt;

Furthermore, the tests force you to think how you can call the
functionality you are about to implement, which means it shapes
architecture towards simplicity, high cohesion and low-coupling.&lt;p&gt;

&lt;h4&gt;Generating tests&lt;/h4&gt;

Auto-generating tests for existing methods is a means of subverting the test-driven approach:  there will be the appearance of testing, but with
virtually none of the benefits.  It is probably worse than not having
tests, because in the latter case you at least know that you're not
covered.&lt;p&gt;

Is it a good way of starting with unit test coverage for legacy code?  No.  See the &lt;a href="http://c2.com/cgi/wiki?RefactoringLegacyCode"&gt;C2 wiki entry&lt;/a&gt; for a good explanation of how to approach this case.  In short, start refactoring and adding unit tests when you actually need to touch the code, 
be it for new features or to fix defects that are scheduled to be fixed.&lt;p&gt;



&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-2839549565849301289?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/2839549565849301289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=2839549565849301289' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2839549565849301289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2839549565849301289'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/12/some-test-driven-development-notes.html' title='Some test-driven-development notes'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-9046487851133964083</id><published>2009-11-10T00:25:00.001-08:00</published><updated>2009-11-10T00:25:25.035-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Higher Order Messaging'/><category scheme='http://www.blogger.com/atom/ns#' term='Blocks'/><category scheme='http://www.blogger.com/atom/ns#' term='Smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='Cocoa'/><title type='text'>Blocked-C II</title><content type='html'>&lt;a href="http://people.untyped.org/damien.pollet/"&gt;Damien Pollet&lt;/a&gt; thinks my &lt;a href="http://www.metaobject.com/blog/2009/11/blocked-c.html"&gt;comparison between Objective-C blocks and HOM&lt;/a&gt; is not completely fair:
&lt;blockquote&gt;
… from my (Smalltalk) experience, the block passed to #collect: is often not a single message send, but rather a small adhoc expression, for which it does not really make sense to define a named method. Or you might need both the element and its key/index… how does HOM deal with that?
&lt;/blockquote&gt;
These are certainly valid observations, and were some of the reasons
that I didn't really think that much of HOM for the first couple of
years after coming up with it back in 1997 or so.  Since then, I've
become less and less convinced that the problems raised are a big concern, for a number of reasons.&lt;p&gt;
&lt;h4&gt;Inline vs. Named&lt;/h4&gt;
One reason is that I actually looked at usage of blocks in the Squeak
image, and found that the majority of blocks with at least one argument
(so not ifTrue:, whileTrue: and other control structures) actually did
contain just a single message send, and so could be immediately expressed
as HOMs.  Second, I noticed that there were a lot of fairly large (3+ LOC)
blocks that &lt;em&gt;should&lt;/em&gt; have been separate methods but weren't.
That's when I discovered that the presence of blocks actually 
encourages bad code, and the 'limitation' of HOMs actually was
encouraging better(-factored) code.&lt;p&gt;
Of course, I wasn't particularly convinced by that line of reasoning,
because it smelled too much like "that's not a bug, that's a feature".
Until that is, I saw &lt;a href="http://nat.truemesh.com/"&gt;others&lt;/a&gt; with less vested interest reporting the same
&lt;a href="http://nat.truemesh.com/archives/000535.html"&gt;observation&lt;/a&gt;:

&lt;blockquote&gt;
But are these really limitations? After using higher order messages for a while I've come to think that they are not. The first limitation encourages you move logic that belongs to an object into that object's implementation instead of in the implementation of methods of other objects. The second limitation encourages you to represent application concepts as objects rather than procedural code. Both limitations have the surprising effect of guiding the code away from a procedural style towards better object-oriented design.
&lt;/blockquote&gt;

My experience has been that Nat is right, having a mechanism that
pushes you towards factoring and naming is better for your code
that one that pushes you towards inlining and anonymizing.&lt;p&gt;

&lt;h4&gt;Objective-C I&lt;/h4&gt;

In fact, the Cocoa &lt;a href="http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/Blocks/Articles/bxGettingStarted.html"&gt;example&lt;/a&gt; that Apple gives for blocks illustrates this idea
very well.  They implement a "Finder like" sorting mechanism using blocks:

&lt;blockquote&gt;
&lt;hr&gt;
&lt;pre&gt;
static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
        NSWidthInsensitiveSearch | NSForcedOrderingSearch;
NSLocale *currentLocale = [NSLocale currentLocale];
 
NSComparator finderSort = ^(id string1, id string2) {
    NSRange string1Range = NSMakeRange(0, [string1 length]);
    return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
};
 
NSLog(@"finderSort: %@", [stringsArray sortedArrayUsingComparator:finderSort]);
&lt;/pre&gt;
&lt;hr&gt;
&lt;/blockquote&gt;
The block syntax is so verbose that there is no hope of actually defining the block inline, the supposed raison d'etre for blocks.  So we actually need to take the
block out-of-line and name it.  So it looks suspiciously like an
equivalent implementation using functions:
&lt;blockquote&gt;
&lt;hr&gt;
&lt;pre&gt;
static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
        NSWidthInsensitiveSearch | NSForcedOrderingSearch;
NSLocale *currentLocale = [NSLocale currentLocale];
 
static NSComparisonResult finderSort(id string1, id string2) {
    NSRange string1Range = NSMakeRange(0, [string1 length]);
    return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
};
 
NSLog(@"finderSort: %@", [stringsArray sortedArrayUsingFunction:finderSort context:nil hint:nil]);
&lt;/pre&gt;
&lt;hr&gt;
&lt;/blockquote&gt;
Of course, something as useful as a Finder-like comparison sort
really deserves to be exposed and made available for reuse, rather
than hidden inside one specific sort.  Objective-C categories are
just the mechanism for this sort of thing:
&lt;blockquote&gt;
&lt;hr&gt;
&lt;pre&gt;
@implementation NSString(finderCompare)
-(NSSComparisonResult)finderCompare:(NSString*)string2) {
    NSRange myRange = NSMakeRange(0, [self length]);
    return [self compare:string2 options: NSCaseInsensitiveSearch | NSNumericSearch |
        NSWidthInsensitiveSearch | NSForcedOrderingSearch range:string1Range locale:[NSLocale currentLocale]];
}
@end
NSLog(@"finderSort: %@", [stringsArray sortedArrayUsingSelector:@selector(finderCompare:)]);
&lt;/pre&gt;
&lt;hr&gt;
&lt;/blockquote&gt;
Note that some of these criticisms are specific to Apple's implementation of blocks, they do not apply in the same way to
Smalltalk blocks, which are a lot less noisy.

&lt;h4&gt;Objective-C II&lt;/h4&gt;

Objective-C has at least one other pertinent difference from
Smalltalk, which is that it already contains control structures
in the basic language, without blocks.  (Of course, those control
structures can also take blocks as arguments, but these are the
different types of blocks that are delimited by curly braces and
cannot be passed around as first class objects).&lt;p&gt;

This means that in Objective-C, we already have the ability to
do all the iterating we need, mechanisms such as blocks and 
HOM are mostly conveniences, not required building blocks.  If
we need indices, use a for loop.  If we require keys, use a
key-enumerator and iterate over that.&lt;p&gt;

In fact, I remember when my then colleagues started working
with a enum-filters, a HOM-precursor that's strikingly similar
to the Google Toolbox's &lt;a href="http://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/Foundation/GTMNSEnumerator%2BFilter.m"&gt;GTMSEnumerator+Filter.m&lt;/a&gt;.  They really took to
the elegance, but then also wanted to use it for various special
cases.  They laughed when they realized that those special-cases
were actually already handled better by existing C control structures
such as for-loops.


&lt;h4&gt;FP, HANDs and Aggregate Operations&lt;/h4&gt;
While my dislike of blocks is easy to discount by the usual
inventor's pride (your child must be ugly for mine to be pretty),
that interpretation actually reverses the  causation:  I came 
up with HOM because I was never very fond of blocks.  In fact,
when I first encountered Smalltalk during my university
years I was enthralled until I saw the iteration methods.&lt;p&gt;

That's not to say that do:, collect: and friends were not light-years
ahead of Algol-type control structures, they most definitely were
and still are.  Having some sort of higher-order mechanism is
vastly superior than not having a higher-order mechanism.
I do wish that "higher order mechanism" and "blocks" weren't
used as synonyms quite as much, because they are not, in fact,
synonymous.&lt;p&gt;

When I first encountered  Smalltalk blocks, I had just previously been
exposed to Backus's &lt;a href="http://www.stanford.edu/class/cs242/readings/backus.pdf"&gt;FP&lt;/a&gt;, and that was just so much prettier!  In
FP functions are composed using functionals without ever talking
about actual data, and certainly without talking about individual
elements.  I have always been on the lookout for higher levels
of expression, and this was such a higher level.  Now taking
things down to "here's another element, what do you want to
do with that" was definitely a step back, and quite frankly
a bit of a let-down.&lt;p&gt;

The fundamental difference I see is that in Smalltalk there
is still an iteration, even if it is encapsulated:  we iterate
over some collection and then execute some code for each element.
In FP, and in HOM, there is instead an aggregate operation:  we
take an existing operation and lift it up as applying to an entire collection.&lt;p&gt;

This difference might seem contrived, but the research done with
the &lt;a href="http://www.cs.cmu.edu/~pane/research.html"&gt;HANDS system&lt;/a&gt; demonstrates that it is very real:
&lt;blockquote&gt;
After creating HANDS, I conducted another user study to examine the effectiveness of three features of HANDS: queries, aggregate operations, and data visibility. HANDS was compared with a limited version that lacked these features. In the limited version, programmers were able to achieve the desired results but had to use more traditional programming techniques. Children using the full-featured HANDS system performed significantly better than their peers who used the limited version.
&lt;/blockquote&gt;

I also find this difference to be very real.&lt;p&gt;

The difference between iterating with blocks and lifting operations
to be aggregate operations also shows up in the fact that the lifting can be done on any 
combination of the involved parameters, whereas you tend to only
iterate over one collection at a time, because the collection and
the iteration are in focus.

&lt;h4&gt;Symmetry&lt;/h4&gt;

Finally, the comparison to functional languages shows a couple of 
interesting asymmetries:  in a functional language, higher order
functions can be applied both to named functions and to anonymous
functions.  In essence, the higher order mechanism just takes 
functions and doesn't care wether they are named or not.  Also
the higher order mechanism uses the same mechanisms (functions)
as the base system,&lt;p&gt;

With block-based higher order mechanisms, on the other hand,
we must make the argument an anonymous function (that's what
a block is), and we cannot use a named function, bringing
us back to the conundrum mentioned at the start that this
mechanisms encourages bad code.  Not only that, it also turns
out that the base mechanism (messages and methods) is different
from the higher order mechanism, which requires anonymous functions,
rather than methods.&lt;p&gt;

HOM currently solves only the latter part of this asymmetry, making
the higher order mechanism the same as the base mechanism, that
mechanism being messaging in both cases.   However, it currently 
cannot solve the other asymmetry:  where blocks support unnamed,
inline code and not named code, HOM supports named but not unnamed
code. While I think that this is the better choice in the larger
number of cases, it would be nice to actually suport both.&lt;p&gt;

One solution to this problem might be to simply support both blocks
and Higher Order Messaging, but it seems to me that the more 
elegant solution would be to support inline definition of more-or-less
anonymous methods that could then be integrated into the Higher Order
Messaging framework.&lt;p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-9046487851133964083?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/9046487851133964083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=9046487851133964083' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/9046487851133964083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/9046487851133964083'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/11/blocked-c-ii.html' title='Blocked-C II'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-842137603030076791</id><published>2009-11-07T22:55:00.001-08:00</published><updated>2009-11-08T15:37:02.394-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Objective-Smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Exploring the Weather Underground with Objective-XML and Objective-Smalltalk</title><content type='html'>Having taken up &lt;a href="http://video.google.com/videosearch?hl=en&amp;client=safari&amp;rls=en&amp;q=paragliding+mussel+rock&amp;um=1&amp;ie=UTF-8&amp;ei=BGn2SpvlJY_ysQPy3OEd&amp;sa=X&amp;oi=video_result_group&amp;ct=title&amp;resnum=5&amp;ved=0CBcQqwQwBA#"&gt;various forms&lt;/a&gt; of &lt;a href="http://www.advantage-aviation.com/accomplishments.php"&gt;flying&lt;/a&gt; last year, I have developed a strong interest in the weather, particularly wind information.  While there are various web-sites with relevant information, for example Jeff Greenbaum's excellent &lt;a href="http://www.paragliding-lessons.com/Pacifica_Wind.htm"&gt;Wind Conditions Page for Pacifica&lt;/a&gt; page, they don't really present the information quite the way I need, and also don't really work well on small mobile devices...&lt;p&gt;

Fixing that should hopefully just be ASMOP.  The &lt;a href="http://www.wunderground.com/"&gt;Weather Underground&lt;/a&gt; fortunately has some reasonably well-documented &lt;a href="http://wiki.wunderground.com/index.php/API_-_XML"&gt;XML APIs&lt;/a&gt;, let's see what they have to offer and wether we can get to the data we want.&lt;p&gt; 

First, let's fire up the interactive Smalltalk Shell (stsh) and load the Objective-XML framework.
&lt;pre&gt;
marcel@spock[Projects]stsh
&amp;gt; context loadFramework:'MPWXmlKit'
&lt;/pre&gt;
Next, let's have a look at the raw XML returned by the Weather Underground APIs. 
&lt;pre&gt;
&amp;gt; urlstr:='http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1'
&amp;gt; url:=NSURL URLWithString: urlstr
&amp;gt; NSString stringWithContentsOfURL:url.
&amp;gt; 
&lt;/pre&gt;
Hmm...no result.  (It turns out that Weather Underground checks the user agent and errors if it doesn't find one.  The various convenience methods do not send a User Agent).  Maybe curl can help?

&lt;pre&gt;
&amp;gt;context addExternalCommand:'curl'.
&amp;gt;curl run:'http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1'
&amp;lt;?xml version="1.0"?&amp;gt;
	&amp;lt;current_observation&amp;gt;
		&amp;lt;credit&amp;gt;Weather Underground Personal Weather Station&amp;lt;/credit&amp;gt;
		&amp;lt;credit_URL&amp;gt;http://wunderground.com/weatherstation/&amp;lt;/credit_URL&amp;gt;
		&amp;lt;image&amp;gt;
		&amp;lt;url&amp;gt;http://icons.wunderground.com/graphics/bh-wui_logo.gif&amp;lt;/url&amp;gt;
		&amp;lt;title&amp;gt;Weather Underground&amp;lt;/title&amp;gt;
		&amp;lt;link&amp;gt;http://wunderground.com/weatherstation/&amp;lt;/link&amp;gt;
		&amp;lt;/image&amp;gt;
		&amp;lt;location&amp;gt;
		&amp;lt;full&amp;gt;Mussel Rock, Daly City, CA&amp;lt;/full&amp;gt;
		&amp;lt;neighborhood&amp;gt;Mussel Rock&amp;lt;/neighborhood&amp;gt;
		&amp;lt;city&amp;gt;Daly City&amp;lt;/city&amp;gt;
		&amp;lt;state&amp;gt;CA&amp;lt;/state&amp;gt;
		&amp;lt;zip&amp;gt;&amp;lt;/zip&amp;gt;
		&amp;lt;latitude&amp;gt;37.667347&amp;lt;/latitude&amp;gt;
		&amp;lt;longitude&amp;gt;-122.489342&amp;lt;/longitude&amp;gt;
		&amp;lt;elevation&amp;gt;514 ft&amp;lt;/elevation&amp;gt;
		&amp;lt;/location&amp;gt;
		&amp;lt;station_id&amp;gt;KCADALYC1&amp;lt;/station_id&amp;gt;
		&amp;lt;station_type&amp;gt;Fan-aspirated Davis Vantage Pro 2 Plus&amp;lt;/station_type&amp;gt;
		&amp;lt;observation_time&amp;gt;Last Updated on November 7, 1:55 PM PST&amp;lt;/observation_time&amp;gt;
		&amp;lt;observation_time_rfc822&amp;gt;Sat, 07 November 2009 21:55:21 GMT&amp;lt;/observation_time_rfc822&amp;gt;
		&amp;lt;weather&amp;gt;&amp;lt;/weather&amp;gt;
		&amp;lt;temperature_string&amp;gt;56.9 F (13.8 C)&amp;lt;/temperature_string&amp;gt;
		&amp;lt;temp_f&amp;gt;56.9&amp;lt;/temp_f&amp;gt;
		&amp;lt;temp_c&amp;gt;13.8&amp;lt;/temp_c&amp;gt;
		&amp;lt;relative_humidity&amp;gt;83&amp;lt;/relative_humidity&amp;gt;
		&amp;lt;wind_string&amp;gt;From the NW at 15.0 MPH Gusting to 16.0 MPH&amp;lt;/wind_string&amp;gt;
		&amp;lt;wind_dir&amp;gt;NW&amp;lt;/wind_dir&amp;gt;
		&amp;lt;wind_degrees&amp;gt;313&amp;lt;/wind_degrees&amp;gt;
		&amp;lt;wind_mph&amp;gt;15.0&amp;lt;/wind_mph&amp;gt;
		&amp;lt;wind_gust_mph&amp;gt;16.0&amp;lt;/wind_gust_mph&amp;gt;
		&amp;lt;pressure_string&amp;gt;30.07" (1018.2 mb)&amp;lt;/pressure_string&amp;gt;
		&amp;lt;pressure_mb&amp;gt;1018.2&amp;lt;/pressure_mb&amp;gt;
		&amp;lt;pressure_in&amp;gt;30.07&amp;lt;/pressure_in&amp;gt;
		&amp;lt;dewpoint_string&amp;gt;51.8 F (11.0 C)&amp;lt;/dewpoint_string&amp;gt;
		&amp;lt;dewpoint_f&amp;gt;51.8&amp;lt;/dewpoint_f&amp;gt;
		&amp;lt;dewpoint_c&amp;gt;11.0&amp;lt;/dewpoint_c&amp;gt;
		
		&amp;lt;heat_index_string&amp;gt;&amp;lt;/heat_index_string&amp;gt;
		&amp;lt;heat_index_f&amp;gt;&amp;lt;/heat_index_f&amp;gt;
		&amp;lt;heat_index_c&amp;gt;&amp;lt;/heat_index_c&amp;gt;
		
		&amp;lt;windchill_string&amp;gt;&amp;lt;/windchill_string&amp;gt;
		&amp;lt;windchill_f&amp;gt;&amp;lt;/windchill_f&amp;gt;
		&amp;lt;windchill_c&amp;gt;&amp;lt;/windchill_c&amp;gt;
		
		&amp;lt;solar_radiation&amp;gt;483.00&amp;lt;/solar_radiation&amp;gt;
		&amp;lt;UV&amp;gt;2.5&amp;lt;/UV&amp;gt;
		&amp;lt;precip_1hr_string&amp;gt;0.00 in (0.0 mm)&amp;lt;/precip_1hr_string&amp;gt;
		&amp;lt;precip_1hr_in&amp;gt;0.00&amp;lt;/precip_1hr_in&amp;gt;
		&amp;lt;precip_1hr_metric&amp;gt;0.0&amp;lt;/precip_1hr_metric&amp;gt;
		&amp;lt;precip_today_string&amp;gt;0.01 in (0.0 mm)&amp;lt;/precip_today_string&amp;gt;
		&amp;lt;precip_today_in&amp;gt;0.01&amp;lt;/precip_today_in&amp;gt;
		&amp;lt;precip_today_metric&amp;gt;0.0&amp;lt;/precip_today_metric&amp;gt;
		&amp;lt;history_url&amp;gt;http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=KCADALYC1&amp;lt;/history_url&amp;gt;
		&amp;lt;ob_url&amp;gt;http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.667347,-122.489342&amp;lt;/ob_url&amp;gt;
	&amp;lt;/current_observation&amp;gt;
&amp;lt;!-- 0.029:0 --&amp;gt;
&lt;/pre&gt;
Much better.  Now let's see if we can parse that XML data into a Cocoa Property List.
&lt;pre&gt;
&amp;gt; parser := MPWMAXParser parser.
&amp;gt; parser parsedDataFromURL: 'http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1'
{
    UV = "2.5";
    credit = "Weather Underground Personal Weather Station";
    "credit_URL" = "http://wunderground.com/weatherstation/";
    "dewpoint_c" = "11.1";
    "dewpoint_f" = "51.9";
    "dewpoint_string" = "51.9 F (11.1 C)";
    "heat_index_c" =     {
    };
    "heat_index_f" =     {
    };
    "heat_index_string" =     {
    };
    "history_url" = "http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=KCADALYC1";
    image =     {
        link = "http://wunderground.com/weatherstation/";
        title = "Weather Underground";
        url = "http://icons.wunderground.com/graphics/bh-wui_logo.gif";
    };
    location =     {
        city = "Daly City";
        elevation = "514 ft";
        full = "Mussel Rock, Daly City, CA";
        latitude = "37.667347";
        longitude = "-122.489342";
        neighborhood = "Mussel Rock";
        state = CA;
        zip =         {
        };
    };
    "ob_url" = "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.667347,-122.489342";
    "observation_time" = "Last Updated on November 7, 1:55 PM PST";
    "observation_time_rfc822" = "Sat, 07 November 2009 21:55:51 GMT";
    "precip_1hr_in" = "0.00";
    "precip_1hr_metric" = "0.0";
    "precip_1hr_string" = "0.00 in (0.0 mm)";
    "precip_today_in" = "0.01";
    "precip_today_metric" = "0.0";
    "precip_today_string" = "0.01 in (0.0 mm)";
    "pressure_in" = "30.07";
    "pressure_mb" = "1018.2";
    "pressure_string" = "30.07\" (1018.2 mb)";
    "relative_humidity" = 83;
    "solar_radiation" = "482.00";
    "station_id" = KCADALYC1;
    "station_type" = "Fan-aspirated Davis Vantage Pro 2 Plus";
    "temp_c" = "13.9";
    "temp_f" = "57.0";
    "temperature_string" = "57.0 F (13.9 C)";
    weather =     {
    };
    "wind_degrees" = 342;
    "wind_dir" = NNW;
    "wind_gust_mph" = "24.0";
    "wind_mph" = "18.0";
    "wind_string" = "From the NNW at 18.0 MPH Gusting to 24.0 MPH";
    "windchill_c" =     {
    };
    "windchill_f" =     {
    };
    "windchill_string" =     {
    };
}
&lt;/pre&gt;
That looks good, we can see the wind information near the bottom of the output, with keys "wind_degrees" and "wind_mph".  So let's grab the values for those keys using the collect Higher Order Message and -objectForKey:. 
&lt;pre&gt;
&amp;gt; (parser parsedDataFromURL:'http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1' ) collect objectForKey: #( wind_mph wind_dir wind_string ) each. 
21.0
NW
From the NW at 21.0 MPH Gusting to 24.0 MPH
&lt;/pre&gt;
Almost what we wanted, except that we grabbed the wind direction as a string instead of the exact numeric direction.  Easy fix:
&lt;pre&gt;
&amp;gt; (parser parsedDataFromURL: 'http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=KCADALYC1' ) collect objectForKey: #( wind_mph wind_degrees wind_string ) each.
12.0
318
From the NW at 12.0 MPH Gusting to 24.0 MPH
&amp;gt;
&lt;/pre&gt;
Perfect.  We have the wind speed, the direction and an informative text in case we want to display that.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-842137603030076791?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/842137603030076791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=842137603030076791' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/842137603030076791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/842137603030076791'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/11/exploring-weather-underground-with.html' title='Exploring the Weather Underground with Objective-XML and Objective-Smalltalk'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-6085482498787548154</id><published>2009-11-06T00:24:00.001-08:00</published><updated>2009-11-07T13:39:32.962-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Higher Order Messaging'/><category scheme='http://www.blogger.com/atom/ns#' term='Blocks'/><category scheme='http://www.blogger.com/atom/ns#' term='Cocoa'/><title type='text'>Blocked-C</title><content type='html'>&lt;b&gt;Update:&lt;/b&gt;  It appears that the original article has been removed, and has been superseded by material at: &lt;a href="http://developer.apple.com/mac/articles/cocoa/introblocksgcd.html"&gt;http://developer.apple.com/mac/articles/cocoa/introblocksgcd.html&lt;/a&gt;.  The original article had more on the Cocoa block APIs and gave a refreshingly honest assessment of the for-loop vs. Block-iteration comparison.&lt;p&gt;


While the news that Apple is adding blocks to C and Objective-C in the SnowLeopard time frame has been around for some time, a recent &lt;a href="https://developer.apple.com/mac/articles/snowleopard/usingblockswithgrandcentraldispatch.html"&gt;article&lt;/a&gt; shed some light on the actual API.&lt;p&gt;

While there probably are some places where Objective-C blocks can be useful, I am not really impressed.  In the following samples, &lt;font color="ff0000"&gt;red&lt;/font&gt; is used to show &lt;a href="http://www.metaobject.com/blog/2009/01/semantic-noise.html"&gt;noise&lt;/a&gt;, meaning code that is just there to make the compiler happy.
&lt;hr&gt;
&lt;blockquote&gt;
&lt;pre&gt;

&lt;font color="ff0000"&gt;NSMutableArray *&lt;/font&gt;filteredItems= &lt;font color="ff0000"&gt;[NSMutableArray array];
&lt;/font&gt;[items enumerateObjects&lt;font color="ff0000"&gt;WithOptions:0 withBlock:
    ^(id item, NSUInteger index, BOOL *stop) {
        [filteredItems addObject:[item &lt;/font&gt;stringByAppendingString:@"suffix"&lt;font color="ff0000"&gt;]];
    }
];&lt;/font&gt;
&lt;/pre&gt;
&lt;/font&gt;
&lt;/blockquote&gt;
&lt;hr&gt;

As you can see, the version using blocks is very, very noisy, both syntactically and semantically, especially compared with the &lt;a href="http://www.metaobject.com/papers/Higher_Order_Messaging_OOPSLA_2005.pdf" &gt;HOM&lt;/a&gt; version:

&lt;hr&gt;
&lt;blockquote&gt;
&lt;pre&gt;
&lt;font color="ff0000"&gt;[[&lt;/font&gt;items collect&lt;font color="ff0000"&gt;]&lt;/font&gt; stringByAppendingString:@"suffix"&lt;font color="ff0000"&gt;];&lt;/font&gt;
&lt;/pre&gt;
&lt;/font&gt;
&lt;/blockquote&gt;
&lt;hr&gt;

No prizes for guessing which I'd prefer.  To put some numbers on my preference:  234 characters vs. 52, 19 tokens vs. 3, 5 lines vs. 1.  In fact, even a plain old C for-loop is more compact and less noisy than our "modern" blocked version:

&lt;hr&gt;
&lt;blockquote&gt;
&lt;pre&gt;
&lt;font color="ff0000"&gt;NSMutableArray *&lt;/font&gt;filteredItems= &lt;font color="ff0000"&gt;[NSMutableArray array];
for (int i=0; i &amp;lt; [items count]; i++ ) {
     [filteredItems addObject:[&lt;/font&gt;items &lt;font color="ff0000"&gt;objectAtIndex:i] &lt;/font&gt;stringByAppendingString:@"suffix"&lt;font color="ff0000"&gt;];
    }
];&lt;/font&gt;
&lt;/pre&gt;
&lt;/font&gt;
&lt;/blockquote&gt;
&lt;hr&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-6085482498787548154?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/6085482498787548154/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=6085482498787548154' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6085482498787548154'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6085482498787548154'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/11/blocked-c.html' title='Blocked-C'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-383704232621896542</id><published>2009-11-05T23:08:00.001-08:00</published><updated>2009-11-05T23:08:05.388-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><title type='text'>Why not Objective-C?</title><content type='html'>Patrick Logan &lt;a href="http://patricklogan.blogspot.com/2008/05/objectively.html"&gt;can't understand&lt;/a&gt; why projects use C++ rather than Ojective-C.  Neither can I.&lt;p&gt;

For the 95% (or more) of code that isn't performance sensitive, it gives you expressiveness very close to Smalltalk, and for the 5% or less that need high performance, it gets you the performance and predictability of C. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-383704232621896542?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/383704232621896542/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=383704232621896542' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/383704232621896542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/383704232621896542'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/11/why-not-objective-c.html' title='Why not Objective-C?'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-5443311908713019379</id><published>2009-09-20T00:13:00.001-07:00</published><updated>2009-09-20T00:13:27.889-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Memory management'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='Cocoa'/><title type='text'>Cocoa(touch) memory management is as easy as 1-2-3</title><content type='html'>There is a common &lt;a href="http://broadcast.oreilly.com/2009/06/big-learning-curve-for-iphone.html"&gt;misconception&lt;/a&gt; that Cocoa memory management is hard.  It's not.&lt;p&gt;

&lt;ol&gt;
&lt;li&gt;Use auto-generated accessors religiously&lt;/li&gt;
&lt;li&gt;Release your instance variables in dealloc&lt;/li&gt;
&lt;li&gt;Always use convenience methods to create objects&lt;/li&gt;
&lt;/ol&gt;

Wow, that wasn't too hard!&lt;p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-5443311908713019379?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/5443311908713019379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=5443311908713019379' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5443311908713019379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5443311908713019379'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/09/cocoatouch-memory-management-is-as-easy.html' title='Cocoa(touch) memory management is as easy as 1-2-3'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-7834470163936763598</id><published>2009-02-08T14:27:00.001-08:00</published><updated>2009-02-08T14:28:04.343-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Objective-XML-5.0.1</title><content type='html'>Just pushed out a minor bugfix release to Objective-XML-5.0:
&lt;ul&gt;
&lt;li&gt;Re-enabled character-set conversion code that had gotten disabled&lt;/li&gt;
&lt;li&gt;Fixed a compile-error for some targets&lt;/li&gt;
&lt;li&gt;Other minor improvements&lt;/li&gt;

&lt;/ul&gt;

Download here: &lt;a href="http://www.metaobject.com/downloads/Objective-C/Objective-XML-5.0.1.tgz"&gt;http://www.metaobject.com/downloads/Objective-C/Objective-XML-5.0.1.tgz&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-7834470163936763598?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/7834470163936763598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=7834470163936763598' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/7834470163936763598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/7834470163936763598'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/02/objective-xml-501.html' title='Objective-XML-5.0.1'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-8191601417333060557</id><published>2009-01-25T14:21:00.001-08:00</published><updated>2009-01-25T14:21:08.274-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Objective-XML 5.0</title><content type='html'>I've just pushed out a new release of Objective-XML, with some pretty significant new features.

&lt;h4&gt;Incremental parsing&lt;/h4&gt;

This feature, which was already discussed a little in an earlier post, is now available in an official release.  In short, Objective-XML will now stream data from network data sources (specified by URL) and produce results incrementally, rather than reading all of the data first and then parsing it.  This can make a huge difference in responsiveness and perceived performance for slow networks.  CPU and memory consumption will be slightly higher because of extra buffering and buffer stitching required, so this should only be used when necessary.

&lt;h4&gt;Static iPhone library&lt;/h4&gt;

Although Objective-XML has always been compatible with the iPhone, previous releases required copying the pre-requisite files into your project.  This burden has now been eased by the inclusion of a static library target.  You still need to copy the headers, either MPWMAXParser.h or MPWXmlParser.h (or both).

&lt;h4&gt;Unique keys&lt;/h4&gt;

Previous releases of Objective-XML had an -objectForTag:(int)tag method for quickly retrieving attribute or element values. &lt;p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;pre&gt;
enum songtags {
  item_tag=10, title_tag, category_tag	
};
...
  [parser setHandler:self forElements:[NSArray arrayWithObjects:@"item",@"title",@"category",nil]
          inNamespace:nil prefix:@"" map:nil tagBase:item_tag];
...
-itemElement:(MPWXMLAttributes*)children attributes:(MPWXMLAttributes*)attributes parser:(MPWMAXParser*)p
{
   ...
   [song setTitle:[children objectForTag:title_tag]];
   ...
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
Objective-XML adds an -objectForUniqueKey:aKey method that removes the need for these additional integer tags.
&lt;hr&gt;
&lt;blockquote&gt;
&lt;pre&gt;
...
  [parser setHandler:self forElements:[NSArray arrayWithObjects:@"item",@"title",@"category",nil]
          inNamespace:nil prefix:@"" map:nil];
...
-itemElement:(MPWXMLAttributes*)children attributes:(MPWXMLAttributes*)attributes parser:(MPWMAXParser*)p
{
   ...
   [song setTitle:[children objectForUniqueKey:@"title"]];
   ...

&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;hr&gt;

In addition to providing faster access, the integer tags also served to disambiguate tag names that might occur in multiple namespaces.  To handle these conflicts, there now is a -objectForUniqueKey:aKey namespace:aNamespace method.  The namespace objects required for this disambiguation process are now returned by the -setHandler:... and -declareAttributes:... methods, which were previously void.

&lt;h4&gt;Default methods&lt;/h4&gt;

One of the attractive features of DOM parsers is that they do something useful "out of the box":  point a DOM parser at some XML and you get back a generic in-memory representation of that XML that you can then start taking apart.  However, once you go down that road, you are stuck with the substantial CPU and memory overheads of that generic representation.&lt;p&gt;

Streaming parser like SAX or MAX can be a lot more efficient, but it takes a lot more time and effort until achieving a first useful result.  Default methods overcome this hurdle by also delivering an immediately useful generic representation without any extra work.  Unlike a DOM, however, this generic representation can be incrementally replaced by more specialized and efficient processing later on.&lt;p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-8191601417333060557?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/8191601417333060557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=8191601417333060557' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/8191601417333060557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/8191601417333060557'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/01/objective-xml-50.html' title='Objective-XML 5.0'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-3708601404734017378</id><published>2009-01-20T10:39:00.001-08:00</published><updated>2009-01-20T10:39:53.525-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Cocoa HTML parsing with Objective-XML</title><content type='html'>Although Objective-XML's MPWSAXParser mostly provides NSXMLParser compatibility it also provides a number of useful additional features.  Among these features is the ability to parse HTML files via the settings of two flags:  &lt;b&gt;enforceTagNesting&lt;/b&gt; and &lt;b&gt;ignoreCase&lt;/b&gt;.  By default, these are on and off, respectively, which gives you strict XML behavior.  However, by setting   &lt;b&gt;enforceTagNesting&lt;/b&gt; to NO and &lt;b&gt;ignoreCase&lt;/b&gt; to YES, you get a SAX parser that will happily and speedily process HTML.&lt;p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-3708601404734017378?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/3708601404734017378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=3708601404734017378' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/3708601404734017378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/3708601404734017378'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/01/cocoa-html-parsing-with-objective-xml.html' title='Cocoa HTML parsing with Objective-XML'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-8267842734052308706</id><published>2009-01-17T23:57:00.001-08:00</published><updated>2009-01-18T02:08:10.405-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Higher Order Messaging'/><category scheme='http://www.blogger.com/atom/ns#' term='Blocks'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><title type='text'>Semantic Noise</title><content type='html'>&lt;a href="http://martinfowler.com"&gt;Martin Fowler&lt;/a&gt; and &lt;a href="http://gbracha.blogspot.com/2007/01/parser-combinators.html"&gt;Gilad Bracha&lt;/a&gt; write about &lt;a href="http://martinfowler.com/bliki/SyntacticNoise.html"&gt;Syntactic Noise&lt;/a&gt;, making similar points and using similar typographical techniques as I did in my &lt;a href="http://www.metaobject.com/papers/Higher_Order_Messaging_OOPSLA_2005.pdf"&gt;HOM paper&lt;/a&gt;.

&lt;blockquote&gt;
By Syntactic Noise, what people mean is extraneous characters that aren't part of what we really need to say, but are there to satisfy the language definition. Noise characters are bad because they obscure the meaning of our program, forcing us to puzzle out what it's doing.
&lt;/blockquote&gt;

Couldn't have said it better myself, so I'll just quote Martin Fowler.  Syntactic noise is one of the reasons I think neither the for(each) statement nor the blocks added to Objective-C are particularly good replacements for Higher Order Messaging.

&lt;hr&gt;
&lt;blockquote&gt;
&lt;pre&gt;
&lt;font face="Courier" size="-3"&gt;newArray = &lt;font color="ff0000"&gt;[&lt;/font&gt;existingArray map:&lt;font color="ff0000"&gt;^(id obj){ return [obj &lt;/font&gt; stringByAppendingString:&lt;font color="ff0000"&gt;@"&lt;/font&gt;suffix"&lt;font color="ff0000"&gt;]; }];&lt;/font&gt;
newArray = &lt;font color="ff0000"&gt;[[&lt;/font&gt;existingArray map&lt;font color="ff0000"&gt;]&lt;/font&gt; stringByAppendingString:@"suffix"&lt;font color="ff0000"&gt;]];&lt;/font&gt;
&lt;/pre&gt;
&lt;/font&gt;
&lt;/blockquote&gt;
&lt;hr&gt;

To me, that extra syntax is quite noisy, though the noise isn't, in fact, just syntactic.  We also have to introduce, name and even correctly type a completely redundant stand-in (&lt;font color="ff0000"&gt;obj&lt;/font&gt;) that we don't really care about.  Introducing &lt;a href="http://en.wikipedia.org/wiki/Occam's_razor"&gt;extra entities&lt;/a&gt; is semantic noise.  Apart from having to puzzle out what that extra entity is (and that it is, in fact, redundant) every time we read the code, it also brings us back to &lt;a href="http://www.stanford.edu/class/cs242/readings/backus.pdf"&gt;"element at a time"&lt;/a&gt; programming and thinking.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-8267842734052308706?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/8267842734052308706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=8267842734052308706' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/8267842734052308706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/8267842734052308706'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/01/semantic-noise.html' title='Semantic Noise'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-3114638642670394043</id><published>2009-01-15T11:26:00.001-08:00</published><updated>2009-01-15T11:39:29.175-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Higher Order Messaging'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><title type='text'>Simple HOM</title><content type='html'>While it is good to see that Higher Order Messaging is still inspiring new work, I feel a bit guilty that part of that inspiration are sentiments such as the &lt;a href="http://seriot.ch/blog.php?article=20090109"&gt;following&lt;/a&gt;:&lt;p&gt;
&lt;blockquote&gt;
 "Still I have yet to find a simple implementation that I like and that does not use private methods. The last thing I want is a relying on classes which can break at any time."
&lt;/blockquote&gt;
Mea culpa.&lt;p&gt;
While I did &lt;a href="http://www.metaobject.com/blog/2007/11/better-hom-selects.html"&gt;explain&lt;/a&gt; a bit why the current HOM implementation is a bit gnarly, code probably speaks more loudly than repeated mea-culpas.&lt;p&gt;

So, without further ado, a really simple HOM implementation.  An NSArray category provides the interface and does the actual processing:

&lt;blockquote&gt;
&lt;pre&gt;
@interface NSArray(hom)

-collect;

@end

@implementation NSArray(hom)

-(NSArray* )collect:(NSInvocation*)anInvocation
{
  NSMutableArray *resultArray=[NSMutableArray array];
  for (id obj in self ) {
    id resultObject;
    [anInvocation invokeWithTarget:obj];
    [anInvocation getReturnValue:&amp;resultObject];
    [resultArray addObject:resultObject];
  }
  return resultArray;
}

-collect {
  return [HOM homWithTarget:self selector:@selector(collect:)];
}

@end
&lt;/pre&gt;
&lt;/blockquote&gt;

The fact that NSInvocation deals with pointers to values rather than values makes this a bit longer than it needs to be, but the gist is simple enough:  iterate over the array, invoke the invocation, return the result.&lt;p&gt;

That leaves the actual trampoline, which is really just an implementation detail for conveniently creating NSInvocation objects.
&lt;blockquote&gt;
&lt;pre&gt;

@interface HOM : NSProxy {
  id xxTarget;
  SEL xxSelector;
}

@end

@implementation HOM

-(void)forwardInvocation:(NSInvocation*)anInvocation
{
  [xxTarget performSelector:xxSelector withObject:anInvocation];
}

-methodSignatureForSelector:(SEL)aSelector
{
  return [[xxTarget objectAtIndex:0] methodSignatureForSelector:aSelector];
}

-xxinitWithTarget:aTarget selector:(SEL)newSelector
{
  xxTarget=aTarget;
  xxSelector=newSelector;
  return self;
}

+homWithTarget:aTarget selector:(SEL)newSelector
{
  return [[[self alloc] xxinitWithTarget:aTarget selector:newSelector] autorelease];
}

@end
&lt;/pre&gt;
&lt;/blockquote&gt;

This code compiles without warnings, does not use any private API, and runs on both Leopard and the iPhone.   The Xcode project can be downloaded &lt;a href="http://www.metaobject.com/downloads/Objective-C/SimpleHom-1.0.tgz"&gt;here&lt;/a&gt;.&lt;p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-3114638642670394043?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/3114638642670394043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=3114638642670394043' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/3114638642670394043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/3114638642670394043'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/01/simple-hom.html' title='Simple HOM'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-1529330234451847130</id><published>2009-01-11T16:01:00.001-08:00</published><updated>2009-01-12T09:12:57.294-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>iPhone XML performance</title><content type='html'>Shortly after becoming an iPhone developer, I found a clever little piece of example code called &lt;a href="http://developer.apple.com/iphone/library/samplecode/XMLPerformance/index.html"&gt;XML Performance&lt;/a&gt; (login required).  Having done some &lt;a href="http://www.metaobject.com/Technology/index.html#Objective-XML"&gt;high performance XML processing code&lt;/a&gt; that works on the iPhone, I was naturally intrigued.&lt;p&gt;

The example pits Cocoa's NSXMLParser against a custom parser based on libxml2, the benchmark is downloading a top 300 list of songs from iTunes.  


&lt;h4&gt;More responsiveness using libxml2 instead of NSXMLParser&lt;/h4&gt;

Based on my previous experience, I was expecting libxml2 to be noticeably faster, but with the advantage in processing speed being less and less important with lower and lower I/O data rates (WiFi to 3G to Edge), as I/O would start to completely overwhelm processing.  Was I ever wrong!&lt;p&gt;

While my expectations were technically correct for overall performance, I had completely failed to take responsiveness into account.  Depending on the network selected, the NSXMLParser sample would appear to hang for 3 to 50 seconds before starting to show results.  Needless to say, that is an awful user experience.  The libxml example, on the other hand, would start displaying some results almost immediately.  While it also was a bit faster in the total time taken, this effect seemed pretty insignificant compared to the fact that results were arriving continually pretty much during the entire time.&lt;p&gt;

The difference, of course, is incremental processing.  Whereas NSXMLParser's -initWithContentsOfURL: method apparently downloads the entire document first and then begins processing, the libxml2-based code in the sample downloads the XML in small chunks and processes those chunks immediately.&lt;p&gt;

Alas, going with libxml2 has clear and significant disadvantages, with the code that uses libxml2 being around twice the size of the NSXMLParser-based code, at around 150 lines (non-comment, non-whitespace).  If you have worked with NSXMLParser before, you will know that that is already pretty painful, so just imagine that particular brand of joy doubled, with the 150 lines of code giving you the simplest of parsers, with just 5 tags processed.  Fortunately, there is a simpler way.

&lt;h4&gt;A simpler way:  Objective-XML's SAX&lt;/h4&gt;

Assuming you have already written a Cocoa-(Touch-)based parser using NSXMLParser, all you need to do is include Objective-XML in your projects and replace the reference to NSXMLParser with a reference to MPWSAXParser, everything else will work just as before.  Well, the same except for being significantly faster (even faster than libxml2) and now also more responsive on slow connections due to incremental processing.&lt;p&gt;

I have to admit that not having incremental processing was a "feature" Objective-XML shared with NSXMLParser until very recently, due to my not taking into account the fact that &lt;a href="http://www.ll.mit.edu/HPEC/agendas/proc04/invited/patterson_keynote.pdf"&gt;latency lags bandwidth&lt;/a&gt;.  This silly oversight has now been fixed, with both MPWMAXParser and MPWSAXParser sporting URL-based parsing methods that do incremental processing.&lt;p&gt;

So that's all there is to it, Objective-XML provides a drop-in replacement for NSXMLParser that has all the performance and responsiveness-benefits of a libxml2-based solution without the coding horror.

&lt;h4&gt;Even simpler: Messaging API for XML (MAX)&lt;/h4&gt;

However, even a Cocoa version of the SAX API represents a pretty low-bar in terms of ease of coding.  With MAX, Objective-XML provides an API that can do the same job much more simply.  MAX naturally integrates XML processing with Objective-C messaging using the following two main features:
&lt;ul&gt;
&lt;li&gt;Clients get sent element-specific messages for processing&lt;/li&gt;
&lt;li&gt;The parser handles nesting, controlled by the client&lt;/li&gt;

&lt;/ul&gt;

The following code for building Song objects out of iTunes &amp;lt;item&amp;gt; elements illustrates these two features:

&lt;blockquote&gt;
&lt;pre&gt;
-itemElement:(MPWXMLAttributes*)children attributes:(MPWXMLAttributes*)attributes parser:(MPWMAXParser*)p
{
  Song *song=[[Song alloc] init];
  [song setArtist:[children objectForTag:artist_tag]];
  [song setAlbum:[children objectForTag:album_tag]];
  [song setTitle:[children objectForTag:title_tag]];
  [song setCategory:[children objectForTag:category_tag]];
  [song setReleaseDate:[parseFormatter dateFromString:[children objectForTag:releasedate_tag]]];
  [self parsedSong:song];
  [song release];
  return nil;
}
&lt;/pre&gt;
&lt;/blockquote&gt;

MAX sends the -itemElement:attributes:parser: message to its client whenever it has encountered a complete &amp;lt;item&amp;gt; element, so there is no need for the client to perform string processing on tag names or
manage partial state as in a SAX parser.
The method constructs a song object using data from the &amp;lt;item&amp;gt; element's child elements which it then passes directly to the rest of the app via the parsedSong: message.  It does not return an value, so MAX will not build a tree at this level.&lt;p&gt;

Artist, album, title and category are the values of nested child elements of the &amp;lt;item&amp;gt; element.   The (common) code shared by all these child-elements gets the character content of the respective elements and is shown below:

&lt;blockquote&gt;
&lt;pre&gt;
-defaultElement:children attributes:atrs parser:parser
{
	return [[children combinedText] retain];
}
&lt;/pre&gt;
&lt;/blockquote&gt;

Unlike the &amp;lt;item&amp;gt; processing code, which did not return a value, this method does return a value.  MAX uses this return value to build
a DOM-like structure which is then consumed by the next higher-level, in this case the  -itemElement:attributes:parser: method shown above.  Unlike a traditional DOM, the MAX tree structure is built out of domain-specific objects returned incrementally by the client.&lt;p&gt;

These two pieces of sample code demonstrate how MAX can act like both a DOM parser or a SAX parser, controlled simply by wether the processing methods return objects (DOM) or not (SAX).  They also demonstrated both element-specific and generic processing.&lt;p&gt;

In the iTunes Song parsing example, I was able to build a MAX parser using about half the code required for the NSXMLParser-based example, a ratio that I have also encountered in larger projects.  What about performance?  It is slightly better than MPWSAXParser, so also somewhat better than libxml2 and significantly better than NSXMLParser.

&lt;h4&gt;Summary and Conclusion&lt;/h4&gt;

The slightly misnamed XML Performance sample code for the iPhone demonstrates how important managing latency is for perceived end user performance, while showing only very little in terms of actual XML processing performance.&lt;p&gt;

While ably demonstrating the performance problems of NSXMLParser, the sample code's solution of using libxml2 is really not a solution, due to the significant increase in code complexity.  Objective-XML provides both a drop-in replacement for NSXMLParser with all the performance and latency benefits of the libxml2 solution, as well as a new API that is not just faster, but also much more straightforward than either NSXMLParser or libxml2.&lt;p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-1529330234451847130?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/1529330234451847130/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=1529330234451847130' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/1529330234451847130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/1529330234451847130'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/01/iphone-xml-performance.html' title='iPhone XML performance'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-8836488921722500253</id><published>2009-01-11T14:22:00.001-08:00</published><updated>2009-01-11T17:51:33.959-08:00</updated><title type='text'>Best of Show, MacWorld 2009</title><content type='html'>Since I recently became the Mac tech lead for &lt;a href="http://www.livescribe.com/"&gt;Livescribe&lt;/a&gt;, responsible for delivering the Mac desktop software, I am happy to report that not only did we meet all of our &lt;a href="http://odeo.com/episodes/23629821-Livescribe-digital-pen-is-Mac-ready"&gt;target&lt;/a&gt; &lt;a href="http://www.gizmodo.com.au/2008/11/livescribe_pulse_smartpen_is_finally_mac_compatible.html"&gt;dates&lt;/a&gt;, we also won &lt;a href="http://www.macworld.com/article/138002-8/2009/01/bos2009.html"&gt;Best of Show&lt;/a&gt; at MacWorld 2009.&lt;p&gt;

Spending 3 days at the booth was both exhausting and rewarding, the &lt;a href="http://www.macworld.com/article/138067/2009/01/expo_notes_livescribe_booth_packed.html?lsrc=rss_main"&gt;enthusiasm&lt;/a&gt; &lt;a href="http://www.youtube.com/watch?v=_AZNMzjKYb8"&gt;exhibited&lt;/a&gt; by customers was absolutely mind-blowing.&lt;p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-8836488921722500253?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/8836488921722500253/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=8836488921722500253' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/8836488921722500253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/8836488921722500253'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2009/01/best-of-show-macworld-2009.html' title='Best of Show, MacWorld 2009'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-5158058258246801503</id><published>2008-12-24T18:35:00.001-08:00</published><updated>2008-12-24T18:42:24.492-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><title type='text'>More on one test class per class</title><content type='html'>I knew there was some other place &lt;a href="http://www.metaobject.com/blog/2008/12/unit-test-class.html"&gt;this recommendation&lt;/a&gt; had recently popped up:  &lt;a href="http://www.amazon.com/Emergent-Design-Evolutionary-Professional-Development/dp/0321509366"&gt;"Emergent Design"&lt;/a&gt; by Scott Bain, via &lt;a href="http://www.cincomsmalltalk.com/userblogs/ralph/blogView?showComments=true&amp;printTitle=Emergent_design,_and_refactoring_in_large_projects&amp;entry=3405976049"&gt;Ralph Johnson&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-5158058258246801503?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/5158058258246801503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=5158058258246801503' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5158058258246801503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5158058258246801503'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2008/12/more-on-test-class-per-class.html' title='More on one test class per class'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-398410128493973413</id><published>2008-12-20T15:45:00.001-08:00</published><updated>2008-12-22T14:21:18.353-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><title type='text'>Unit test the class</title><content type='html'>Travis Griggs comes to the &lt;a href="http://www.cincomsmalltalk.com/userblogs/travis/blogView?showComments=true&amp;printTitle=Unit_Testing&amp;entry=3373495029"&gt;conclusion&lt;/a&gt; that unit test objects
should map 1:1 to classes under test.&lt;p&gt;
I agree.&lt;p&gt;
In fact, I would go a bit further:  tests should be an integral part of a class.  While this helps avoid negative outcomes such as &lt;a href="http://www.soberit.hut.fi/mmantyla/badcodesmellstaxonomy.htm"&gt;parallel&lt;/a&gt; &lt;a href="http://books.google.com/books?id=1MsETFPD3I0C&amp;pg=PA83&amp;lpg=PA83&amp;dq=Parallel+Class+Hierarchy+fowler&amp;source=web&amp;ots=pKN7s_UDcd&amp;sig=vMlHIhUDA2xLBoCvsHKPePec5yQ&amp;hl=en&amp;sa=X&amp;oi=book_result&amp;resnum=1&amp;ct=result"&gt;class&lt;/a&gt; &lt;a href="http://www.codinghorror.com/blog/archives/000589.html"&gt;hierarchies&lt;/a&gt; or having code and tests diverge, it more importantly simplifies the test/code relationship and drives home the point that code is incomplete without its tests.&lt;p&gt;

While I was working with JUnit on a reasonably large Java system, both finding a good place for a particular test and finding the tests for a specific class became quite burdensome after a while.&lt;p&gt;

For this reason &lt;a href="http://www.metaobject.com/Technology/#MPWTest"&gt;MPWTest&lt;/a&gt; simply asks classes to test themselves.  Furthermore, only frameworks are tested, so the test tool simply loads each framework to test, enumerates the classes within that particular framework and then runs the tests it finds.  TestCases and TestSuites are implicitly created from this structure, removing most of the administrative burdens of unit testing, and also any explicit dependence of the tests on the testing framework.&lt;p&gt;

Having no dependencies on the testing framework makes it easier to ship tests in production code without having to also ship the testing framework.  While this may sound odd at first, it avoids potential issues with code compiled for testing being different than code destined to be shipped, and further reinforces the idea that tests are an integral part of each class, rather than an optional add-on.&lt;p&gt;



&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-398410128493973413?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/398410128493973413/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=398410128493973413' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/398410128493973413'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/398410128493973413'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2008/12/unit-test-class.html' title='Unit test the class'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-8715235209615330510</id><published>2008-10-11T18:44:00.001-07:00</published><updated>2008-10-12T08:59:20.476-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Binary XML</title><content type='html'>Jimmy Zhang hits the nail on the head when he &lt;a href="http://xml.sys-con.com/read/250512.htm"&gt;notes&lt;/a&gt; that parsing ASCII text is not the primary problem in XML performance, object allocation is.  I was
surprised by the same finding when I started working on Objective-XML around a decade ago.&lt;p&gt;


Sean McGrath claims that &lt;a href="http://seanmcgrath.blogspot.com/2008/01/binary-xml-solves-wrong-problem.html"&gt;Binary XML solves the wrong problem&lt;/a&gt;.&lt;p&gt;

Yes and no:  it doesn't help much with existing structures and parsing methods, but with the right methods, it can be extremely helpful!&lt;p&gt;

Also:  "...how weird is it that we have not moved on from the DOM and SAX in terms of "standard" APIs for XML processing?"
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-8715235209615330510?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/8715235209615330510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=8715235209615330510' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/8715235209615330510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/8715235209615330510'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2008/10/binary-xml.html' title='Binary XML'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-6458468706458471009</id><published>2008-08-09T21:39:00.001-07:00</published><updated>2008-08-09T21:39:00.293-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='Agile'/><title type='text'>Code is not an asset</title><content type='html'>&lt;p&gt;&lt;a href="http://michaelfeathers.typepad.com/michael_feathers_blog/"&gt;Michael Feathers&lt;/a&gt; wonders how to go &lt;a href="http://michaelfeathers.typepad.com/michael_feathers_blog/2008/08/beyond-technica.html"&gt;beyond technical debt&lt;/a&gt;.  I have been wondering about this for some time, and I think the answer is to account for code as a liability, not an asset.&lt;/p&gt;

&lt;p&gt;The functionality that the code has, the value it delivers is an asset, but the code itself is a liability.  This easily explains how refactoring and removing code add value, as long as functionality is preserved.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-6458468706458471009?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/6458468706458471009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=6458468706458471009' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6458468706458471009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6458468706458471009'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2008/08/code-is-not-asset.html' title='Code is not an asset'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-5786690447800980125</id><published>2008-04-20T13:57:00.001-07:00</published><updated>2008-04-20T13:57:03.222-07:00</updated><title type='text'>Higher Order Messaging backgrounded</title><content type='html'>
&lt;a href="http://www.bofh.org.uk/articles/2007/05/19/domain-agnostic-languages"&gt;Piers Cawley&lt;/a&gt; talks 
about &lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt;'s use of &lt;a href="http://www.metaobject.com/Research/"&gt;HOM&lt;/a&gt;:

&lt;blockquote&gt;It is, however, encouraging to see initiatives like Rspec which, through judicious use of higher order messages enables a much more fluent environment for writing tests: &lt;/blockquote&gt;

I think that's the first time I've seen HOM used to explain something else, rather than being the
&lt;a href="http://nat.truemesh.com/archives/000535.html"&gt;object&lt;/a&gt; of &lt;a href="http://www.devchix.com/2007/05/25/ruby-dry-up-your-enumerations/"&gt;attention&lt;/a&gt; itself.  So HOM is starting to be seen as simply a part of the computing landscape, at least by some.  Cool.

HOM was never conceived of as an interesting thing by itself but rather as a (meta-)building block for
building more expressive computational forms.  RSpec looks exactly like one of those cool things HOM 
enables that I would never have come up with myself.  I look forward to seeing more.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-5786690447800980125?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/5786690447800980125/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=5786690447800980125' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5786690447800980125'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5786690447800980125'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2008/04/higher-order-messaging-backgrounded.html' title='Higher Order Messaging backgrounded'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-1602973155561144165</id><published>2008-04-19T21:42:00.001-07:00</published><updated>2008-04-20T14:13:51.025-07:00</updated><title type='text'>Not just high performance Objective-C</title><content type='html'>Addendum to my &lt;a href="http://www.metaobject.com/blog/2007/08/high-performance-objective-c-i.html"&gt;article&lt;/a&gt; on implementing a high performance Postscript interpreter in Objective-C:  not just is performance better, so is accuracy.

Despite the fact that we are optimizing the heck out of the Objective-C objects we are using, they still
give us encapsulation and polymorphism, allowing us to choose arbitrary representations.  For example,
most Postscript interpreters use a fixed-size value object (polymorphic in a C-union type of way) 
that constraints floating point precision to 32 bits.  With Objective-C, we have no such constraints,
so EGOS floats are actually 64 bit doubles, so running the modified benchmark below in PostView doesn't just yield the result 75% faster than Preview, it also produces it with 7 orders of magnitude less error.  Not that that is necessarily important in Postscript, but it is a pleasant side effect and shows the power of combining performance with abstraction.


&lt;pre&gt;
%!
  usertime
  1000 0 1 10000000 { pop 0.0001 sub  } bind for
  exch usertime exch sub dup ==
  20 20 moveto /Times-Roman 24 selectfont
  100 string cvs show ( ms) show ( error:  ) show 
  1000.0 div 100.0 mul abs  100 string cvs show ( %) show
  showpage
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-1602973155561144165?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/1602973155561144165/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=1602973155561144165' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/1602973155561144165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/1602973155561144165'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2008/04/not-just-high-performance-objective-c.html' title='Not just high performance Objective-C'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-4715721926048787685</id><published>2007-11-28T18:27:00.001-08:00</published><updated>2007-11-28T18:27:25.863-08:00</updated><title type='text'>Better HOM selects</title><content type='html'>Wincent Colaiuta &lt;a href="http://wincent.com/a/about/wincent/weblog/archives/2006/11/hom_improvement.php"&gt;discusses&lt;/a&gt; a variant of the select HOM that takes an arbitrary number of
argument messages.  I like it!&lt;p&gt;

My initial implementation actually had arbitrary nesting, but as he discusses with collect, that
requires a trigger message to start, as there is no way of knowing at runtime when the expression terminates.  It never occurred to me that this limitation did not apply to select, which can
look at the return type of the messages sent and stop when it reaches a BOOL (char).&lt;p&gt;

Nice.&lt;p&gt;

p.s.:  the arbitrary nesting is still in the implementation, with each of the collection processing
HOMs actually running an enumerator and those enumerators stackable, and this is two of the reasons
the implementation is so gnarly:  (1) there is extra generality that is not needed and (2) making
that more general mechanism run fast was really, really tricky.&lt;p&gt;
p.p.s:  He actually discussed it almost a year ago, but I just saw it now.&lt;p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-4715721926048787685?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/4715721926048787685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=4715721926048787685' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4715721926048787685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4715721926048787685'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2007/11/better-hom-selects.html' title='Better HOM selects'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-4907891978879381450</id><published>2007-10-03T11:41:00.001-07:00</published><updated>2007-10-03T11:41:56.434-07:00</updated><title type='text'>Reading Helps</title><content type='html'>&lt;a href="http://www.cincomsmalltalk.com/blog/blogView"&gt;James Robertson&lt;/a&gt; &lt;a href="http://www.cincomsmalltalk.com/blog/blogView?showComments=true&amp;printTitle=Stupidity_as_an_Ideology&amp;entry=3368786071"&gt;slams&lt;/a&gt; the proposal by the &lt;a href="http://www.globalisation.eu/blog/technology/microsoft-responds-to-our-unbundling-proposal-200709251246/"&gt;EU free market institute&lt;/a&gt; to unbundle the OS from PCs sold in the EU.
&lt;blockquote&gt;

I've seen a lot of stupid ideas float past, but &lt;a href="http://tech.blorge.com/Structure:%20/2007/09/24/globalization-institute-wants-eu-to-unbundle-windows-with-pcs/"&gt;this&lt;/a&gt; one from the EU's Globalization Institute makes it into the top 5 - only the existence of the RIAA and the MPAA prevent a complete victory for these morons:
&lt;blockquote&gt;
[..]
The think tank recommended to the EU that all computers be sold without an operating system and sees no reason "why computer operating systems could not follow the same model as computer hard drives and processors."
&lt;/blockquote&gt;
Yes, installing an OS from scratch is exactly what most buyers long to do - it's such a productive use of their time.

&lt;/blockquote&gt;

Hmmm, what could they mean with the "same model as computer hard drives and processors"?&lt;p&gt;

Well, of course!  We all buy processors and hard drives separately, mount the CPU on our separately purchased motherboard, hook up hard-drive and power-supply and stick it all in a chassis.  That *must* be what they meant with that phrase.&lt;p&gt;

Or maybe, they meant that you can configure your computer with different CPUs and hard drives, and have the vendor ship you a machine configured to your specifications, whereas you cannot actually get
a computer and not pay the Microsoft tax?   Nah, that's just *crazy*:

&lt;blockquote&gt;
IT professionals are being forced to adopt Microsoft's operating systems — even if they tell their PC supplier they want a system free of Microsoft software, ZDNet UK's research has revealed.
&lt;/blockquote&gt;

&lt;a href="http://news.zdnet.co.uk/hardware/0,1000000091,39286228,00.htm"&gt;Oh&lt;/a&gt;.&lt;p&gt;  
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-4907891978879381450?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/4907891978879381450/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=4907891978879381450' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4907891978879381450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4907891978879381450'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2007/10/reading-helps.html' title='Reading Helps'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-4162159017653404038</id><published>2007-09-30T17:28:00.001-07:00</published><updated>2007-09-30T23:17:50.029-07:00</updated><title type='text'>'Thousands' or transistor²</title><content type='html'>The Alan Kay quote in my previous &lt;a href="http://www.metaobject.com/blog/2007/09/objective-c-futures.html"&gt;post&lt;/a&gt; made me think of &lt;a href="http://www.eetimes.com/news/latest/showArticle.jhtml?articleID=172301051"&gt;Montecito&lt;/a&gt;, the new Itanic version with 1.72 billion transistors.  Compare that to the ARM6, which had a measly 35K transistors, including its 4K cache.&lt;p&gt; Dividing the two numbers gets you almost 50K.  That's how many ARM6 CPUs you could get on the same chip with the same transistor budget as the Montecito.  A processor for every object.  Or viewed another way, more ARM6-equivalents than the ARM6 has transistors.  Which begs the question:  is the  Montecito proportionally as much an improvement in computational capacity as an ARM6 is over a  single transistor?&lt;p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-4162159017653404038?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/4162159017653404038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=4162159017653404038' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4162159017653404038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/4162159017653404038'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2007/09/or-transistor.html' title='&amp;#39;Thousands&amp;#39; or transistor²'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-5228696922178297357</id><published>2007-09-25T17:57:00.001-07:00</published><updated>2007-09-25T17:59:22.763-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Threading'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><title type='text'>Objective-C future(s)</title><content type='html'>Via &lt;a href="http://lambda-the-ultimate.org/node/2469"&gt;LtU&lt;/a&gt;, I got alerted to the fact that the&lt;a href="http://www.etoile-project.org/"&gt;Etoile&lt;/a&gt; project now has an &lt;a href="http://www.etoile-project.org/etoile/blog/2007/09/futures-in-objective-c.html"&gt;implementation&lt;/a&gt; of &lt;a href="http://en.wikipedia.org/wiki/Future_%28programming%29"&gt;futures&lt;/a&gt;.  Cool.&lt;p&gt;However, their implementation has specific objects reacting asynchronously to messages, making it more similar to the &lt;a href="http://dspace.mit.edu/handle/1721.1/6272"&gt;actor&lt;/a&gt; &lt;a href="http://www.lcs.mit.edu/publications/specpub.php?id=762"&gt;model&lt;/a&gt;,which as they mention is also very much Alan Kay's &lt;a href="http://gagne.homedns.org/~tgagne/contrib/EarlyHistoryST.html"&gt;original conceptual model&lt;/a&gt; for Smalltalk:&lt;p&gt;	&lt;blockquote&gt;Bob Barton, the main designer of the B5000 and a professor at Utah had said in one of his talks a few days earlier: "The basic principle of recursive design is to make the parts have the same power as the whole." For the first time I thought of the whole as the entire computer and wondered why anyone would want to divide it up into weaker things called data structures and procedures. &lt;em&gt;Why not divide it up into little computers&lt;/em&gt;, as time sharing was starting to? But not in dozens. Why not thousands of them, each simulating a useful structure? [Emphasis mine]&lt;/blockquote&gt;Actors are inherently asynchronous, each actor runs in a separate process/thread and messages arealso asynchronous, with the sender not waiting for the message to be delivered or ever gettinga return value.  Of course the actor model also makes all objects active, so the Etoile model, whichonly makes objects of specific classes active, is somewhere inbetween.&lt;p&gt;Futures, on the other hand, as introduced in &lt;a href="http://portal.acm.org/citation.cfm?doid=4472.4478"&gt;MULTLSIP&lt;/a&gt; &lt;a href="http://pages.cs.wisc.edu/~fischer/cs538.s07/multilisp.pdf"&gt;(pdf)&lt;/a&gt;, tryto integrate asynchronous execution into a traditional call/return control- and data-flow.  So messages(or functions in MULTILSIP) appear to have normal synchronous semantics and immediately yielda return value, but when annotated with the &lt;em&gt;future&lt;/em&gt; keyword execution of that return valueis done in a background thread and the immediate return value is just a proxy for the value that is still being computed.&lt;p&gt;In the &lt;a href="http://portal.acm.org/citation.cfm?doid=1146841.1146844"&gt;HOM paper&lt;/a&gt; (&lt;a href="http://www.metaobject.com/papers/Higher_Order_Messaging_OOPSLA_2005.pdf"&gt;pdf&lt;/a&gt;) presented at &lt;a href="http://www.oopsla.org/2005/ShowPage.do?id=Home"&gt;OOPSLA 2005&lt;/a&gt;, I also describe a Future implementationbased on Higher Order Messaging that comes very close to the way it was done in MULTILSIP.  A -futureHOM is all that is needed to indicate that you would like a result computed in a background thread:&lt;pre&gt;  result = [anObject lengthyOperation:parameter];           //  synchronous
  result = [[anObject future] lengthyOperation:parameter];  //  asynchronous with future&lt;/pre&gt;I am probably biased, but this seems about as easy-to-use as possible,with all the nasty machinery (worker-queues, lockless FIFOs, etc.)hidden behind a single -future message.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-5228696922178297357?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/5228696922178297357/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=5228696922178297357' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5228696922178297357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/5228696922178297357'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2007/09/objective-c-futures.html' title='Objective-C future(s)'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-7695276182360787519</id><published>2007-09-22T15:33:00.001-07:00</published><updated>2007-09-22T15:37:21.473-07:00</updated><title type='text'>Message-oriented persistence</title><content type='html'>The good folks at Omni posted an interesting discussion of their &lt;a href="http://blog.omnigroup.com/2007/09/17/omnifocus-what-weve-learned-so-far-engineering/trackback/"&gt;persistence strategy&lt;/a&gt; for OmniFocus.   In short they found that using a database, specifically a CoreData data store, was not exactly ideal for their primary public data format.&lt;p&gt;  Instead, they appear to be using a pattern that Martin Fowler calls &lt;a href="http://martinfowler.com/bliki/EventPoster.html"&gt;EventPoster&lt;/a&gt;.  After reading &lt;a href="http://en.wikipedia.org/wiki/David_P._Reed"&gt;David Reed&lt;/a&gt;'s &lt;a href="http://www.lcs.mit.edu/publications/specpub.php?id=773"&gt;thesis&lt;/a&gt;, I think I prefer to call it message-oriented persistence.&lt;p&gt;  I first stumbled on this pattern when designing a replacement for a feed processor at the &lt;a href="http://news.bbc.co.uk/sport"&gt;BBC&lt;/a&gt;.  The basic task was to process a feed of information snippets encoded as XML and generate/update web and interactive TV (Ceefax) pages.&lt;p&gt;  Like a good little enterprise architect, and similar to the existing system, I had originally planned to use a central SQL database for storage, though designing a data model for that was proving difficult due to the highly irregular nature of the feed data.  As an auditing/logging measure, I also wanted to keep a copy of the incoming feed data, so when the time came to do the first implementation spikes,  I decided we would implemented the display, update and XML feed processing logic, but not the datastore.  Instead, we would just re-play the feed data from the log we had kept.&lt;p&gt;  This worked so well that we never got around to implementing the central database.&lt;p&gt;  Leaving out the database vastly simplified both our code-base and the deployed system, which I could run in its entirety on my 12" AlBook whereas the system we were replacing ran around a dozen networked machines.  Apart from making us popular with our sysadmin team both in terms of reliability and deployment/maintenance complexity (essentially a jar and a working directory was all it needed), a fringe benefit was being able to work on the system on said AlBook while disconnected from the network, working from home or from a sunny patch of grass outside the office.&lt;p&gt;  In addition to personal happiness, systen performance was also positively affected:  since we kept our working state completely in memory, the AlBook mentioned outperformed the original cluster by 2-3 orders of magnitude, producing hundreds of pages per second versus taking from several seconds to several minutes to produce a single page.&lt;p&gt;  Performance and simplicity are exactly the benefits claimed for &lt;a href="http://www.prevayler.org/"&gt;prevlayer&lt;/a&gt;, a persistence layer for Java based on the same principles.&lt;p&gt;  &lt;a href="http://portal.acm.org/citation.cfm?doid=1094855.1094861"&gt;TeaTime&lt;/a&gt;, the theoretical foundation and actual engine working underneath &lt;a href="http://www.croquetproject.org"&gt;Croquet&lt;/a&gt;, takes this idea to a whole different level:  objects do not have state, but are simply names for message histories.  This is truly "objects [and messages] all the way down".  Turtles need not apply.&lt;p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-7695276182360787519?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/7695276182360787519/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=7695276182360787519' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/7695276182360787519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/7695276182360787519'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2007/09/message-oriented-persistence.html' title='Message-oriented persistence'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-2658466420241734000</id><published>2007-09-01T11:53:00.000-07:00</published><updated>2007-09-01T11:57:30.566-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><title type='text'>More on MPWObjectCache</title><content type='html'>&lt;p&gt;Now that I've motivated why an MPWObjectCache might be &lt;a href="http://www.metaobject.com/blog/2007/08/high-performance-objective-c-i.html"&gt;useful&lt;/a&gt;, let's go into some more detail as to how it actually works.  To follow along, or if you'd rather just read the source code than my ramblings, MPWObjectCache is part of MPWFoundation, which can be downloaded here: &lt;a hre="http://www.metaobject.com/downloads/Objective-C/"&gt;http://www.metaobject.com/downloads/Objective-C/&lt;/a&gt;.&lt;/p&gt;

As I mentioned before, the algorithm for MPWObjectCache is quite simple:  there is a circular buffer of object slots.  We try to get pre-allocated 
objects from this circular buffer if possible.  If we find an object
in the cache and it is available for reuse, we just return it and 
have just saved the cost of allocation.

Two things can prevent this happy state of affairs: (1) we don't have
an object yet or (2) we cannot reuse the object because it is still in use.
In both cases we will need to allocate a new object, but in the second
case we also remove the old object from the cache position.

&lt;pre&gt;
#if  SLOW_SAMPLE_IMPLEMENTATION_WANTED
-getObject
{
    id obj;
    objIndex++;
    if ( objIndex &amp;gt;= cacheSize ) {
        objIndex=0;
    }
    obj=objs[objIndex];
    if ( obj==nil ||  [obj retainCount] &amp;gt; 1 ) {
        if ( obj!=nil ) {
            [obj release];
        }
        obj = [[objClass alloc] init];
        objs[objIndex]=obj;
    }
    return [[obj retain] autorelease];
}
#else

&lt;/pre&gt;

&lt;p&gt;This is what a naive implementation looks like.  A couple of notes on the code:
&lt;ul&gt;
&lt;li&gt;objects must be reinitialized by the client (and reinitializable in the first place)
&lt;li&gt;only one attempt is made to find an object&lt;/li&gt;
&lt;li&gt;the retain/autorelease will prevent the cache from working unless a fairly tight autorelease pool regime is maintained&lt;/li&gt;
&lt;li&gt;there are quite a few message sends&lt;/li&gt;
&lt;li&gt;it's not what is used in production&lt;/li&gt;
&lt;/ul&gt;

The effectiveness of the cache obviously depends on your allocation
patterns and the size of the object-cache.  Larger caches take
longer to be filled up before they start wrapping around with
the potential for reuse,
but smaller sizes can mean that the object will still be in use
when we do wrap around.

The actual implementation is very similar to the one presented
above, except that it does a little more probing and uses
IMP-caching for all the messages sent on the critical path.
These optimizations ensure that object-caches are no slower
than normal allocations even in worst-case situations such
as every allocated object being retained.  In addition the
cache can also be set to not do the retain/autorelease, which is
safe when you are pushing objects and have control over
the cache:

&lt;pre&gt;
-doSomething:target
{
 // cache is an ivar
 id obj=GETOBJECT(cache);
 // target does not have access to cache
 [target doSomethingWithObject:obj];
 // obj now either has an extra retain or can be reused
}
&lt;/pre&gt;

This pleasant property is a side effect of the decision to turn
the object-cache into an object that can be instantiated and
placed in an instance variable, rather than the typical 
object pools that are implemented as class
methods.  The class method that maintains such a pool 
usually has no information about the lifetime
of objects, so to be safe such an implementation always
has to protect the objects it returns, negating much of
the advantage of caching.  Similar caveats apply to 
multi-threading and locking.

Those caveats notwithstanding, MPWObjectCache also provides the
CACHING_ALLOC macro for creating class-side allocation methods
backed by an object cache, which is used in the HOM implementation
to reduce the cost of allocating trampolines:
&lt;pre&gt;
 CACHING_ALLOC( quickTrampoline, 5, YES )
&lt;/pre&gt;

This creates a +quickTramplone method backed by an object cache
with 5 entries.  The YES flag allows objects to be returned from
the cache without the retain/autorelease despite the fact
that it isn't one of the safe "push" patterns described above.
However, this use is also safe because the trampoline is used
only temporarily to catch and forward the message, all of 
which is code controlled by the implementation.  It is no
longer needed once any client code implementing the actual
HOM is run.

So, this is how and why object-caches can make your (temporary)
object allocations much, much faster.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-2658466420241734000?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/2658466420241734000/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=2658466420241734000' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2658466420241734000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/2658466420241734000'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2007/09/more-on-mpwobjectcache.html' title='More on MPWObjectCache'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-1302058502082968477</id><published>2007-08-27T13:17:00.000-07:00</published><updated>2007-08-27T17:54:58.379-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='Postscript'/><title type='text'>High performance Objective-C I: a Postscript interpreter</title><content type='html'>A key component of the metaobject product suite is EGOS, which includes as a central ingredient a custom Postscript Level 3 compatible interpreter.  The project was started in part as a hedge against the chance of Apple dropping DisplayPostscript, in part because our Postscript virtualization technique was hitting limits, and in part because it would make getting Objective-C objects out of the interpreter much easier.&lt;p&gt;At its core, Postscript is a stack-oriented, dynamically typed and highly polymorphic interpreted programming language.  So implementing Postscript with Objective-C objects is actually not just convenient when you want to get Objective-C objects out, it is also a good match for the semantics of the language.&lt;/p&gt;&lt;p&gt;So all is good, right?  Well, we also need to make sure that performance is competitive, otherwise there really isn't much of a point.  How do we find out if performance is competitive?  Fortunately, we have the gold standard handily available:  Adobe's interpreter was not just used in NeXT's DisplayPostscript, but is also available as the PS Normalizer on Mac OS X .  So let's test performance with a little Postscript program:&lt;/p&gt;&lt;pre&gt;  %!
  usertime
  0 1 1000000 { 4 mul pop } bind for
  usertime exch sub dup ==
  20 20 moveto /Times-Roman 24 selectfont
  100 string cvs show ( ms) show
  showpage
&lt;/pre&gt;
The program times a loop that multiplies some numbers one million times.   It exercises a good deal of the basic execution machinery in the Postscript language:  stack manipulation, procedure invocation, array access (a procedure is just an array with the executable bit set), looping and arithmetic.  The loop is timed with the usertime command, which returns CPU time used in milliseconds.&lt;p&gt;This test clocks in at  513 ms (513 ns per iteration) in Preview, which isn't too shabby.&lt;/p&gt;&lt;h3&gt;1.  The problem&lt;/h3&gt;
As proof of concept, let's code up some Objective-C equivalent of what the Postscript interpreter has to do in this loop.  That should give us a good lower bound for the time taken (lower bound because there will be additional interpretation overhead, and Postscript semantics are slightly more complicated).   We need a stack, some number objects and a bit of arithmetic.  Easy:&lt;pre&gt;
 id startcounter=[NSNumber numberWithInt:0];
 id endcounter=[NSNumber numberWithInt:1000000];
 id counter=startcounter;
 id four=[NSNumber numberWithInt:4];
 while ( [counter intValue] &amp;lt; [endcounter intValue] ) {
  int intResult;
  id result;
  [stack addObject:counter];
  [stack addObject:four];
  intResult = [[stack lastObject] intValue] * [[stack objectAtIndex:[stack count]-2] intValue];
  result=[NSNumber numberWithInt:intResult];
  [stack removeLastObject];
  [stack removeLastObject];
  [stack addObject:result];
  [stack removeLastObject];
  counter=[NSNumber numberWithInt:[counter intValue]+1];
 }
 &lt;/pre&gt;
Sadly, this takes 4.8 µs per iteration, so our 'lower' bound is almost 10 times slower than our target, and that's without accounting for interpretation.  Clearly not good enough.  What if we get rid of all that silly stack manipulation code and use a plain C loop?&lt;pre&gt;
  id b=[NSNumber numberWithInt:4];
  for (i=0;i &amp;lt; 10000000;i++) {
  id a=[NSNumber numberWithInt:i];
  id c=[NSNumber numberWithInt:[a intValue] * [b intValue]];
 }

&lt;/pre&gt;
&lt;h3&gt;2. Mutable State&lt;/h3&gt;Objective-C is an imperative object oriented language, meaning objects can change state.  However, we have treated numbers as immutable value objects, requiring them to be recreated from scratch.  Allocating objects tends to be around 25x more costly than an Objective-C message send, so what if we don't allocate new integer objects, but instead reuse an existing one and just change its value?  It turns out we can't use NSNumber for this as it doesn't allow its value to be set, so we need a (trivial) wrapper class for a single integer.&lt;pre&gt; 
   id b=[MPWInteger numberWithInt:4];
   id a=[MPWInteger numberWithInt:0];
   id c=[MPWInteger numberWithInt:0];
   for (i=0;i &amp;lt;10000000;i++) {
  [a setIntValue:i];
  [c setIntValue:[a intValue] * [b intValue]];
  }

&lt;/pre&gt;
That's more like it:  50ns per iteration is 100x better than our first attempt and also 10x better than the target we're aiming for.  So taking advantage of mutable state makes our basic plan possible, at least in principle.  Of course, we now have to reintroduce the stack and add interpretation.
&lt;h3&gt;3.   Save the planet&lt;/h3&gt;
Alas, it turns out that the interpreter really does need fresh instances.  While it will discard them quickly in most cases, it sometimes stores them away meaning we can't statically reuse objects the way we did above.&lt;p&gt;Instead, we need to figure out a way to recycle temporary objects so we can reuse them without spending a lot of time.  The common way to do this is to keep a pool of objects from which requests for new MPWInteger instances are satisfied.  However, due to the unpredictable nature of the interpreted code, we cannot use the explicit checkin/checkout policy such pools usually require.&lt;/p&gt;&lt;p&gt;Instead we make the pool a circular buffer and use the retain count to verify that an object can be reused.  When we get to a position in the pool that has an object, we can reuse that object if the retain count is one, meaning that only the pool has a valid reference.  If the retain count of the object is greater than one, someone other than the pool is holding on to the object and it cannot be reused (yet), so we need to get another instance.&lt;/p&gt;&lt;p&gt;This logic is encapsulated in the class MPWObjectCache, which can be used very similarly to a class (factory object) in creating new instances.
&lt;/p&gt;&lt;pre&gt;
 MPWObjectCache* intCache=[[MPWObjectCache alloc] initWithCapacity:20 
        class:[MPWInteger class]];
 id b=[MPWInteger integer:5];
 for (i=0;i &amp;lt; 1000000;i++) {
  id a=GETOBJECT(intCache);
  id d=GETOBJECT(intCache);
  [a setIntValue:i];
  [d setIntValue:[a intValue] * [b intValue]];

&lt;/pre&gt;


This code runs in 100ns per iteration,  so we now have a solution that gives us new or safely recycled objects quickly enough to build on with the confidence the end result will perform acceptably.&lt;h3&gt;4.  Results&lt;/h3&gt;Running the Postscript test program from the start of this post in &lt;a href="http://www.metaobject.com/Products/"&gt;PostView&lt;/a&gt; yields a result of 260ns per iteration, meaning that our Objective-C Postscript interpreter is almost twice as fast as Adobe's, at least on this particular workload.  While I wouldn't generalize this isolated result to say that EGOS is a faster interpreter, it clearly shows that it is at least competitive, which was the goal of the exercise.&lt;div&gt;
&lt;/div&gt;&lt;div&gt;The fact that it took a measly 20 KLOC illustrates the leverage Objective-C provides:  Ghostscript weighs in at around 250+ KLOC (without drivers).&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;&lt;span class="Apple-style-span"  style="font-size:large;"&gt;5.  Conclusion&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;One of the things I've always liked about Objective-C is that it lets you have your cake and eat it, too:  great expressiveness to solve your problem effectively is always coupled with the ability to get down and dirty and get really great performance, without losing the structure of the original solution.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;The most important factor to watch out for in terms of performance tends to be object allocation.  Controlling this factor with a transparent object-cache allowed us to get an overall performance improvement of around 10-20x in the case of a Postscript interpreter, taking performance from unacceptably slow up to and beyond the industry standard.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;Of course, this isn't the only factor and Postscript interpretation not the only application.  Stay tuned!&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-1302058502082968477?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/1302058502082968477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=1302058502082968477' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/1302058502082968477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/1302058502082968477'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2007/08/high-performance-objective-c-i.html' title='High performance Objective-C I: a Postscript interpreter'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8397311766319215218.post-6601746517682976273</id><published>2007-05-12T14:12:00.000-07:00</published><updated>2007-05-19T18:47:34.854-07:00</updated><title type='text'>Onward the Blogosphere</title><content type='html'>Somewhat late to the party, metaobject now has a blog.  Expect to see more details about our products, as well as more general ramblings about programming in general, dynamic languages, Objective-C, Cocoa and (Higher Order) Messaging in particular, and anything else that strikes my fancy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8397311766319215218-6601746517682976273?l=blog.metaobject.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.metaobject.com/feeds/6601746517682976273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8397311766319215218&amp;postID=6601746517682976273' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6601746517682976273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8397311766319215218/posts/default/6601746517682976273'/><link rel='alternate' type='text/html' href='http://blog.metaobject.com/2007/05/onwards-blogosphere.html' title='Onward the Blogosphere'/><author><name>Marcel Weiher</name><uri>http://www.blogger.com/profile/11651004661887001433</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
