{"id":87,"date":"2010-08-06T17:32:32","date_gmt":"2010-08-07T01:32:32","guid":{"rendered":"http:\/\/bynomial.com\/blog\/?p=87"},"modified":"2010-08-06T17:32:32","modified_gmt":"2010-08-07T01:32:32","slug":"dont-worry-be-happy","status":"publish","type":"post","link":"http:\/\/bynomial.com\/blog\/?p=87","title":{"rendered":"Don&#8217;t worry, Be happy"},"content":{"rendered":"<p>This post describes a correction to <a href=\"http:\/\/bynomial.com\/blog\/?p=33\">the Be category<\/a> when dealing with class clusters.<\/p>\n<p>The Be category adds the<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">be<\/div><\/div>\n<p>method to all objects and allows you to replace code like this:<\/p>\n<pre>MyObject *obj = [[[MyObject alloc] initWithThing:thing] autorelease];<\/pre>\n<p>with code like this:<\/p>\n<pre>MyObject *obj = [[MyObject be] initWithThing:thing];<\/pre>\n<p>This is useful in that it simplifies code and allows for <a href=\"http:\/\/bynomial.com\/blog\/?p=65\">some very nice memory management guidelines<\/a>.<\/p>\n<p>Unfortunately, as commenter David recently pointed out, there was a bug in my initial implementation. \u00c2\u00a0Specifically, some classes, such as NSArray, change what<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">self<\/div><\/div>\n<p>is when their init method is called. \u00c2\u00a0In other words the two lines:<\/p>\n<pre>NSArray *myArray = [NSArray alloc];\r\nmyArray = [myArray init];<\/pre>\n<p>might point to different objects. \u00c2\u00a0Why is that?<\/p>\n<h2>Class Clusters<\/h2>\n<p>The main reason for this strange behavior is the implementation of a class cluster.  A class cluster is a group of subclasses of a single abstract superclass.  NSArray and NSString are examples of class clusters.<\/p>\n<p>The problem they solve is this: sometimes, one implementation of a class is much more efficient than another, but only for certain data.  For example, suppose we know an array will only ever have a single element.  Then we can drop a lot of extra code and CPU cycles by utilizing that knowledge.  So we get better performance out of a NSOneElementArray class.  But clearly some arrays have more than one element, so we will still need something like an NSMultiElementArray class.  I&#8217;m not sure if this is actually the class cluster behind NSArray, but it illustrates the idea that we get a specific subclass when we initialize from an abstract superclass.  It is essentially a kind of factory method design pattern, if you&#8217;re into design patterns.<\/p>\n<h2>What doesn&#8217;t work<\/h2>\n<p>The old be implementation did this:<\/p>\n<pre>MyObject *obj = [[[MyObject alloc] autorelease] initWithThing:thing];<\/pre>\n<p>This works fine for all standard objects.  Autorelease generally does <em>not<\/em> need to operate on an initialized object.  It&#8217;s not exactly conservative code, but it works in practice on many classes.<\/p>\n<p>However, it does not work as intended on class clusters.<\/p>\n<p>Suppose MyObject is a class cluster.  This is what happens:<\/p>\n<pre>MyObject *obj = [MyObject alloc];  \/\/ obj points to an allocated instance of the superclass\r\nobj = [obj autorelease];  \/\/ obj points to the same object, which is now autoreleased\r\nobj = [obj init];  \/\/ obj now points to something else, which is not autoreleased<\/pre>\n<p>The problem is that the autorelease was sent to the abstract superclass, an object that we no longer care about after the call to init.<\/p>\n<h2>The solution<\/h2>\n<p>Luckily, there is a way to fix this.  We can use an NSProxy subclass.  Specifically, I&#8217;ll use the BeProxy object, which expects the first-and-only method call to be to some init method.  The return value of the init method becomes the object the user has a pointer to, and the BeProxy object gives us a place to call autorelease <em>after<\/em> the init method is called.<\/p>\n<p>In other words, we can continue using the Be method exactly as we want to, and it works even in weird cases where init returns something other than what we started with.<\/p>\n<p>If you&#8217;re curious about the details, check out the code here.  Otherwise you can just scroll to the bottom to download the source files.  As with all the code I post on this blog, it&#8217;s free to use without any warranty or strings attached.<\/p>\n<h2>The code<\/h2>\n<p><a href=\"http:\/\/bynomial.com\/blog\/wp-content\/uploads\/2010\/08\/nsobject+be_source_screenshot.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-88\" title=\"nsobject+be_source_screenshot\" src=\"http:\/\/bynomial.com\/blog\/wp-content\/uploads\/2010\/08\/nsobject+be_source_screenshot.png\" alt=\"\" width=\"474\" height=\"721\" srcset=\"http:\/\/bynomial.com\/blog\/wp-content\/uploads\/2010\/08\/nsobject+be_source_screenshot.png 474w, http:\/\/bynomial.com\/blog\/wp-content\/uploads\/2010\/08\/nsobject+be_source_screenshot-197x300.png 197w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><\/p>\n<h2>Source files<\/h2>\n<p><a href=\"http:\/\/bynomial.com\/blogfiles\/NSObject+Be.h\">NSObject+Be.h<\/a><\/p>\n<p><a href=\"http:\/\/bynomial.com\/blogfiles\/NSObject+Be.m\">NSObject+Be.m<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post describes a correction to the Be category when dealing with class clusters. The Be category adds the be method to all objects and allows you to replace code like this: MyObject *obj = [[[MyObject alloc] initWithThing:thing] autorelease]; with code like this: MyObject *obj = [[MyObject be] initWithThing:thing]; This is useful in that it [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[1],"tags":[],"_links":{"self":[{"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/87"}],"collection":[{"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=87"}],"version-history":[{"count":0,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/87\/revisions"}],"wp:attachment":[{"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=87"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=87"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/bynomial.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=87"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}