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

<channel>
	<title>An Experiment in Bloggery &#187; obj</title>
	<atom:link href="http://kevin.sb.org/tag/obj/feed/" rel="self" type="application/rss+xml" />
	<link>http://kevin.sb.org</link>
	<description>The occasional view into my life</description>
	<lastBuildDate>Fri, 09 Sep 2011 00:19:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Objective-C caching and Method Swizzling</title>
		<link>http://kevin.sb.org/2006/11/16/objective-c-caching-and-method-swizzling/</link>
		<comments>http://kevin.sb.org/2006/11/16/objective-c-caching-and-method-swizzling/#comments</comments>
		<pubDate>Fri, 17 Nov 2006 01:46:03 +0000</pubDate>
		<dc:creator>Kevin Ballard</dc:creator>
				<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[obj]]></category>
		<category><![CDATA[objective]]></category>

		<guid isPermaLink="false">http://08e21bee-cd75-42a9-a777-72dd2fe54c09</guid>
		<description><![CDATA[(This post was adapted from an email I sent to Mike Solomon). Objective-C has a method caching mechanism that optimizes for the case where a small number of methods are called repeatedly on one (or more) objects of a given class. This happens very often; for example, if you iterate over an array to gather [...]]]></description>
			<content:encoded><![CDATA[<p>(This post was adapted from an email I sent to <a href="http://www.culater.net" title="culataer.net">Mike Solomon</a>).</p>

<p>Objective-C has a method caching mechanism that optimizes for the case where
a small number of methods are called repeatedly on one (or more) objects of a
given class. This happens very often; for example, if you iterate over an array
to gather the results of performing an operation on each element, you&#8217;re going to
be calling the same method on a bunch of instances of the same class. And in fact
the <code>-[NSEnumerator nextObject]</code> method itself will also be cached.</p>

<p>The fact that method caching exists is common knowledge. However, what isn&#8217;t generally
known is how that caching is implemented, and what it means for you if you want to
hack around on the internals of a class.</p>

<p><span id="more-132"></span></p>

<h3>Objective-C Method Caching</h3>

<p>The biggest reason why people might care about Objective-C method caching is if
they&#8217;re doing what&#8217;s commonly termed &#8220;Method Swizzling&#8221;. This is where you take
two methods with the same signature (same return value and argument types), and you
swap them so any code that tries to call the first will really call the second, and
vice versa. The primary use of this is when you want to patch an app you have no control
over - you can use a category to add a new method to the class, then swizzle that with
the original method.</p>

<p>The reason why this is a potential issue is, since nobody knows how the method caching
actually works, nobody knows if Method Swizzling can be affected by the method caching.</p>

<p>Well, the short answer is no.</p>

<p>Objective-C method caching works via a miniature hash table (of type <code>struct objc_cache</code>)
stored in the <code>cache</code> field in every <code>struct objc_class</code>. This hash table contains pointers
to recently-used <code>Method</code> objects, which are the same <code>Method</code> objects as found in the
<code>struct objc_method_list</code>s on each <code>struct objc_class</code>. The important part here is the
cached <code>Method</code> objects on a given class can actually belong to one of the class&#8217;s superclasses.
This is what makes the caching really useful &#8212; the method dispatch system doesn&#8217;t have to
trawl upwards through the class hierarchy to find the method if it&#8217;s already been cached.</p>

<p>The reason why Method Swizzling doesn&#8217;t have to worry about method caching is because of
the fact that the cached <code>Method</code> objects are the same ones from the <code>struct objc_method_list</code>s
in each class. When Method Swizzling modifies the Method object, the cache is modified at
the same time, so any subsequent cache hit will reflect this modified <code>Method</code>.</p>

<p>If for some reason you want to flush the cache for a class (and I can&#8217;t think of why), simply insert
<typo:tmcode theme="Space Cadet"></p>

<pre class="textmate-source"><span class="source source_objc"><span class="storage storage_type storage_type_c">void</span> _objc_flush_caches(<span class="storage storage_type storage_type_objc">Class</span> cls);</span></pre>

<p></typo:tmcode>
somewhere in your file and then call it, passing the class you wish to flush the cache of.
This particular method will also flush the cache of every superclass of the given class.</p>

<h3>Method Swizzling</h3>

<p>If you want to use Method Swizzling in your own app (or Input Manager, or <a href="http://culater.net/software/SIMBL/SIMBL.php" title="Simple Input Manager Bundle Loader">SIMBL</a> plugin, or&#8230;)
the following implementation is the best one I&#8217;m aware of.</p>

<p><typo:tmcode theme="Space Cadet"></p>

<pre class="textmate-source"><span class="source source_objc"><span class="storage storage_type storage_type_c">void</span><span class="meta meta_function meta_function_c"> <span class="entity entity_name entity_name_function entity_name_function_c">PerformSwizzle</span><span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">(</span><span class="storage storage_type storage_type_objc">Class</span> aClass, <span class="storage storage_type storage_type_objc">SEL</span> orig_sel, <span class="storage storage_type storage_type_objc">SEL</span> alt_sel, <span class="storage storage_type storage_type_objc">BOOL</span> forInstance<span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">)</span></span>
{
    <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> First, make sure the class isn't nil
</span>    <span class="keyword keyword_control keyword_control_c">if</span> (aClass != <span class="constant constant_language constant_language_objc">nil</span>) {
        Method orig_method = <span class="constant constant_language constant_language_objc">nil</span>, alt_method = <span class="constant constant_language constant_language_objc">nil</span>;

        <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Next, look for the methods
</span>        <span class="keyword keyword_control keyword_control_c">if</span> (forInstance) {
            orig_method = class_getInstanceMethod(aClass, orig_sel);
            alt_method = class_getInstanceMethod(aClass, alt_sel);
        } <span class="keyword keyword_control keyword_control_c">else</span> {
            orig_method = class_getClassMethod(aClass, orig_sel);
            alt_method = class_getClassMethod(aClass, alt_sel);
        }

        <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> If both are found, swizzle them
</span>        <span class="keyword keyword_control keyword_control_c">if</span> ((orig_method != <span class="constant constant_language constant_language_objc">nil</span>) &amp;&amp; (alt_method != <span class="constant constant_language constant_language_objc">nil</span>)) {
            <span class="storage storage_type storage_type_objc">IMP</span> temp;

            temp = orig_method-&gt;method_imp;
            orig_method-&gt;method_imp = alt_method-&gt;method_imp;
            alt_method-&gt;method_imp = temp;
        } <span class="keyword keyword_control keyword_control_c">else</span> {
<span class="meta meta_preprocessor meta_preprocessor_c">#<span class="keyword keyword_control keyword_control_import keyword_control_import_c">if</span> DEBUG</span>
            <span class="support support_function support_function_cocoa">NSLog</span>(<span class="string string_quoted string_quoted_double string_quoted_double_objc"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_objc">@"</span>PerformSwizzle Error: Original %@, Alternate %@<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_objc">"</span></span>,(orig_method == <span class="constant constant_language constant_language_objc">nil</span>)?<span class="string string_quoted string_quoted_double string_quoted_double_objc"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_objc">@"</span> not found<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_objc">"</span></span>:<span class="string string_quoted string_quoted_double string_quoted_double_objc"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_objc">@"</span> found<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_objc">"</span></span>,(alt_method == <span class="constant constant_language constant_language_objc">nil</span>)?<span class="string string_quoted string_quoted_double string_quoted_double_objc"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_objc">@"</span> not found<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_objc">"</span></span>:<span class="string string_quoted string_quoted_double string_quoted_double_objc"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_objc">@"</span> found<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_objc">"</span></span>);
<span class="meta meta_preprocessor meta_preprocessor_c">#<span class="keyword keyword_control keyword_control_import keyword_control_import_c">endif</span></span>
        }
    } <span class="keyword keyword_control keyword_control_c">else</span> {
<span class="meta meta_preprocessor meta_preprocessor_c">#<span class="keyword keyword_control keyword_control_import keyword_control_import_c">if</span> DEBUG</span>
        <span class="support support_function support_function_cocoa">NSLog</span>(<span class="string string_quoted string_quoted_double string_quoted_double_objc"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_objc">@"</span>PerformSwizzle Error: Class not found<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_objc">"</span></span>);
<span class="meta meta_preprocessor meta_preprocessor_c">#<span class="keyword keyword_control keyword_control_import keyword_control_import_c">endif</span></span>
    }
}

<span class="storage storage_type storage_type_c">void</span><span class="meta meta_function meta_function_c"> <span class="entity entity_name entity_name_function entity_name_function_c">MethodSwizzle</span><span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">(</span><span class="storage storage_type storage_type_objc">Class</span> aClass, <span class="storage storage_type storage_type_objc">SEL</span> orig_sel, <span class="storage storage_type storage_type_objc">SEL</span> alt_sel<span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">)</span></span>
{
    PerformSwizzle(aClass, orig_sel, alt_sel, <span class="constant constant_language constant_language_objc">YES</span>);
}

<span class="storage storage_type storage_type_c">void</span><span class="meta meta_function meta_function_c"> <span class="entity entity_name entity_name_function entity_name_function_c">ClassMethodSwizzle</span><span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">(</span><span class="storage storage_type storage_type_objc">Class</span> aClass, <span class="storage storage_type storage_type_objc">SEL</span> orig_sel, <span class="storage storage_type storage_type_objc">SEL</span> alt_sel<span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_c">)</span></span>
{
    PerformSwizzle(aClass, orig_sel, alt_sel, <span class="constant constant_language constant_language_objc">NO</span>);
}</span></pre>

<p></typo:tmcode></p>

<p>This particular implementation swaps the <code>method_imp</code> pointers on the two methods, so calling the
first method will call the original implementation of the second, and vice versa. The elegant aspect
to this is when you want to call the original method &#8212; simply call your new method again. The following
example, adapted from <a href="http://pimpmysafari.com/plugins/safarisource-161">SafariSource</a>, demonstrates this.</p>

<p><typo:tmcode theme="Space Cadet"></p>

<pre class="textmate-source"><span class="source source_objc">- (<span class="storage storage_type storage_type_c">void</span>) mySetString:(<span class="support support_class support_class_cocoa">NSString</span> *)string {
    <span class="support support_class support_class_cocoa">NSUserDefaults</span> *defs = <span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span><span class="support support_class support_class_cocoa">NSUserDefaults</span> <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">standardUserDefaults</span></span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span>;
    <span class="keyword keyword_control keyword_control_c">if</span> (<span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span>defs <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">boolForKey<span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span>SafariSourceEnabled</span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span>) {
        <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> do stuff here
</span>    } <span class="keyword keyword_control keyword_control_c">else</span> {
        <span class="meta meta_bracketed meta_bracketed_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">[</span><span class="variable variable_language variable_language_objc">self</span> <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">mySetString<span class="punctuation punctuation_separator punctuation_separator_arguments punctuation_separator_arguments_objc">:</span></span>string</span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_objc">]</span></span>;
    }
}</span></pre>

<p></typo:tmcode></p>

<p>When any code tries to call <code>setString:</code> on this class, the swizzled method <code>mySetString:</code> is called
again. But since the swizzling is done in both directions, trying to call <code>mySetString:</code> will in fact
call <code>setString:</code>. So when the code appears to be calling itself recursively, it&#8217;s really calling
the original function.</p>

<p>The biggest flaw with this implementation (and any others I&#8217;ve seen) is that if you want to swizzle
a method that&#8217;s actually implemented in a superclass, the swizzling will affect all instances of
that superclass instead of just instances of the subclass. There are a couple of possible solutions,
such as dynamically creating a new class and using it to pose as the old class, but none have
yet been implemented to my knowledge.</p>
]]></content:encoded>
			<wfw:commentRss>http://kevin.sb.org/2006/11/16/objective-c-caching-and-method-swizzling/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
<!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->
