<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://alwaysprocessing.blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://alwaysprocessing.blog/" rel="alternate" type="text/html" /><updated>2024-01-14T20:36:51+00:00</updated><id>https://alwaysprocessing.blog/feed.xml</id><title type="html">Always Processing</title><subtitle>Exploring many aspects of software development in detail.</subtitle><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><entry><title type="html">Rust API Bindings: CFStringGetBytes Is Hard, Part 2</title><link href="https://alwaysprocessing.blog/2024/01/07/rust-ffi-cfstr-getbytes-2" rel="alternate" type="text/html" title="Rust API Bindings: CFStringGetBytes Is Hard, Part 2" /><published>2024-01-07T00:00:00+00:00</published><updated>2024-01-07T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2024/01/07/rust-ffi-cfstr-getbytes-2</id><content type="html" xml:base="https://alwaysprocessing.blog/2024/01/07/rust-ffi-cfstr-getbytes-2"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>The <a href="https://alwaysprocessing.blog/2023/12/29/rust-ffi-cfstr-getbytes-1">last post</a> described the complexities I encountered when building Rust bindings for <a href="https://developer.apple.com/documentation/corefoundation/1543006-cfstringgetbytes?language=objc"><code>CFStringGetBytes</code></a>. This post shares the rationale behind my design choices for the first bindings layer. This lowest level layer mitigates the four problems identified in the previous post.</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>I say "first bindings layer" because the method discussed in this post underlies up to four other Rust interfaces for calling <code>CFStringGetBytes</code>. Why so many layers, though? From the <a href="https://rust-lang.github.io/api-guidelines/flexibility.html#c-intermediate">Rust API Guidelines</a>:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><strong>Functions expose intermediate results to avoid duplicate work</strong></p>
</div>
<div class="paragraph">
<p>Many functions that answer a question also compute interesting related data. If this data is potentially of interest to the client, consider exposing it in the API.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>Therefore, in building what I thought was the most idiomatic Rust API, I exposed successively lower-level layers for more customizability (at the cost of complexity).</p>
</div>
</div>
</div>
<div class="paragraph">
<p>First, let&#8217;s review the Core Foundation C API:</p>
</div>
<div class="listingblock">
<div class="title"><code>String.subproj/CFString.h</code> line <a href="https://github.com/apple/swift-corelibs-foundation/blob/swift-5.9-RELEASE/CoreFoundation/String.subproj/CFString.h#L357">357</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="c"><span></span><span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-nf">CFStringGetBytes</span><span class="tok-p">(</span><span class="tok-n">CFStringRef</span><span class="tok-w"> </span><span class="tok-n">theString</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFRange</span><span class="tok-w"> </span><span class="tok-n">range</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFStringEncoding</span><span class="tok-w"> </span><span class="tok-n">encoding</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">UInt8</span><span class="tok-w"> </span><span class="tok-n">lossByte</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">Boolean</span><span class="tok-w"> </span><span class="tok-n">isExternalRepresentation</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">UInt8</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">buffer</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-n">maxBufLen</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">usedBufLen</span><span class="tok-p">);</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>And compare it to the most direct Rust interface implemented in my crate:</p>
</div>
<div class="listingblock">
<div class="title"><code>src/string.rs</code> lines <a href="https://github.com/briantkelley/apple-rs/blob/e6c8ae9e8aaedf8f888a41debf70ce1b1f919bcc/lib/corefoundation/src/string.rs#L605-L641">605-641</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="rust"><span></span><span class="tok-k">impl</span><span class="tok-w"> </span><span class="tok-nb">String</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">pub</span><span class="tok-w"> </span><span class="tok-k">fn</span> <span class="tok-nf">get_bytes_unchecked</span><span class="tok-p">(</span>
<span class="tok-w">    </span><span class="tok-o">&amp;</span><span class="tok-bp">self</span><span class="tok-p">,</span>
<span class="tok-w">    </span><span class="tok-n">range</span>: <span class="tok-nc">impl</span><span class="tok-w"> </span><span class="tok-n">RangeBounds</span><span class="tok-o">&lt;</span><span class="tok-kt">usize</span><span class="tok-o">&gt;</span><span class="tok-p">,</span>
<span class="tok-w">    </span><span class="tok-n">encoding</span>: <span class="tok-nc">GetBytesEncoding</span><span class="tok-p">,</span>
<span class="tok-w">    </span><span class="tok-n">buf</span>: <span class="tok-nb">Option</span><span class="tok-o">&lt;&amp;</span><span class="tok-k">mut</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-kt">u8</span><span class="tok-p">]</span><span class="tok-o">&gt;</span><span class="tok-p">,</span>
<span class="tok-w">  </span><span class="tok-p">)</span><span class="tok-w"> </span>-&gt; <span class="tok-nc">GetBytesResult</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-cm">/* ... */</span><span class="tok-w"> </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><code>&amp;self</code> represents the <code>CFStringRef</code> pointer, which is typical for bindings of object-oriented interfaces.</p>
</li>
<li>
<p>The <code>CFRange</code> parameter equivalent is an <code>impl <a href="https://doc.rust-lang.org/std/ops/trait.RangeBounds.html">RangeBounds</a>&lt;usize&gt;</code>, enabling the caller to use a <a href="https://doc.rust-lang.org/reference/expressions/range-expr.html">Rust range expression</a>. A caller may, for example, pass <code>..</code> to specify the full range of the string.</p>
<div class="ulist">
<ul>
<li>
<p><code>CFRange</code>'s fields are of type <code>CFIndex</code>, which is a signed type. The first post in this mini-series discussed the design choices in implementing <a href="https://alwaysprocessing.blog/2023/12/27/rust-ffi-signed-conv">unsigned to signed conversion</a>.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>GetBytesEncoding</code> replaces <code>CFStringEncoding</code> and also subsumes <code>lossByte</code> and <code>isExternalRepresentation</code>. The following section provides more detail.</p>
</li>
<li>
<p><code>buf: Option&lt;&amp;mut [u8]&gt;</code> captures the optional <code>UInt8 &#42;buffer</code> and <code>CFIndex maxBufLen</code> arguments. The <a href="https://doc.rust-lang.org/std/option/"><code>Option</code></a> type clearly expresses that a buffer is not required. If supplied, though, the slice provides the buffer&#8217;s length.</p>
</li>
<li>
<p>The <code>GetBytesResult</code> return type includes the C API&#8217;s return value (the number of UTF-16 code units converted) and the out parameter in the C API, <code>usedBufLen</code>.</p>
</li>
<li>
<p>The <code>&#95;unchecked</code> suffix hints that this method has a quirk the caller must handle. The following sections elaborate on this behavior.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This method <a href="https://github.com/briantkelley/apple-rs/blob/e6c8ae9e8aaedf8f888a41debf70ce1b1f919bcc/lib/corefoundation/src/string.rs#L664-L675">implements a check</a> to mitigate the fourth problem identified in the previous post:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>If <code>encoding == kCFStringEncodingUTF16</code>, <code>isExternalRepresentation == true</code>, and <code>maxBufLen &lt; 2</code>, Core Foundation will overrun the <code>buffer</code> when writing the BOM. (The UTF-32 BOM write does validate the buffer&#8217;s capacity.)</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>Without this check, the function&#8217;s use could lead to <a href="https://docs.rs/dtolnay/0.0.7/dtolnay/macro._03__soundness_bugs.html">unsoundness</a>, so it would need the <code>unsafe</code> qualifier.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="getbytesencoding">GetBytesEncoding</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <code>GetBytesEncoding</code> struct encompasses the <code>CFStringEncoding</code>, <code>lossByte</code>, and <code>isExternalRepresentation</code> arguments.</p>
</div>
<div class="listingblock">
<div class="title"><code>src/string.rs</code> lines <a href="https://github.com/briantkelley/apple-rs/blob/e6c8ae9e8aaedf8f888a41debf70ce1b1f919bcc/lib/corefoundation/src/string.rs#L126-L161">126-161</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="rust"><span></span><span class="tok-k">pub</span><span class="tok-w"> </span><span class="tok-k">enum</span> <span class="tok-nc">GetBytesEncoding</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">CharacterSet</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">character_set</span>: <span class="tok-nc">CharacterSet</span><span class="tok-p">,</span>

<span class="tok-w">    </span><span class="tok-sd">/// **Note:** Core Foundation will process surrogate pairs as two individual lossy code</span>
<span class="tok-w">    </span><span class="tok-sd">/// points, so the number of output code points will equal the number of input code units.</span>
<span class="tok-w">    </span><span class="tok-n">loss_byte</span>: <span class="tok-nb">Option</span><span class="tok-o">&lt;</span><span class="tok-n">NonZeroU8</span><span class="tok-o">&gt;</span><span class="tok-p">,</span>
<span class="tok-w">  </span><span class="tok-p">},</span>
<span class="tok-w">  </span><span class="tok-n">Utf8</span><span class="tok-p">,</span>
<span class="tok-w">  </span><span class="tok-n">Utf16</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">byte_order</span>: <span class="tok-nc">GetBytesByteOrder</span><span class="tok-p">,</span>
<span class="tok-w">  </span><span class="tok-p">},</span>
<span class="tok-w">  </span><span class="tok-n">Utf32</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">byte_order</span>: <span class="tok-nc">GetBytesByteOrder</span><span class="tok-p">,</span>
<span class="tok-w">    </span><span class="tok-n">loss_byte</span>: <span class="tok-nb">Option</span><span class="tok-o">&lt;</span><span class="tok-n">NonZeroU8</span><span class="tok-o">&gt;</span><span class="tok-p">,</span>
<span class="tok-w">  </span><span class="tok-p">},</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>The <a href="https://github.com/briantkelley/apple-rs/blob/e6c8ae9e8aaedf8f888a41debf70ce1b1f919bcc/lib/corefoundation/src/string/character_set.rs"><code>CharacterSet</code></a> enum encompasses all the non-Unicode encodings. (The corresponding string creation function, <a href="https://developer.apple.com/documentation/corefoundation/1543419-cfstringcreatewithbytes?language=objc"><code>CFStringCreateWithBytes</code></a>, is quirky, too. The string construction bindings also specifically handle UTF-8, UTF-16, and UTF-32, and handle all non-Unicode representations with the <code>CharacterSet</code> enum.)</p>
<div class="ulist">
<ul>
<li>
<p>The <code>lossByte</code> type in the bindings is an <code>Option</code> of <a href="https://doc.rust-lang.org/stable/std/num/struct.NonZeroUsize.html"><code>NonZeroU8</code></a> to express, through the type system, that a loss byte, if used, must have a non-zero value.</p>
</li>
<li>
<p>Unfortunately, the use of a comment was the only mitigation I could find for the second problem identified in the previous post:</p>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>A code point encoded as a surrogate pair becomes two lossy code points for non-Unicode encodings.</p>
</div>
</blockquote>
</div>
</li>
</ul>
</div>
</li>
<li>
<p>The <code>Utf8</code> encoding does not have a loss byte, mitigating potential unexpected behavior in reading the call site and documentation, identified in part a of the first problem in the previous post:</p>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Even if the caller provides a <code>lossByte</code>, the function <strong>does not</strong> process the code unit as a lossy conversion.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>The absence of a conversion fallback may imply UTF-8 cannot fail like UTF-16, which is a contributing factor to the method&#8217;s <code>&#95;unchecked</code> suffix&mdash;the behavior of a straightforward call may not align with default assumptions.</p>
</div>
</li>
<li>
<p><code>Utf16</code> has a <code>GetBytesByteOrder</code> field, discussed below, which implements the <code>isExternalRepresentation</code> argument.</p>
</li>
<li>
<p><code>Utf32</code>, like <code>Utf16</code>, also has a <code>GetBytesByteOrder</code> field and, similar to <code>CharacterSet</code>, has a loss byte to handle invalid surrogates (clarifying UTF-32 is the only Unicode target encoding that implements loss byte support, as described in part b of the first problem in the previous post).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>UTF-16 and UTF-32 use 16-bit and 32-bit integer scalars, which may have big or little endian byte orders. <code>GetBytesByteOrder</code> enumerates the supported options.</p>
</div>
<div class="listingblock">
<div class="title"><code>src/string.rs</code> lines <a href="https://github.com/briantkelley/apple-rs/blob/e6c8ae9e8aaedf8f888a41debf70ce1b1f919bcc/lib/corefoundation/src/string.rs#L109-L124">109-124</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="rust"><span></span><span class="tok-k">pub</span><span class="tok-w"> </span><span class="tok-k">enum</span> <span class="tok-nc">GetBytesByteOrder</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">BigEndian</span><span class="tok-p">,</span>
<span class="tok-w">  </span><span class="tok-n">HostNative</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">include_bom</span>: <span class="tok-kt">bool</span><span class="tok-p">,</span>
<span class="tok-w">  </span><span class="tok-p">},</span>
<span class="tok-w">  </span><span class="tok-n">LittleEndian</span><span class="tok-p">,</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Core Foundation only supports writing a byte order mark, or BOM (<code>isExternalRepresentation = true</code>), when using the host&#8217;s native byte order. This enum prevents callers from specifying unsupported combinations and, therefore, receiving unexpected results.</p>
</div>
<div class="paragraph">
<p>The combination of <code>GetBytesEncoding::Utf8</code> and the implementation of <code>isExternalRepresentation</code> in <code>GetBytesByteOrder</code> combine to mitigate the third problem identified in the previous post:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><code>isExternalRepresentation</code> <strong>does not</strong> encode a BOM for UTF-8 despite the implication in the comment.</p>
</div>
</blockquote>
</div>
</div>
</div>
<div class="sect1">
<h2 id="getbytesresult">GetBytesResult</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The function returns a struct incorporating the C API’s return value and out parameter.</p>
</div>
<div class="listingblock">
<div class="title"><code>src/string.rs</code> lines <a href="https://github.com/briantkelley/apple-rs/blob/e6c8ae9e8aaedf8f888a41debf70ce1b1f919bcc/lib/corefoundation/src/string.rs#L202-L222">202-222</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="rust"><span></span><span class="tok-k">pub</span><span class="tok-w"> </span><span class="tok-k">struct</span> <span class="tok-nc">GetBytesResult</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">pub</span><span class="tok-w"> </span><span class="tok-n">buf_len</span>: <span class="tok-kt">usize</span><span class="tok-p">,</span>
<span class="tok-w">  </span><span class="tok-k">pub</span><span class="tok-w"> </span><span class="tok-n">remaining</span>: <span class="tok-nb">Option</span><span class="tok-o">&lt;</span><span class="tok-n">Range</span><span class="tok-o">&lt;</span><span class="tok-kt">usize</span><span class="tok-o">&gt;&gt;</span><span class="tok-p">,</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If <code>buf</code> was <a href="https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some"><code>Some</code></a>, the <code>buf_len</code> field contains the number of bytes written into the slice. Otherwise, it contains the number of bytes required to convert the processed range.</p>
</div>
<div class="paragraph">
<p>If the call converted the entire input <code>range</code>, the <code>remaining</code> field is <a href="https://doc.rust-lang.org/std/option/enum.Option.html#variant.None"><code>None</code></a>. Otherwise, it contains the portion of the input range not converted during the call. Returning a <code>Range</code> instead of the number of UTF-16 code units converted simplifies conversion loop implementations by providing a clear "done" signal and the range argument value for the next call.</p>
</div>
<div class="paragraph">
<p>If the caller does not provide a loss byte, <em>any</em> conversion (except to UTF-16) may fail. As this function is "unchecked," it&#8217;s the caller&#8217;s responsibility to <em>check</em> for forward progress, or it risks never terminating.</p>
</div>
<div class="paragraph">
<p>The next post will discuss the <code>get_bytes</code> method, which explicitly handles lossy conversions and prevents a potential infinite loop.</p>
</div>
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[Using Rust&#8217;s features, we can provide API bindings to CFStringGetBytes that prevent unsupported argument combinations at the call site to fix the problems identified in the previous post.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/143db75f-fa33-44a3-5c54-f83309888400/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/143db75f-fa33-44a3-5c54-f83309888400/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Rust API Bindings: CFStringGetBytes Is Hard, Part 1</title><link href="https://alwaysprocessing.blog/2023/12/29/rust-ffi-cfstr-getbytes-1" rel="alternate" type="text/html" title="Rust API Bindings: CFStringGetBytes Is Hard, Part 1" /><published>2023-12-29T00:00:00+00:00</published><updated>2023-12-29T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/12/29/rust-ffi-cfstr-getbytes-1</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/12/29/rust-ffi-cfstr-getbytes-1"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>When I started building Rust API bindings for Core Foundation, I thought implementing the <a href="https://doc.rust-lang.org/std/fmt/trait.Debug.html"><code>Debug</code></a> trait with <a href="https://developer.apple.com/documentation/corefoundation/1521252-cfcopydescription?language=objc"><code>CFCopyDescription</code></a> would be a great place to start, and it would help validate incremental progress in implementing the crate.</p>
</div>
<div class="paragraph">
<p>Use of <code>CFStringCopyBytes</code> is required to write the string value returned by <code>CFCopyDescription</code> to the <code>Debug</code> <a href="https://doc.rust-lang.org/std/fmt/struct.Formatter.html"><code>Formatter</code></a> as <code>CFStrings</code> are canonically UTF-16 and Rust uses UTF-8, so this ended up being the first function for which I&#8217;d write Rust API bindings.</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>When writing API bindings, I prefer to cover 100% of the API up front. Incomplete binding layers have bitten me in the past, so I try to avoid creating <em>any</em> divergent behavior in a logically equivalent interface. Insufficient API coverage may also lead to a bindings layer design gap that can be difficult to close after accumulating many dependencies.</p>
</div>
<div class="paragraph">
<p>API bindings are a domain where it&#8217;s essential to <strong>go slow</strong> at first to coherently incorporate the entire API surface area into the design of the bindings to enable product development <strong>to go fast</strong> by not getting caught up fixing bindings bugs and closing bindings gaps.</p>
</div>
</div>
</div>
<div class="paragraph">
<p>In all interface design, I have two common goals:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>The interface should be idiomatic&mdash;a person proficient in the language should find the naming, design patterns, integration points, etc., intuitive and familiar.</p>
</li>
<li>
<p>It should not be possible to represent an invalid state. In particular, assertions, preconditions, etc., are unnecessary because the compiler will reject code containing invalid states or control flow.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>I had intended to discuss my Rust API design in today&#8217;s post, but I discovered a potential invalid state in the design in my final review and am still working on a fix. That works well for this blog since we can separate the problem and solution into independent posts!</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="cfstringgetbytes-complexity">CFStringGetBytes Complexity</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The API doesn&#8217;t seem <em>that</em> complicated:</p>
</div>
<div class="listingblock">
<div class="title"><code>String.subproj/CFString.h</code> lines <a href="https://github.com/apple/swift-corelibs-foundation/blob/swift-5.9-RELEASE/CoreFoundation/String.subproj/CFString.h#L340-L357">340-357</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="c"><span></span><span class="tok-cm">/* The primitive conversion routine; allows you to convert a string piece at a time</span>
<span class="tok-cm">       into a fixed size buffer. Returns number of characters converted.</span>
<span class="tok-cm">   Characters that cannot be converted to the specified encoding are represented</span>
<span class="tok-cm">       with the byte specified by lossByte; if lossByte is 0, then lossy conversion</span>
<span class="tok-cm">       is not allowed and conversion stops, returning partial results.</span>
<span class="tok-cm">   Pass buffer==NULL if you don&#39;t care about the converted string (but just the convertability,</span>
<span class="tok-cm">       or number of bytes required).</span>
<span class="tok-cm">   maxBufLength indicates the maximum number of bytes to generate. It is ignored when buffer==NULL.</span>
<span class="tok-cm">   Does not zero-terminate. If you want to create Pascal or C string, allow one extra byte at start or end.</span>
<span class="tok-cm">   Setting isExternalRepresentation causes any extra bytes that would allow</span>
<span class="tok-cm">       the data to be made persistent to be included; for instance, the Unicode BOM. Note that</span>
<span class="tok-cm">       CFString prepends UTF encoded data with the Unicode BOM &lt;http://www.unicode.org/faq/utf_bom.html&gt;</span>
<span class="tok-cm">       when generating external representation if the target encoding allows. It&#39;s important to note that</span>
<span class="tok-cm">       only UTF-8, UTF-16, and UTF-32 define the handling of the byte order mark character, and the &quot;LE&quot;</span>
<span class="tok-cm">       and &quot;BE&quot; variants of UTF-16 and UTF-32 don&#39;t.</span>
<span class="tok-cm">*/</span>
<span class="tok-n">CF_EXPORT</span>
<span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-n">CFStringGetBytes</span><span class="tok-p">(</span><span class="tok-n">CFStringRef</span><span class="tok-w"> </span><span class="tok-n">theString</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFRange</span><span class="tok-w"> </span><span class="tok-n">range</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFStringEncoding</span><span class="tok-w"> </span><span class="tok-n">encoding</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">UInt8</span><span class="tok-w"> </span><span class="tok-n">lossByte</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">Boolean</span><span class="tok-w"> </span><span class="tok-n">isExternalRepresentation</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">UInt8</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">buffer</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-n">maxBufLen</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">usedBufLen</span><span class="tok-p">);</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>But, in writing tests for my bindings, I discovered the following behaviors that were not apparent to me from the comment or the <a href="https://developer.apple.com/documentation/corefoundation/1543006-cfstringgetbytes?language=objc">documentation</a>:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>range</code> may start or end in the middle of a <a href="https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF">surrogate pair</a>, or the <code>CFString</code> may contain invalid UTF-16 (does not validate strings created from a UTF-16 buffer, and it allows the deletion of a surrogate code unit without deleting its counterpart). The handling of invalid surrogates is dependent on the encoding:</p>
<div class="olist loweralpha">
<ol class="loweralpha" type="a">
<li>
<p>For UTF-8, conversion stops. Even if the caller provides a <code>lossByte</code>, the function <strong>does not</strong> process the code unit as a lossy conversion.</p>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>Corollary: <strong>Code that assumes UTF-8 conversion cannot fail will infinitely loop</strong> if the string contains invalid UTF-16 and the loop does not validate conversion made forward progress.</p>
</div>
</div>
</div>
</li>
<li>
<p>For UTF-32, the surrogate code unit becomes a lossy conversion.</p>
</li>
<li>
<p>For all other encodings, including UTF-16, there is no observable effect.</p>
</li>
</ol>
</div>
</li>
<li>
<p>A code point encoded as a surrogate pair becomes two lossy code points for non-Unicode encodings.</p>
</li>
<li>
<p><code>isExternalRepresentation</code> <strong>does not</strong> encode a BOM for UTF-8 despite the implication in the comment.</p>
</li>
<li>
<p>If <code>encoding == kCFStringEncodingUTF16</code>, <code>isExternalRepresentation == true</code>, and <code>maxBufLen &lt; 2</code>, Core Foundation will overrun the <code>buffer</code> when writing the BOM. (The UTF-32 BOM write does validate the buffer&#8217;s capacity.)</p>
</li>
</ol>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>I didn&#8217;t see any mention of buffer alignment requirements in the documentation or code. <code>buffer</code>'s type of <code>UInt8 &#42;</code> implies <code>CFStringGetBytes</code> supports unaligned buffers. Such support is reasonable, for example, to facilitate callers writing bytes into a persistent format where packing may create unaligned offsets. But, as far as I can tell, the <a href="https://github.com/apple/swift-corelibs-foundation/blob/swift-5.9-RELEASE/CoreFoundation/String.subproj/CFStringEncodings.c#L754-L806">implementation</a> relies on undefined behavior (casting the buffer pointer to a type with stricter alignment) for unaligned pointer support. Precariously but fortuitously, the instructions selected by the compiler support unaligned writes.</p>
</div>
</div>
</div>
<div class="paragraph">
<p>For the most part, these are insignificant corner cases:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Although a lot of code working with UTF-16 needs to handle surrogate pairs better, I suspect there are not many cases where a surrogate pair split occurs at the beginning or end of a range.</p>
</li>
<li>
<p>The primary conversion direction for non-Unicode encodings is <em>into</em> Unicode, so the code point inflation of surrogate pairs when converting from Unicode is likely rare.</p>
</li>
<li>
<p>In practice, BOMs are rarely used, especially for UTF-8.</p>
</li>
<li>
<p>No production code uses a buffer size of one byte.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Fortunately, a few layers of abstraction can simplify most of this complexity. Stay tuned for an overview of how I designed a Rust API to enforce correct and predictable behavior.</p>
</div>
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[Designing a "correct by default" interface for CFStringGetBytes is surprisingly complex, as many of its behaviors are encoding-dependent.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/8255f6c4-0f05-425b-f936-cef2e1030a00/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/8255f6c4-0f05-425b-f936-cef2e1030a00/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Rust API Bindings: Core Foundation Memory Management and Mutability</title><link href="https://alwaysprocessing.blog/2023/12/28/rust-ffi-cf-rc" rel="alternate" type="text/html" title="Rust API Bindings: Core Foundation Memory Management and Mutability" /><published>2023-12-28T00:00:00+00:00</published><updated>2023-12-28T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/12/28/rust-ffi-cf-rc</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/12/28/rust-ffi-cf-rc"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>As I&#8217;m designing Rust API bindings for Core Foundation, I want the user-facing API to match <a href="https://doc.rust-lang.org/std/">The Rust Standard Library</a> as closely as possible, and memory management is a crucial area whose design significantly impacts the API surface. There are (at least) two critical differences between Core Foundation and The Rust Standard Library in their approach to memory management:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>All Core Foundation objects are allocated on the heap and are reference counted. Generally, Rust types can be stack-allocated, heap-allocated and uniquely owned, or heap-allocated with shared ownership.</p>
</li>
<li>
<p>Core Foundation uses different types for immutable and mutable objects, while Rust expresses mutability through the type system.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>The following summarizes my exploration in this space, my design goals for memory management, and how I achieved them.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="ad-hoc-approach">Ad Hoc Approach</h2>
<div class="sectionbody">
<div class="paragraph">
<p>For many C APIs, wrapping a pointer in a tuple struct and implementing <a href="https://doc.rust-lang.org/std/ops/trait.Drop.html"><code>Drop</code></a> is sufficient to provide an idiomatic Rust API for a foreign interface. Consider the following example of this approach for <code>CFString</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="rust"><span></span><span class="tok-k">struct</span> <span class="tok-nb">String</span><span class="tok-p">(</span><span class="tok-o">*</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">__CFString</span><span class="tok-p">);</span>

<span class="tok-k">impl</span><span class="tok-w"> </span><span class="tok-nb">String</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">fn</span> <span class="tok-nf">len</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-bp">self</span><span class="tok-p">)</span><span class="tok-w"> </span>-&gt; <span class="tok-nc">CFIndex</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-k">unsafe</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-n">CFStringGetLength</span><span class="tok-p">(</span><span class="tok-bp">self</span><span class="tok-p">.</span><span class="tok-mi">0</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span>

<span class="tok-k">impl</span><span class="tok-w"> </span><span class="tok-nb">Drop</span><span class="tok-w"> </span><span class="tok-k">for</span><span class="tok-w"> </span><span class="tok-nb">String</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">fn</span> <span class="tok-nf">drop</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-k">mut</span><span class="tok-w"> </span><span class="tok-bp">self</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-k">unsafe</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-n">CFRelease</span><span class="tok-p">(</span><span class="tok-bp">self</span><span class="tok-p">.</span><span class="tok-mf">0.</span><span class="tok-n">cast</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This approach is straightforward, but it is not a zero-cost abstraction. Each time the Core Foundation object pointer is required, for example, in the <code>len</code> method, the compiler must emit a dereference of the tuple struct <code>&amp;self</code> to load the Core Foundation pointer value. This indirection is unavoidable because we must define a type to implement <code>Drop</code>, though it is negligible in practice.</p>
</div>
<div class="paragraph">
<p>Although Core Foundation is a C-based API, many types have logical subclasses. If we were to add Rust API bindings for <code>CFMutableString</code> with this approach, it would require defining a new, independent type. Adding an implementation of <a href="https://doc.rust-lang.org/std/ops/trait.Deref.html"><code>Deref</code></a> would enable the logical subclass to gain all the methods of its logical superclass through <a href="https://doc.rust-lang.org/std/ops/trait.Deref.html#deref-coercion">deref coercion</a>, and the resulting Rust API would still be reasonably idiomatic.</p>
</div>
<div class="paragraph">
<p>While this is a well-trodden path<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>, I wanted to find a design that:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Is a true zero-cost abstraction.</p>
</li>
<li>
<p>Shows Core Foundation objects are heap-allocated through the type system (e.g., <a href="https://doc.rust-lang.org/std/boxed/struct.Box.html"><code>Box</code></a>).</p>
</li>
<li>
<p>Combines Rust&#8217;s mutable references with Core Foundation&#8217;s mutable types.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="box-for-core-foundation">Box for Core Foundation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I started exploring the design space by building equivalents of the standard library&#8217;s <a href="https://doc.rust-lang.org/std/boxed/struct.Box.html"><code>Box</code></a> and <a href="https://doc.rust-lang.org/std/sync/struct.Arc.html"><code>Arc</code></a> types, knowing unique ownership (à la <code>Box</code>) would be part of the mutability story and that shared pointers (like <code>Arc</code>) are a fundamental part of programming on Apple platforms.</p>
</div>
<div class="paragraph">
<p>Through this exercise, I immediately achieved my first two design goals. Consider the following sample code illustrating the approach for <code>CFString</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="rust"><span></span><span class="tok-k">struct</span> <span class="tok-nb">Box</span><span class="tok-o">&lt;</span><span class="tok-n">T</span><span class="tok-o">&gt;</span><span class="tok-p">(</span><span class="tok-n">NonNull</span><span class="tok-o">&lt;</span><span class="tok-n">T</span><span class="tok-o">&gt;</span><span class="tok-p">);</span>

<span class="tok-k">impl</span><span class="tok-o">&lt;</span><span class="tok-n">T</span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-n">Deref</span><span class="tok-w"> </span><span class="tok-k">for</span><span class="tok-w"> </span><span class="tok-nb">Box</span><span class="tok-o">&lt;</span><span class="tok-n">T</span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">type</span> <span class="tok-nc">Target</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">T</span><span class="tok-p">;</span>

<span class="tok-w">  </span><span class="tok-cp">#[inline]</span>
<span class="tok-w">  </span><span class="tok-k">fn</span> <span class="tok-nf">deref</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-bp">self</span><span class="tok-p">)</span><span class="tok-w"> </span>-&gt; <span class="tok-kp">&amp;</span><span class="tok-nc">Self</span>::<span class="tok-n">Target</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-k">unsafe</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-bp">self</span><span class="tok-p">.</span><span class="tok-mf">0.</span><span class="tok-n">as_ref</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span>

<span class="tok-k">struct</span> <span class="tok-nb">String</span><span class="tok-p">;</span>

<span class="tok-k">impl</span><span class="tok-w"> </span><span class="tok-nb">String</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">fn</span> <span class="tok-nf">new</span><span class="tok-p">()</span><span class="tok-w"> </span>-&gt; <span class="tok-nb">Box</span><span class="tok-o">&lt;</span><span class="tok-bp">Self</span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-cm">/* ... */</span><span class="tok-w"> </span><span class="tok-p">}</span>

<span class="tok-w">  </span><span class="tok-k">fn</span> <span class="tok-nf">len</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-bp">self</span><span class="tok-p">)</span><span class="tok-w"> </span>-&gt; <span class="tok-nc">CFIndex</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-n">cf</span>: <span class="tok-o">*</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">_</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-bp">self</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-k">unsafe</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-n">CFStringGetLength</span><span class="tok-p">(</span><span class="tok-n">cf</span><span class="tok-p">.</span><span class="tok-n">cast</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Like the approach in the previous section, a tuple struct wraps the raw Core Foundation object instance pointer. But this wrapper has three essential differences.</p>
</div>
<div class="paragraph">
<p>First, the type name, <code>Box&lt;T&gt;</code>, signals to the reader that <code>T</code> is heap-allocated and that the instance <code>T</code> is unique.</p>
</div>
<div class="paragraph">
<p>Second, it implements <code>Deref</code> to <code>T</code>, the Rust type implementing the API bindings, which is crucial in making the abstraction zero-cost. When the box is dereferenced by the compiler, for example, to call the <code>len</code> method, the box returns the Core Foundation pointer value as a reference to <code>T</code>. The reference value (i.e., <code>&amp;self</code>) is bitwise identical to the Core Foundation pointer value and can be passed directly through to the C API.</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>The compiler dereferences the tuple struct in each approach, so why is one approach a zero-cost abstraction while the other is not?</p>
</div>
<div class="paragraph">
<p>In the previous section, <code>&amp;self</code> is a reference to the tuple struct. The compiler must load the Core Foundation pointer value from the tuple struct&#8217;s field through the reference. An illustration in C may help clarify:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="c"><span></span><span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">String</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">__CFString</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">s</span><span class="tok-p">;</span><span class="tok-w"> </span><span class="tok-p">};</span>

<span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-nf">String_len</span><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">String</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">self</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">CFStringGetLength</span><span class="tok-p">(</span><span class="tok-n">self</span><span class="tok-o">-&gt;</span><span class="tok-n">s</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-c1">// dereference+member access ^^ is an abstraction cost</span>
<span class="tok-p">}</span>

<span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">String</span><span class="tok-w"> </span><span class="tok-n">s</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-cm">/* ... */</span><span class="tok-p">;</span>
<span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-n">length</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">String_len</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">s</span><span class="tok-p">);</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>In this section, <code>&amp;self</code> <strong>is</strong> the pointer value. The dereferencing operation is a logical process, not a physical one, that is effectively a member access. To illustrate in C:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="c"><span></span><span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">Box__CFString</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">__CFString</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">v</span><span class="tok-p">;</span><span class="tok-w"> </span><span class="tok-p">};</span>

<span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-nf">String_len</span><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">String</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">self</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">CFStringGetLength</span><span class="tok-p">((</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">__CFString</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-n">self</span><span class="tok-p">);</span>
<span class="tok-p">}</span>

<span class="tok-k">struct</span><span class="tok-w"> </span><span class="tok-nc">Box__CFString</span><span class="tok-w"> </span><span class="tok-n">s</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-cm">/* ... */</span><span class="tok-p">;</span>
<span class="tok-n">CFIndex</span><span class="tok-w"> </span><span class="tok-n">length</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">String_len</span><span class="tok-p">(</span><span class="tok-n">s</span><span class="tok-p">.</span><span class="tok-n">v</span><span class="tok-p">);</span>
<span class="tok-c1">//        member access here ^ is zero cost</span></code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Finally, the separation of the type bindings (e.g., <code>String</code>) from the memory management facility (e.g., <code>Box&lt;T&gt;</code>) enables idiomatic, zero-cost use of references to the Core Foundation type bindings. Consider potential bindings for <a href="https://developer.apple.com/documentation/corefoundation/1388767-cfarraygetvalueatindex?language=objc"><code>CFArrayGetValueAtIndex</code></a>. With the approach in this section, the function binding can simply cast the pointer into a reference with the array&#8217;s lifetime.</p>
</div>
<div class="paragraph">
<p>With the approach in the previous section, the bindings for this function could:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Return a new binding instance for the value, retaining and releasing the object. The new binding instance does not have a lifetime associated with the array, so the retain is necessary to guarantee that the object lives at least as long as the binding instance. In many cases, however, the retain/release is unnecessary overhead.</p>
</li>
<li>
<p>Use an <a href="https://github.com/servo/core-foundation-rs/blob/core-foundation-v0.9.4/core-foundation/src/base.rs#L254-L255">intermediate type</a> to associate a lifetime with the binding instance and sidestep its retain/release, which is effectively the same as the function binding for the approach in this section but requires more ceremony to eliminate the retain/release correctly..</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="arc-for-core-foundation">Arc for Core Foundation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>It took more exploration and trial and error to identify an approach to achieve my third design goal of combining Rust&#8217;s mutable references with Core Foundation&#8217;s mutable types.</p>
</div>
<div class="paragraph">
<p>At some point, I asked, "Why do immutable objects need exclusive ownership?" I was eventually able to convince myself that "They don&#8217;t!" Looking back, I don&#8217;t know why this wasn&#8217;t more obvious. Rust&#8217;s documentation for its <a href="https://doc.rust-lang.org/std/sync/struct.Arc.html"><code>Arc</code></a> type clearly states:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>You cannot generally obtain a mutable reference to something inside an <code>Arc</code>.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>With that insight, developing the guidance to identify the appropriate smart pointer type was reasonably straightforward: Is the Core Foundation object instance a mutable type uniquely owned by the raw pointer (i.e., a <code>Create</code> or <code>Copy</code> function return the pointer)? If yes, use <code>Box&lt;T&gt;</code>; otherwise use <code>Arc&lt;T&gt;</code>.</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/briantkelley/apple-rs/blob/447436040f4a83b2e5186eed8a84647b678a95b5/lib/corefoundation/src/rc/">My implementations</a> of <a href="https://github.com/briantkelley/apple-rs/blob/447436040f4a83b2e5186eed8a84647b678a95b5/lib/corefoundation/src/rc/boxed.rs"><code>Box&lt;T&gt;</code></a> and <a href="https://github.com/briantkelley/apple-rs/blob/447436040f4a83b2e5186eed8a84647b678a95b5/lib/corefoundation/src/rc/sync.rs"><code>Arc&lt;T&gt;</code></a> for Core Foundation are virtually identical, with the primary difference being <code>Box&lt;T&gt;</code> also implements <a href="https://doc.rust-lang.org/std/ops/trait.DerefMut.html"><code>DerefMut</code></a>, <a href="https://doc.rust-lang.org/std/convert/trait.AsMut.html"><code>AsMut</code></a>, and <a href="https://doc.rust-lang.org/std/borrow/trait.BorrowMut.html"><code>BorrowMut</code></a>.</p>
</div>
<div class="paragraph">
<p>The combination of reference counting and mutability in the smart pointer types<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup> fulfilled my design goals and resulted in surprisingly idiomatic Rust code.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="rust"><span></span><span class="tok-k">impl</span><span class="tok-w"> </span><span class="tok-nb">String</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">fn</span> <span class="tok-nf">append</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-k">mut</span><span class="tok-w"> </span><span class="tok-bp">self</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">s</span>: <span class="tok-kp">&amp;</span><span class="tok-nb">String</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-n">cf</span>: <span class="tok-o">*</span><span class="tok-k">mut</span><span class="tok-w"> </span><span class="tok-n">_</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-bp">self</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-n">s</span>: <span class="tok-o">*</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">_</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">s</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-k">unsafe</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><span class="tok-n">CFStringAppend</span><span class="tok-p">(</span><span class="tok-n">cf</span><span class="tok-p">.</span><span class="tok-n">cast</span><span class="tok-p">(),</span><span class="tok-w"> </span><span class="tok-n">s</span><span class="tok-p">.</span><span class="tok-n">cast</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">};</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span>

<span class="tok-k">fn</span> <span class="tok-nf">main</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-k">mut</span><span class="tok-w"> </span><span class="tok-n">s</span>: <span class="tok-nb">Box</span><span class="tok-o">&lt;</span><span class="tok-nb">String</span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">String</span>::<span class="tok-n">new</span><span class="tok-p">();</span>
<span class="tok-w">  </span><span class="tok-n">s</span><span class="tok-p">.</span><span class="tok-n">append</span><span class="tok-p">(</span><span class="tok-n">cfstr</span><span class="tok-o">!</span><span class="tok-p">(</span><span class="tok-s">&quot;Hello, World!&quot;</span><span class="tok-p">));</span>
<span class="tok-w">  </span><span class="tok-fm">println!</span><span class="tok-p">(</span><span class="tok-s">&quot;{s}&quot;</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. The "tuple struct wrapper with <code>Drop</code>" is the approach used by the <a href="https://github.com/servo/core-foundation-rs">Servo Core Foundation bindings</a>.
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. Although I approached this as a clean-room design, similar prior art exists. In 2014, long before I heard of Rust, GitHub user <a href="https://github.com/SSheldon">SSheldon</a> differentiated between owned and shared references in an <a href="https://github.com/SSheldon/rust-objc-id/blob/0cfd60613718ba2376706de99d842bc9a4901a62/src/id.rs">Objective-C smart pointer</a>.
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[The design patterns used by Core Foundation for memory management and mutability fit surprisingly well in idiomatic Rust. This post shares an overview of how I reached this conclusion the hard way.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/8b35fc93-1528-4fc3-b2a3-524b14093a00/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/8b35fc93-1528-4fc3-b2a3-524b14093a00/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Rust API Bindings: Core Foundation Signed/Unsigned Conversion</title><link href="https://alwaysprocessing.blog/2023/12/27/rust-ffi-signed-conv" rel="alternate" type="text/html" title="Rust API Bindings: Core Foundation Signed/Unsigned Conversion" /><published>2023-12-27T00:00:00+00:00</published><updated>2023-12-27T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/12/27/rust-ffi-signed-conv</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/12/27/rust-ffi-signed-conv"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Core Foundation&#8217;s canonical index type and size type, <code>CFIndex</code>, is signed. Foundation&#8217;s canonical index type and size type, <code>NSUInteger</code>, is unsigned, and Rust&#8217;s canonical type, <code>usize</code>, is unsigned too.</p>
</div>
<div class="paragraph">
<p>Handling <code>CFIndex</code>/<code>usize</code> (signed/unsigned) conversion is important in crafting idiomatic Rust API bindings. Before implementing an approach for my crate, I looked at how Apple handled this problem for Core Foundation&harr;Foundation and Foundation&harr;Swift to understand how they balanced performance (an unchecked bit cast) with correctness (detect and handle a sign change).</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="toll-free-bridging-approach">Toll-Free Bridging Approach</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Many Core Foundation and Foundation <a href="https://alwaysprocessing.blog/2023/02/16/objc-unrealized-classes#toll-free-bridging">types are interchangeable</a>. <code>CFIndex</code> is used throughout Core Foundation, while Foundation&#8217;s equivalent interface uses <code>NSUInteger</code>. I don&#8217;t have access to the Foundation source code, but in peeking at the assembly (examples below), it seems <strong>Foundation effectively uses an unchecked bit cast</strong> when converting between <code>CFIndex</code> and <code>NSUInteger</code>&mdash;the values pass through without detection of a potential sign change. So, integer values with the bit at <code>1 &lt;&lt; sizeof(size_t) * 8 - 1</code> set to <code>1</code> will be negative in the Core Foundation interface but positive in the Foundation interface.</p>
</div>
<div class="paragraph">
<p>The only acknowledgment of the type mismatch in Apple&#8217;s documentation (as far as I&#8217;m aware) is this (slightly edited) description of the <code>location</code> and <code>length</code> fields on <a href="https://developer.apple.com/documentation/corefoundation/cfrange"><code>CFRange</code></a> and <a href="https://developer.apple.com/documentation/foundation/nsrange/1459533-location"><code>NSRange</code></a>:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>For type compatibility with the rest of the system, <code>LONG_MAX</code> is the maximum value you should use for location and length.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>An interesting related quirk is the difference between <a href="https://github.com/apple/swift-corelibs-foundation/blob/swift-5.9-RELEASE/CoreFoundation/Base.subproj/CFBase.h#L497"><code>kCFNotFound</code></a> and <a href="https://github.com/apple/swift-corelibs-foundation/blob/swift-5.9-RELEASE/Darwin/Foundation-swiftoverlay/Foundation.swift#L26"><code>NSNotFound</code></a>. Although many Core Foundation and Foundation types are interchangeable, the semantic "not found" value is interface-dependent. <code>kCFNotFound</code> is defined as <code>-1</code> while <code>NSNotFound</code> is <code>NSIntegerMax</code> (i.e., the maximum value of <code>CFIndex</code>, <code>LONG_MAX</code>). So, the addressable range of failable Foundation methods returning an <code>NSRange</code> is effectively limited to <code>[0, LONG_MAX)</code>.</p>
</div>
<div class="paragraph">
<p>Using a signed type in the underlying Core Foundation implementation and <code>NSNotFound</code>'s definition as the maximum signed value inhibit Foundation from utilizing the full range of the unsigned type.</p>
</div>
<div class="sect2">
<h3 id="unchecked-bit-cast-examples">Unchecked Bit Cast Examples</h3>
<div class="paragraph">
<p><code>-[NSString length]</code> returns an <code>NSUInteger</code>, but its implementation is a simple tail call into Core Foundation&#8217;s <a href="https://github.com/apple/swift-corelibs-foundation/blob/swift-5.9-RELEASE/CoreFoundation/String.subproj/CFString.c#L2098-L2102"><code>_CFStringGetLength2</code></a>, which returns a <code>CFIndex</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="asm"><span></span><span class="tok-nf">CoreFoundation</span><span class="tok-err">`</span><span class="tok-p">-[</span><span class="tok-no">__NSCFString</span><span class="tok-w"> </span><span class="tok-no">length</span><span class="tok-p">]:</span>
<span class="tok-w">  </span><span class="tok-nf">b</span><span class="tok-w">       </span><span class="tok-no">_CFStringGetLength2</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Every method in Objective-C has two implicit arguments:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>self</code>: The pointer to the object instance</p>
</li>
<li>
<p><code>&#95;cmd</code>: The <code>SEL</code> that resolved to the method</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>The above tail call works because <code>&#95;CFStringGetLength2</code> has one argument, the pointer to the object instance, so the Objective-C method and the Core Foundation C function have the same ABI, and no work is required to forward the arguments. (<code>&#95;CFStringGetLength2</code> ignores the second argument.)</p>
</div>
<div class="paragraph">
<p><code>-[NSString getCharacters:range:]</code>, on the other hand, has to do some work before and after its call to Core Foundation&#8217;s <a href="https://github.com/apple/swift-corelibs-foundation/blob/swift-5.9-RELEASE/CoreFoundation/String.subproj/CFString.c#L2165-L2172"><code>_CFStringCheckAndGetCharacters</code></a>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The Objective-C method has four arguments (<code>self</code>, <code>&#95;cmd</code>, <code>buffer</code>, and <code>range</code>), while the Core Foundation function has three (<code>str</code>, <code>range</code>, <code>buffer</code>). Removing <code>&#95;cmd</code> requires moving the arguments that follow it. Also, in this case, the last two arguments are swapped between the interfaces and need to be reordered.</p>
</li>
<li>
<p>Generally, Core Foundation does not do bounds checking, while Foundation does. Foundation calls a Core Foundation SPI (System Programming Interface) that returns an error code if the range is out of bounds so Foundation can raise an exception.</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="asm"><span></span><span class="tok-nf">CoreFoundation</span><span class="tok-err">`</span><span class="tok-p">-[</span><span class="tok-no">__NSCFString</span><span class="tok-w"> </span><span class="tok-no">getCharacters</span><span class="tok-p">:</span><span class="tok-no">range</span><span class="tok-p">:]:</span>
<span class="tok-w">  </span><span class="tok-nf">pacibsp</span><span class="tok-w">                         </span><span class="tok-c1">; insert PAC into LR using SP as the modifier and key B</span>
<span class="tok-w">  </span><span class="tok-nf">stp</span><span class="tok-w">     </span><span class="tok-no">x20</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x19</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-no">sp</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">#-0</span><span class="tok-no">x20</span><span class="tok-p">]!</span><span class="tok-w"> </span><span class="tok-c1">; SP -= 0x20; SP[0x00] = x20; SP[0x08] = x19</span>
<span class="tok-w">  </span><span class="tok-nf">stp</span><span class="tok-w">     </span><span class="tok-no">x29</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x30</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-no">sp</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">#0</span><span class="tok-no">x10</span><span class="tok-p">]</span><span class="tok-w">   </span><span class="tok-c1">; SP[0x10] = FP; SP[0x18] = LR;</span>
<span class="tok-w">  </span><span class="tok-nf">add</span><span class="tok-w">     </span><span class="tok-no">x29</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">sp</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">#0</span><span class="tok-no">x10</span><span class="tok-w">          </span><span class="tok-c1">; FP = SP + 0x10 (address of {FP, LR})</span>
<span class="tok-w">  </span><span class="tok-nf">mov</span><span class="tok-w">     </span><span class="tok-no">x8</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x2</span><span class="tok-w">                  </span><span class="tok-c1">; x8 = buffer (temporary)</span>
<span class="tok-w">  </span><span class="tok-nf">mov</span><span class="tok-w">     </span><span class="tok-no">x19</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x1</span><span class="tok-w">                 </span><span class="tok-c1">; x19 = _cmd</span>
<span class="tok-w">  </span><span class="tok-nf">mov</span><span class="tok-w">     </span><span class="tok-no">x20</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x0</span><span class="tok-w">                 </span><span class="tok-c1">; x20 = self</span>
<span class="tok-w">  </span><span class="tok-nf">mov</span><span class="tok-w">     </span><span class="tok-no">x1</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x3</span><span class="tok-w">                  </span><span class="tok-c1">; x1 = range.location (arg 1, part 1/2)</span>
<span class="tok-w">  </span><span class="tok-nf">mov</span><span class="tok-w">     </span><span class="tok-no">x2</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x4</span><span class="tok-w">                  </span><span class="tok-c1">; x2 = range.length   (arg 1, part 2/2)</span>
<span class="tok-w">  </span><span class="tok-nf">mov</span><span class="tok-w">     </span><span class="tok-no">x3</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x8</span><span class="tok-w">                  </span><span class="tok-c1">; x3 = buffer         (arg 2)</span>
<span class="tok-w">  </span><span class="tok-nf">bl</span><span class="tok-w">      </span><span class="tok-no">_CFStringCheckAndGetCharacters</span>
<span class="tok-w">  </span><span class="tok-nf">cbnz</span><span class="tok-w">    </span><span class="tok-no">w0</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">raiseException</span><span class="tok-w">      </span><span class="tok-c1">; goto raiseException if _CFStringCheckAndGetCharacters did not return 0</span>
<span class="tok-w">  </span><span class="tok-nf">ldp</span><span class="tok-w">     </span><span class="tok-no">x29</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x30</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-no">sp</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">#0</span><span class="tok-no">x10</span><span class="tok-p">]</span><span class="tok-w">   </span><span class="tok-c1">; FP = SP[0x10]; LR = SP[0x18]</span>
<span class="tok-w">  </span><span class="tok-nf">ldp</span><span class="tok-w">     </span><span class="tok-no">x20</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x19</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-no">sp</span><span class="tok-p">],</span><span class="tok-w"> </span><span class="tok-mi">#0</span><span class="tok-no">x20</span><span class="tok-w">   </span><span class="tok-c1">; x20 = SP[0x00]; x19 = SP[0x08]; SP += 0x20</span>
<span class="tok-w">  </span><span class="tok-nf">retab</span><span class="tok-w">                           </span><span class="tok-c1">; return using PAC with SP as the modifier and key B</span>
<span class="tok-nl">raiseException:</span>
<span class="tok-w">  </span><span class="tok-nf">mov</span><span class="tok-w">     </span><span class="tok-no">x0</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x20</span><span class="tok-w">                 </span><span class="tok-c1">; x0 = self</span>
<span class="tok-w">  </span><span class="tok-nf">mov</span><span class="tok-w">     </span><span class="tok-no">x1</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-no">x19</span><span class="tok-w">                 </span><span class="tok-c1">; x1 = _cmd</span>
<span class="tok-w">  </span><span class="tok-nf">bl</span><span class="tok-w">      </span><span class="tok-p">-[</span><span class="tok-no">__NSCFString</span><span class="tok-w"> </span><span class="tok-no">getCharacters</span><span class="tok-p">:</span><span class="tok-no">range</span><span class="tok-p">:].</span><span class="tok-no">cold.1</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Although some code is required to adapt the Foundation interface to Core Foundation, there is no validation of the range&#8217;s unsigned to signed conversion.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="swifts-approach">Swift&#8217;s Approach</h2>
<div class="sectionbody">
<div class="paragraph">
<p>For Apple&#8217;s frameworks (e.g., AppKit, Foundation, UIKit, etc.), the Swift compiler imports <code>NSUInteger</code> as <code>Int</code>. To make C and Objective-C interfaces visible to Swift code, the Swift compiler "imports" a <a href="https://clang.llvm.org/docs/Modules.html">Clang module</a> to build a Swift module, where "import" is a Swift compiler process that constructs a Swift-native representation of the compatible declarations, definitions, and types present in the Clang module.</p>
</div>
<div class="paragraph">
<p>Normally, <code>NSUInteger</code> imports as <code>UInt</code>. But, in system modules (i.e., modules found under a directory given by an <code>-isystem</code> flag), <strong>Swift silently retypes <code>NSUInteger</code> to <code>Int</code></strong> unless the <code>NSUInteger</code> declares the type of an enum. This retyping operation occurs during the construction of the Swift module. The compiler does not record any metadata about this change, and the operation is not visible to other parts of the compiler. Therefore, any thunks emitted by the compiler to facilitate crossing the language boundary do not check for a potential sign change.</p>
</div>
<div class="sect2">
<h3 id="down-the-rabbit-hole">Down the Rabbit Hole</h3>
<div class="paragraph">
<p>I looked at the Swift compiler implementation to see under what conditions the change from unsigned to signed took place and how it was implemented.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Swift&#8217;s <code>ClangImporter</code>'s <a href="https://github.com/apple/swift/blob/swift-5.9-RELEASE/lib/ClangImporter/ImportType.cpp#L1736-L1751"><code>shouldAllowNSUIntegerAsInt</code></a> member function contains the main logic to determine if the compiler should change the type of <code>NSUInteger</code> to <code>Int</code>. It returns <code>true</code> if:</p>
<div class="ulist">
<ul>
<li>
<p>The declaration is from a system module.</p>
</li>
<li>
<p>And the declaration&#8217;s name <strong>does not</strong> contain <code>Unsigned</code> or <code>unsigned</code>, which is a special case to preserve <code>NSUInteger</code> for <code>+[NSNumber numberWithUnsignedInteger:]</code>, <code>-[NSNumber initWithUnsignedInteger:]</code>, and <code>-[NSNumber unsignedIntegerValue]</code>.</p>
</li>
</ul>
</div>
</li>
<li>
<p>There are <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/lib/Lex/ModuleMap.cpp#L2091-L2092">two ways</a> to identify a Clang module as a system module:</p>
<div class="ulist">
<ul>
<li>
<p>Its module map has the <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/lib/Lex/ModuleMap.cpp#L3000-L3002"><code>&#91;system&#93;</code> attribute</a>.</p>
</li>
<li>
<p>The compiler loaded the module map from a <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/lib/Lex/ModuleMap.cpp#L3081">system directory</a>.</p>
<div class="olist lowerroman">
<ol class="lowerroman" type="i">
<li>
<p>In this case, the loading of the module map occurs when resolving <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/lib/Lex/HeaderSearch.cpp#L1717">header search</a> paths.</p>
</li>
<li>
<p>The module inherits the <code>IsSystem</code> flag <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/lib/Lex/HeaderSearch.cpp#L331">from the directory</a> containing its module map.</p>
</li>
<li>
<p>Any directory that&#8217;s <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/include/clang/Lex/DirectoryLookup.h#L140-L143">not a user directory</a> is a system directory.</p>
</li>
<li>
<p>Clang identifies <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/lib/Lex/InitHeaderSearch.cpp#L156-L164">non-user directories</a> as those belonging to one of its system <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/include/clang/Lex/HeaderSearchOptions.h#L27-L62">directory groups</a>.</p>
</li>
<li>
<p>The <a href="https://github.com/llvm/llvm-project/blob/llvmorg-17.0.1/clang/lib/Frontend/CompilerInvocation.cpp#L3142-L3172">compiler invocation arguments</a> identify which directories belong to the system directory groups.</p>
</li>
</ol>
</div>
</li>
</ul>
</div>
</li>
</ol>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="my-crates-approach">My Crate&#8217;s Approach</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In considering various signed/unsigned conversion approaches for my Core Foundation Rust API bindings, I evaluated Foundation&#8217;s and Swift&#8217;s approach (transparent retyping) with Rust&#8217;s <a href="https://doc.rust-lang.org/reference/behavior-considered-undefined.html">behavior considered undefined</a>. Although Foundation&#8217;s and Swift&#8217;s approaches may lead to an unexpected sign change, they are not considered unsafe by Rust&#8217;s definition. The only potentially related undefined behavior is "Calling a function with the wrong call ABI," but signed-ness is not generally considered part of the C ABI.</p>
</div>
<div class="paragraph">
<p>I introduced <a href="https://github.com/briantkelley/apple-rs/blob/e8259656737238f02df6d13e53619c78fed131b8/lib/corefoundation/src/base/ffi/convert.rs">two traits</a> to facilitate signed/unsigned conversion:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>ExpectFrom</code> performs the signed/unsigned conversion and panics if the conversion fails. Implementations are a convenience wrapper for <code>&lt;T as TryFrom&gt;::try_from(value).expect("")</code>, which, while trivial, reduces the number of ad hoc <code>expect</code>s in bindings code to improve readability. This trait primarily facilitates conversions from idiomatic Rust types into native Core Foundation types. It provides a user-visible signal if <code>CFIndex</code> cannot represent an index or size.</p>
</li>
<li>
<p><code>FromUnchecked</code> performs the signed/unsigned conversion and assumes the result is correct, emulating the transparent retyping approach of Foundation and Swift. This trait primarily facilitates conversions from native Core Foundation types into idiomatic Rust types where it&#8217;s reasonable to assume the value is in bounds.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If a sign change goes undetected, safe Rust code will panic. Unsafe code must ensure all values are in bounds for the given domain so an undetected sign change does not impose any additional burden, assuming a sign change would cause the value to go out of bounds.</p>
</div>
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[Handling CFIndex/usize (signed/unsigned) conversion is important in crafting idiomatic Rust API bindings. I looked at how Apple handled this conversion between Core Foundation and Foundation, and between Foundation and Swift to help inform the direction for my Core Foundation crate.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/e27c94ae-84aa-4171-1a67-9c061159e800/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/e27c94ae-84aa-4171-1a67-9c061159e800/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Objective-C Internals: Release</title><link href="https://alwaysprocessing.blog/2023/10/01/objc-release" rel="alternate" type="text/html" title="Objective-C Internals: Release" /><published>2023-10-01T00:00:00+00:00</published><updated>2023-10-01T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/10/01/objc-release</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/10/01/objc-release"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Objective-C manages memory using a <a href="https://en.wikipedia.org/wiki/Reference_counting">reference counting</a> approach. This post will look at the release operation, which removes a reference count from an object instance. The <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain">previous post</a> covered the retain implementation, which adds a reference count to an object instance. The retain and release implementations are similar because they are inverse operations. I&#8217;ll refer back to the <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain">retain post</a>, where the discussion is similar, so this post can focus on the unique aspects of retain.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="entry-points">Entry Points</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There are two interfaces for reference counting operations: the long-standing <code>NSObject</code> API and a compiler-private API used by ARC, both of which call into a core implementation. The following two subsections will examine each interface&#8217;s release implementation, and the next section will discuss the core implementation.</p>
</div>
<div class="sect2">
<h3 id="nsobject">NSObject</h3>
<div class="paragraph">
<p>Like <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain#nsobject"><code>-&#91;NSObject retain&#93;</code></a>, <code>-[NSObject release]</code> is trivial&mdash;it simply calls <code>&#95;objc_rootRelease()</code> to release <code>self</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/NSObject.mm</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L2544-L2546">2544-2546</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-p">-</span> <span class="tok-p">(</span><span class="tok-kt">void</span><span class="tok-p">)</span><span class="tok-nf">release</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">_objc_rootRelease</span><span class="tok-p">(</span><span class="tok-nb">self</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The term <em>root</em>, as discussed in the <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain#nsobject">retain post</a>, indicates the operation is occurring via a <code>-release</code> message received by the root class in the object&#8217;s <a href="https://alwaysprocessing.blog/2023/01/02/objc-class-arch#architecture-diagram">class hierarchy</a>.</p>
</div>
<div class="paragraph">
<p>Next, the <code>_objc_rootRelease</code> function, which is also trivial, calls <code>objc_object::rootRelease()</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/NSObject.mm</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L1883-L1889">1883-1889</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-nf">_objc_rootRelease</span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">obj</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-n">obj</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-n">obj</span><span class="tok-o">-&gt;</span><span class="tok-n">rootRelease</span><span class="tok-p">();</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Finally, <code>objc_object::rootRelease()</code> calls an overload of <code>rootRelease</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L729-L733">729-733</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-nf">objc_object::rootRelease</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">rootRelease</span><span class="tok-p">(</span><span class="tok-nb">true</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Fast</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The overload called here is the core implementation, which has two parameters:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>performDealloc</code> specifies whether the release operation should deallocate the object instance if the retain count reaches zero. The runtime always passes <code>true</code> for this parameter unless the <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L1850-L1856"><code>&#95;objc_rootReleaseWasZero()</code></a> SPI<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup> (<em>system programming interface</em> for first-party use, as opposed to <em>application programming interface</em> for third-party use) is performing the release.</p>
</li>
<li>
<p><code>variant</code> provides context about the call path, enabling the core implementation to elide unnecessary work. Releases performed through <code>NSObject</code> use <code>RRVariant::Fast</code> to skip the check for whether the class has a custom reference counting implementation because the operation occurring through the root class is, by definition, not custom.</p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="automatic-reference-counting">Automatic Reference Counting</h3>
<div class="paragraph">
<p>When ARC is enabled, the compiler performs reference counting operations through a compiler-private API added for ARC as a performance optimization (also discussed in the <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain#automatic-reference-counting">retain post</a>).</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/NSObject.mm</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L1780-L1786">1780-1786</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-nf">objc_release</span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">obj</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">_objc_isTaggedPointerOrNil</span><span class="tok-p">(</span><span class="tok-n">obj</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">obj</span><span class="tok-o">-&gt;</span><span class="tok-k">release</span><span class="tok-p">();</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If the object pointer value references an object on the heap, derived through the same ceremony as <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain#automatic-reference-counting">retain</a>, the function calls <code>objc_object::release()</code> to perform the release operation.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L709-L716">709-716</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kr">inline</span><span class="tok-w"> </span><span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-nf">objc_object::release</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">isTaggedPointer</span><span class="tok-p">());</span>
<span class="tok-w">  </span><span class="tok-n">rootRelease</span><span class="tok-p">(</span><span class="tok-nb">true</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">FastOrMsgSend</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This function calls the core implementation (though <em>root</em> in <code>rootRelease</code> is a misnomer at this point) with:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>true</code> for <code>performDealloc</code>, for the same reason discussed above in the <a href="#nsobject">NSObject</a> entry point.</p>
</li>
<li>
<p><code>RRVariant::FastOrMsgSend</code> for <code>variant</code>. No introspection, whether direct (see <a href="#rootrelease">rootRelease</a> below) or indirect (via a message send, see <a href="#nsobject">NSObject</a> above), has occurred, so it&#8217;s not yet known whether the object&#8217;s class overrides any of the reference counting methods (hence the function&#8217;s name does <strong>not</strong> contain the term <em>root</em>).</p>
<div class="paragraph">
<p>The <code>MsgSend</code> part of the <code>variant</code> instructs the core implementation to do the introspection necessary to determine whether the object&#8217;s class overrides the reference counting methods. If it does, the core implementation performs the release operation by sending the object a <code>-release</code> message (which may re-enter the runtime via <a href="#nsobject"><code>-&#91;NSObject release&#93;</code></a>).</p>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="rootrelease">rootRelease</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L741-L902"><code>objc_object::rootRelease(bool, RRVariant)</code></a>  function is on the larger side, so we&#8217;ll analyze it piece by piece.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> line <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L744">744</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">isTaggedPointer</span><span class="tok-p">()))</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Although the ARC entry point checks for a tagged pointer, the <code>NSObject</code> entry point does not. It&#8217;s not immediately apparent to me why the <code>NSObject</code> implementation doesn&#8217;t perform this check, but it has to happen somewhere, and in this version of the runtime, it&#8217;s here.</p>
</div>
<div class="paragraph">
<p>Next, the runtime loads the object&#8217;s <code>isa</code> value<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L746-L750">746-750</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">sideTableLocked</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-n">isa_t</span><span class="tok-w"> </span><span class="tok-n">newisa</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">oldisa</span><span class="tok-p">;</span>
<span class="tok-n">oldisa</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">LoadExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If the compiler-private API was the entry point for the release operation, the runtime must check whether the class overrides any reference counting methods<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L752-L764">752-764</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">variant</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">FastOrMsgSend</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-c1">// These checks are only meaningful for objc_release()</span>
<span class="tok-w">  </span><span class="tok-c1">// They are here so that we avoid a re-load of the isa.</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">getDecodedClass</span><span class="tok-p">(</span><span class="tok-nb">false</span><span class="tok-p">)</span><span class="tok-o">-&gt;</span><span class="tok-n">hasCustomRR</span><span class="tok-p">()))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">getDecodedClass</span><span class="tok-p">(</span><span class="tok-nb">false</span><span class="tok-p">)</span><span class="tok-o">-&gt;</span><span class="tok-n">canCallSwiftRR</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-n">swiftRelease</span><span class="tok-p">.</span><span class="tok-n">load</span><span class="tok-p">(</span><span class="tok-n">memory_order_relaxed</span><span class="tok-p">)((</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-p">((</span><span class="tok-kt">void</span><span class="tok-p">(</span><span class="tok-o">*</span><span class="tok-p">)(</span><span class="tok-n">objc_object</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-kt">SEL</span><span class="tok-p">))</span><span class="tok-n">objc_msgSend</span><span class="tok-p">)(</span><span class="tok-k">this</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">@selector</span><span class="tok-p">(</span><span class="tok-k">release</span><span class="tok-p">));</span>
<span class="tok-w">    </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If a class has a custom reference counting implementation, the runtime sends the object a <code>-release</code> message to fulfill the ARC-initiated release operation. Note the object may then call <code>-[NSObject release]</code>, but this code block will not execute again as the <code>variant</code> will be <code>RRVariant::Fast</code>.</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>The return value of <code>true</code> in the message send variant is defensive. It doesn&#8217;t make sense for a class to implement its own reference counting mechanism <em>and</em> call the <code>&#95;objc_rootReleaseWasZero()</code> SPI (the only entry point utilizing the return value). If a class has its own reference counting mechanism, then, by definition, it knows when the reference count reaches zero.</p>
</div>
<div class="paragraph">
<p>Suppose a class implementation uses the SPI with its own reference counting mechanism. In that case, the process will almost certainly crash shortly after the first release of an instance of the class&mdash;the class implementation would deallocate the instance when the SPI returns true, leaving all other objects that previously retained the instance with a dangling pointer.</p>
</div>
</div>
</div>
<div class="paragraph">
<p>Continuing to the next block.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L766-L773">766-773</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">nonpointer</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-c1">// a Class is a Class forever, so we can perform this check once</span>
<span class="tok-w">  </span><span class="tok-c1">// outside of the CAS loop</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">getDecodedClass</span><span class="tok-p">(</span><span class="tok-nb">false</span><span class="tok-p">)</span><span class="tok-o">-&gt;</span><span class="tok-n">isMetaClass</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><code>Class</code> objects are never deallocated and do not require reference counting. So, if the object is a class object, the function returns it without performing any further work.</p>
</div>
<div class="sect2">
<h3 id="compare-and-swap-loop">Compare and Swap Loop</h3>
<div class="paragraph">
<p>The <a href="https://en.wikipedia.org/wiki/Compare-and-swap">compare-and-swap</a> loop is the heart of the release implementation. It starts, perhaps unexpectedly, with a <code>goto</code> label. <a href="#the-full-variant">The Full Variant</a> subsection below discusses this function&#8217;s use of <code>goto</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L775-L777">775-777</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-nl">retry</span><span class="tok-p">:</span>
<span class="tok-k">do</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">newisa</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">oldisa</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The loop first sets <code>newisa</code> to the current <code>isa</code> value (i.e., <code>oldisa</code>), which the following steps will update to reflect the decremented retain count.</p>
</div>
<div class="paragraph">
<p>Then, the loop checks if the object instance has a <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#non-pointer-isa">non-pointer <code>isa</code></a>. If it does not, the retain count is recorded in a side table<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup>. This check is performed in the loop because if this thread loses a compare-and-swap, it could be due to another thread mutating the object in a way that removed its use of a non-pointer <code>isa</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L778-L781">778-781</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">nonpointer</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">tryRetain</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">sidetable_tryRetain</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-o">?</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-w"> </span><span class="tok-o">:</span><span class="tok-w"> </span><span class="tok-nb">nil</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">sidetable_retain</span><span class="tok-p">(</span><span class="tok-n">sideTableLocked</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Next, the loop checks to see if it lost another race.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L782-L788">782-788</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">isDeallocating</span><span class="tok-p">()))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">sideTableLocked</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-n">variant</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Full</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-n">sidetable_unlock</span><span class="tok-p">();</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>An object may be deallocating while a thread is attempting to release it in (at least) two scenarios:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Another thread released the object, causing it to deallocate, usually due to a race condition that occurs when a process concurrently reads from and writes to a <code>strong</code>, <code>nonatomic</code> property. Everything about this scenario is undefined behavior.</p>
</li>
<li>
<p>Logic in <code>-dealloc</code> causes a release to be performed (e.g., the <code>-dealloc</code> implementation passes <code>self</code> to a clean-up routine where the ARC compiler emits a retain/release pair). This scenario is <strong>not</strong> a race condition like the case above because the release occurred on the same thread executing the deallocation.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>If the <code>&#95;objc_rootReleaseWasZero()</code> SPI performed the release, the return value of <code>false</code> indicates the caller should not initiate deallocation, as the object is already deallocating. The return value is otherwise unused by the <code>NSObject</code> and ARC entry points.</p>
</div>
<div class="paragraph">
<p>Finally, we get to the actual decrement.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L791-L797">791-797</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-c1">// don&#39;t check newisa.fast_rr; we already called any RR overrides</span>
<span class="tok-w">  </span><span class="tok-kt">uintptr_t</span><span class="tok-w"> </span><span class="tok-n">carry</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">subc</span><span class="tok-p">(</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RC_ONE</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">carry</span><span class="tok-p">);</span><span class="tok-w">  </span><span class="tok-c1">// extra_rc--</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">carry</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-c1">// don&#39;t ClearExclusive()</span>
<span class="tok-w">    </span><span class="tok-k">goto</span><span class="tok-w"> </span><span class="tok-n">underflow</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Recall the non-pointer <code>isa</code> is a bit field with <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#non-pointer-isa-variants">three variants</a>. The value of <code>RC_ONE</code> is the bit that represents a retain count of one when viewing the bit field as an integer. The retain count is stored in the most significant bits of the <code>isa</code>, so an underflow, or carry, will occur if all of the retain count bits are zero (discussed in the following subsection). Otherwise, <code>newisa</code> contains the decremented retain count if no underflow occurs and is ready to be written back to the object instance.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> line <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L798">798</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">while</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">StoreReleaseExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">)));</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If the value at <code>&amp;isa()</code> matches the value at <code>&amp;oldisa</code>, the compare-and-swap operation succeeds and writes the value of <code>newisa</code> to <code>&amp;isa()</code>, and the loop ends.</p>
</div>
<div class="paragraph">
<p>Otherwise, the value of <code>&amp;isa()</code> has changed since this thread loaded it into <code>oldisa</code>. The compare-and-swap operation fails and writes the new value at <code>&amp;isa()</code> to <code>&amp;oldisa</code>. The loop continues until the thread wins a compare-and-swap operation or another thread changes the object state to activate one of the above return paths.</p>
</div>
<div class="paragraph">
<p>After the loop ends, the runtime checks if the retain count is zero. If it is, it <a href="#deallocate">deallocates</a> the object instance.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L800-L801">800-801</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">isDeallocating</span><span class="tok-p">()))</span>
<span class="tok-w">    </span><span class="tok-k">goto</span><span class="tok-w"> </span><span class="tok-n">deallocate</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Otherwise, the object has a positive retain count. If necessary, the runtime will release the side table lock. The function ends by returning <code>false</code>, indicating to the <code>&#95;objc_rootReleaseWasZero()</code> SPI that the object should not deallocate.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L803-L808">803-808</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">variant</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Full</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">sideTableLocked</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-n">sidetable_unlock</span><span class="tok-p">();</span>
<span class="tok-w">  </span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">sideTableLocked</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="the-full-variant">The Full Variant</h3>
<div class="paragraph">
<p>If the retain count underflows the bits in the non-pointer <code>isa</code>, the runtime reverts the changes to <code>newisa</code>. Then, it checks whether any retain counts previously overflowed to the side table.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L810-L816">810-816</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-nl">underflow</span><span class="tok-p">:</span>
<span class="tok-c1">// newisa.extra_rc-- underflowed: borrow from side table or deallocate</span>
<span class="tok-n">newisa</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">oldisa</span><span class="tok-p">;</span><span class="tok-w"> </span><span class="tok-c1">// abandon newisa to undo the decrement</span>

<span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">has_sidetable_rc</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If no retain counts overflowed to the side table, no retain counts remain, so the release <a href="#deallocate">deallocates</a> the object instance, though I don&#8217;t think this can happen in practice as it would imply an over-release. Either there are retain counts in the side table, or the retain count reached zero and the above code path deallocated the object instance. In my opinion, it would be cleaner if the runtime trapped in this case, as the process will likely crash when <code>-dealloc</code> gets called for a second time.</p>
</div>
<div class="paragraph">
<p>If retain counts did previously overflow to the side table, the runtime checks whether this function invocation has the <code>Fast</code> or <code>FastOrMsgSend</code> variant. If so, it stops its attempt at the release operation and passes the buck to <code>rootRelease_underflow()</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L817-L820">817-820</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">variant</span><span class="tok-w"> </span><span class="tok-o">!=</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Full</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">rootRelease_underflow</span><span class="tok-p">(</span><span class="tok-n">performDealloc</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The function immediately calls back into <code>objc_object::rootRelease(bool, RRVariant)</code> with the <code>Full</code> variant.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/NSObject.mm</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L1379-L1383">1379-1383</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-n">NEVER_INLINE</span><span class="tok-w"> </span><span class="tok-kt">uintptr_t</span><span class="tok-w"> </span><span class="tok-n">objc_object</span><span class="tok-o">::</span><span class="tok-n">rootRelease_underflow</span><span class="tok-p">(</span><span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">performDealloc</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">rootRelease</span><span class="tok-p">(</span><span class="tok-n">performDealloc</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Full</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>I speculated in the <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain#the-full-variant">retain post</a> the purpose of this function is to provide a frame in stack traces to help Apple engineers troubleshoot release crashes in the runtime, as the interplay of side table locking (which uses a non-reentrant spin lock) can be challenging to reason about.</p>
</div>
<div class="paragraph">
<p>If the release count decrement underflows with the <code>Full</code> variant, the runtime obtains a side table lock.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L822-L832">822-832</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-c1">// Transfer retain count from side table to inline storage.</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">sideTableLocked</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-n">sidetable_lock</span><span class="tok-p">();</span>
<span class="tok-w">    </span><span class="tok-n">sideTableLocked</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-c1">// Need to start over to avoid a race against the nonpointer -&gt; raw pointer transition.</span>
<span class="tok-w">    </span><span class="tok-n">oldisa</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">LoadExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">goto</span><span class="tok-w"> </span><span class="tok-n">retry</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Acquiring the side table lock may cause the thread to suspend, so the runtime first removes its exclusive monitor on the <code>isa</code> address, which is required to use the exclusive monitor correctly. From the <a href="https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Application-Level-Memory-Model/Synchronization-and-semaphores/Load-Exclusive-and-Store-Exclusive-usage-restrictions">ARM Architecture Reference Manual</a> (emphasis mine):</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>The exclusives support a single outstanding exclusive access for each processor thread that is executed. &hellip; If the target address of an <code>STREX</code> <em>(store exclusive)</em> is different from the preceding <code>LDREX</code> <em>(load exclusive)</em> in the same thread of execution, behavior can be unpredictable. As a result, an <code>LDREX</code>/<code>STREX</code> pair can only be relied upon to eventually succeed if they are executed with the same address. <strong>Where a context switch&hellip; might change the thread of execution, a <code>CLREX</code> instruction&hellip; must be executed to avoid unwanted effects&hellip;</strong></p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>After obtaining the side table lock, the runtime reloads the <code>isa</code> value and starts the <a href="#compare-and-swap-loop">compare-and-swap loop</a> again to perform the decrement. A reload of the <code>isa</code> is necessary because another thread may have changed the <code>isa</code> while this thread was waiting to acquire the side table lock.</p>
</div>
<div class="paragraph">
<p>Finally, if the decrement again results in an underflow, it&#8217;s safe for the runtime to load any additional retain counts from the side table.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L834-L835">834-835</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-c1">// Try to remove some retain counts from the side table.</span>
<span class="tok-w">  </span><span class="tok-k">auto</span><span class="tok-w"> </span><span class="tok-n">borrow</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">sidetable_subExtraRC_nolock</span><span class="tok-p">(</span><span class="tok-n">RC_HALF</span><span class="tok-p">);</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L1500-L1528"><code>sidetable_subExtraRC_nolock()</code></a> returns a <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-private.h#L213-L214"><code>SidetableBorrow</code></a> struct (borrow in the sense of taking the value of higher digits in a subtraction operation, not as in leasing the values from the side table), which has two fields:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>borrowed</code>: The number of retain counts taken from the side table.</p>
</li>
<li>
<p><code>remaining</code>: The number of retain counts remaining in the side table.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The runtime first checks whether all the retain counts have been removed from the side table to perform additional bookkeeping later. Then, it checks whether the side table returned any retain counts. If the side table is empty, no retain counts remain, so the release will <a href="#deallocate">deallocate</a> the object instance.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L837-L839">837-839</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">emptySideTable</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">borrow</span><span class="tok-p">.</span><span class="tok-n">remaining</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-p">;</span><span class="tok-w"> </span><span class="tok-c1">// we&#39;ll clear the side table if no refcounts remain there</span>

<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">borrow</span><span class="tok-p">.</span><span class="tok-n">borrowed</span><span class="tok-w"> </span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If the side table returned retain counts for the object instance, the runtime attempts to update the non-pointer <code>isa</code> with the retain counts taken from the side table.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L840-L846">840-846</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">    </span><span class="tok-c1">// Side table retain count decreased.</span>
<span class="tok-w">    </span><span class="tok-c1">// Try to add them to the inline count.</span>
<span class="tok-w">    </span><span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">didTransitionToDeallocating</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">extra_rc</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">borrow</span><span class="tok-p">.</span><span class="tok-n">borrowed</span><span class="tok-w"> </span><span class="tok-o">-</span><span class="tok-w"> </span><span class="tok-mi">1</span><span class="tok-p">;</span><span class="tok-w">  </span><span class="tok-c1">// redo the original decrement too</span>
<span class="tok-w">    </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">has_sidetable_rc</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-o">!</span><span class="tok-n">emptySideTable</span><span class="tok-p">;</span>

<span class="tok-w">    </span><span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">stored</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">StoreReleaseExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">);</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>borrow.borrowed</code> field contains the retain counts taken from the side table. The runtime subtracts one from the count (recall this is the code path for an underflow, so the release accounting has not yet occurred) and stores the value in the non-pointer <code>isa</code>'s <code>extra_rc</code> field. It then updates the <code>has_sidetable_rc</code> bit to reflect whether the side table still has overflowed retain counts for the object instance.</p>
</div>
<div class="paragraph">
<p>It then attempts to store the new <code>isa</code> value. The store may fail, which is handled by the next code block.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L848-L863">848-863</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">stored</span><span class="tok-w"> </span><span class="tok-o">&amp;&amp;</span><span class="tok-w"> </span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">nonpointer</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-c1">// Inline update failed.</span>
<span class="tok-w">      </span><span class="tok-c1">// Try it again right now. This prevents livelock on LL/SC architectures</span>
<span class="tok-w">      </span><span class="tok-c1">// where the side table access itself may have dropped the reservation.</span>
<span class="tok-w">      </span><span class="tok-kt">uintptr_t</span><span class="tok-w"> </span><span class="tok-n">overflow</span><span class="tok-p">;</span>
<span class="tok-w">      </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">addc</span><span class="tok-p">(</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RC_ONE</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">borrow</span><span class="tok-p">.</span><span class="tok-n">borrowed</span><span class="tok-mi">-1</span><span class="tok-p">),</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">overflow</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">has_sidetable_rc</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-o">!</span><span class="tok-n">emptySideTable</span><span class="tok-p">;</span>
<span class="tok-w">      </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">overflow</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">        </span><span class="tok-n">stored</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">StoreReleaseExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">        </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">stored</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">          </span><span class="tok-n">didTransitionToDeallocating</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">isDeallocating</span><span class="tok-p">();</span>
<span class="tok-w">        </span><span class="tok-p">}</span>
<span class="tok-w">      </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If placing the retain counts taken from the side table into the non-pointer <code>isa</code> fails, the runtime immediately tries again. The runtime&#8217;s <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-os.h#L199-L208"><code>StoreReleaseExclusive()</code></a> function performs the load exclusive operation if the store exclusive fails, so <code>oldisa</code> is the most recent value. After subtracting one for this release operation, it adds the retain counts taken from the side table, updates the bit tracking if the object instance has retain counts in the side table, and then attempts to store the updated <code>isa</code> again. This retry is likely less than 32 instructions (meeting ARM&#8217;s recommendation; see aside below) and is more likely to succeed than the general path.</p>
</div>
<div class="paragraph">
<p>If the store is successful, the runtime sets <code>didTransitionToDeallocating</code> to <code>true</code> if the retain count has reached zero. But this can never happen in practice, as adding <code>RC_HALF - 1</code> retain counts to the non-pointer <code>isa</code> just succeeded.</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>"LL/SC" in the comment refers to Load-Linked and Store-Conditional instructions, the general purpose name for the AArch64 <code>lxdr</code> (load exclusive) and <code>stxr</code> (store exclusive) instructions. <em>Exclusive</em> refers to ARM&#8217;s <em>exclusive monitor</em> synchronization primitive and does not imply anything about execution behavior (i.e., nothing prevents other processors or cores from reading from or writing to the address).</p>
</div>
<div class="paragraph">
<p>The store instruction can fail in several circumstances:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Another processor or core has written to the address range associated with the most recent load exclusive operation.</p>
</li>
<li>
<p>A context switch occurred between the load exclusive and store exclusive operation (e.g., interrupt, thread preemption), and the handler routine cleared the exclusive monitor.</p>
</li>
<li>
<p>A subroutine performed another load exclusive/store exclusive operation, invalidating the previous load exclusive operation.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>When the comment mentions the side table access may have dropped the reservation, it could be due to any of the points above.</p>
</div>
<div class="paragraph">
<p><a href="https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Application-Level-Memory-Model/Synchronization-and-semaphores/Load-Exclusive-and-Store-Exclusive-usage-restrictions">ARM recommends 128 byte limit</a> between the load exclusive and store exclusive instructions to minimize the chances a context switch clears the monitor during the operation. I haven&#8217;t measured the number of machine instructions here, but I&#8217;d be willing to bet there are more than 32 instructions between the load and store in the main loop.</p>
</div>
<div class="paragraph">
<p>I could only contrive one scenario where a live lock may occur: one thread continuously retains and releases the object, causing many writes to the <code>isa</code>, thus inhibiting a successful store exclusive operation on the thread dealing with the underflow. A retain/release loop could perform many iterations in the time required for a side table lookup, so without a fast path, the release operation may never finish.</p>
</div>
<div class="paragraph">
<p>If another thread performed one or more retains, no live lock would occur, as this thread should be able to decrement from that count. Or, if another thread performed one or more releases, no live lock would occur as one thread would win the race to acquire the side table lock.</p>
</div>
</div>
</div>
<div class="paragraph">
<p>If the retry did not succeed (e.g., adding <code>RC_HALF - 1</code> retain counts overflowed), the runtime aborts this transaction by clearing the exclusive monitor, putting the retain counts back into the side table, and reloading the non-pointer <code>isa</code> before jumping back to the start of the compare-and-swap loop. It does, however, still hold the side table lock.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L865-L872">865-872</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">stored</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-c1">// Inline update failed. Put the retains back in the side table.</span>
<span class="tok-w">      </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-n">sidetable_addExtraRC_nolock</span><span class="tok-p">(</span><span class="tok-n">borrow</span><span class="tok-p">.</span><span class="tok-n">borrowed</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-n">oldisa</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">LoadExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-k">goto</span><span class="tok-w"> </span><span class="tok-n">retry</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If either of the store attempts succeeds, and the side table does not have any additional retain counts for the object instance, the runtime removes the entry for the object instance from the side table.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L874-L876">874-876</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-c1">// Decrement successful after borrowing from side table.</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">emptySideTable</span><span class="tok-p">)</span>
<span class="tok-w">      </span><span class="tok-n">sidetable_clearExtraRC_nolock</span><span class="tok-p">();</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Finally, if necessary, the runtime releases its side table lock and returns false to indicate the retain count did not reach zero (which is only used by the <code>&#95;objc_rootReleaseWasZero()</code> SPI). The retain count cannot reach zero on this path (see above), so the release operation ends here after a successful side table update.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L878-L882">878-882</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">didTransitionToDeallocating</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">sideTableLocked</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-n">sidetable_unlock</span><span class="tok-p">();</span>
<span class="tok-w">    </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Otherwise, execution continues to the deallocation logic.</p>
</div>
</div>
<div class="sect2">
<h3 id="deallocate">Deallocate</h3>
<div class="paragraph">
<p>If the retain count reaches zero (or underflows and the object instance is not storing retain counts in a side table), the runtime deallocates<sup class="footnote">[<a id="_footnoteref_5" class="footnote" href="#_footnotedef_5" title="View footnote.">5</a>]</sup> the object.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L888-L901">888-901</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-nl">deallocate</span><span class="tok-p">:</span>
<span class="tok-c1">// Really deallocate.</span>
<span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">isDeallocating</span><span class="tok-p">());</span>
<span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">isDeallocating</span><span class="tok-p">());</span>

<span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">sideTableLocked</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-n">sidetable_unlock</span><span class="tok-p">();</span>

<span class="tok-n">__c11_atomic_thread_fence</span><span class="tok-p">(</span><span class="tok-n">__ATOMIC_ACQUIRE</span><span class="tok-p">);</span>

<span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">performDealloc</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-p">((</span><span class="tok-kt">void</span><span class="tok-p">(</span><span class="tok-o">*</span><span class="tok-p">)(</span><span class="tok-n">objc_object</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-kt">SEL</span><span class="tok-p">))</span><span class="tok-n">objc_msgSend</span><span class="tok-p">)(</span><span class="tok-k">this</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">@selector</span><span class="tok-p">(</span><span class="tok-n">dealloc</span><span class="tok-p">));</span>
<span class="tok-p">}</span>
<span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>First, the runtime releases the side table lock, if necessary.</p>
</div>
<div class="paragraph">
<p>Next, it has an <code>acquire</code> fence. I presume this is a <a href="https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence#Atomic-fence_synchronization">atomic-fence synchronization</a>, but it&#8217;s not clear to me  what <code>release</code> operation this fence synchronizes with. The only potentially contentious read after the fence is of the <code>isa</code> in the message send a few lines down, but this thread just set the <code>isa</code>.</p>
</div>
<div class="paragraph">
<p>I would expect writes to the <code>isa</code> from another thread to be undefined behavior because the retain count is zero. Such a write, though, could change the class, which would be visible to this thread because of the fence. So, as far as I can tell, the fence&#8217;s only potential effect is on the message send in quite rare and bizarre circumstances.</p>
</div>
<div class="paragraph">
<p>The fence is probably an artifact from a previous implementation that is no longer relevant, a last minute change to "fix" a memory ordering problem just before a release, an unnecessary addition, or am I misunderstanding the behavior.</p>
</div>
<div class="paragraph">
<p>Then, finally, if the release didn&#8217;t occur through the <code>&#95;objc_rootReleaseWasZero()</code> SPI, a <code>-dealloc</code> message is sent to the object.</p>
</div>
<div class="paragraph">
<p>The release operation ends by returning <code>true</code>, indicating the retain count has reached zero, which is ignored by every caller except the <code>&#95;objc_rootReleaseWasZero()</code> SPI.</p>
</div>
</div>
</div>
</div>
<h1 id="epilogue" class="sect0">Epilogue</h1>
<div class="paragraph">
<p>When I decided to cover retain and release separately, I thought the release post would be significantly shorter than retain, but it&#8217;s 20% longer!</p>
</div>
<div class="paragraph">
<p>When the retain count overflows, the runtime keeps half of the count in the non-pointer <code>isa</code> and then adds half to the side table. It can quickly perform the critical write to the non-pointer <code>isa</code> and follow up with the expensive write to the side table. In contrast, when the retain count underflows, the runtime must take retain counts from the side table to gain the information necessary to perform the critical write to the non-pointer <code>isa</code>. The expensive read between the exclusive load and store increases the probability that the store may fail, which creates a unique corner case the release operation must handle. Writing is a great way to learn.</p>
</div>
<div class="paragraph">
<p>With that lesson learned, I have my fingers crossed that the next post on autorelease will be more straightforward!</p>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. The SPI returns <code>true</code> if the release results in a retain count of zero, enabling system frameworks implementing root classes to perform clean up work before deallocating the root class instance. Safari 10.1 (circa March 2017) <a href="https://github.com/WebKit/WebKit/blob/Safari-603.1.30/Source/WebKit2/Shared/Cocoa/WKObject.mm#L206-L213">added a use of the SPI</a>, though the change was later <a href="https://github.com/WebKit/WebKit/commit/4b5bca14adf3033809dd7a0a8a7ba634646cf7ea">reverted in Safari 11.1</a>.
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. The <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain#rootretain">rootRetain section</a> in the retain post briefly discusses the runtime&#8217;s <code>LoadExclusive</code> function.
</div>
<div class="footnote" id="_footnotedef_3">
<a href="#_footnoteref_3">3</a>. The <a href="https://alwaysprocessing.blog/2023/07/22/objc-retain#rootretain">rootRetain section</a> in the retain post has a detailed discussion on the message send logic and its use of Objective-C runtime functions.
</div>
<div class="footnote" id="_footnotedef_4">
<a href="#_footnoteref_4">4</a>. A future post will discuss the implementation of the retain count side table.
</div>
<div class="footnote" id="_footnotedef_5">
<a href="#_footnoteref_5">5</a>. A future post will discuss object deallocation in more detail.
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[Although release is "just" the logical inverse of retain, its implementation is much more complex, primarily due to the ARM synchronization model. This post explores the unique aspects of the release implementation (relative to retain), focusing on the memory ordering requirements on ARM.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/739048ac-8a94-451e-418a-6042a3a1ed00/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/739048ac-8a94-451e-418a-6042a3a1ed00/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Objective-C Internals: Retain</title><link href="https://alwaysprocessing.blog/2023/07/22/objc-retain" rel="alternate" type="text/html" title="Objective-C Internals: Retain" /><published>2023-07-22T00:00:00+00:00</published><updated>2023-07-22T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/07/22/objc-retain</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/07/22/objc-retain"><![CDATA[<div class="sect1">
<h2 id="background">Background</h2>
<div class="sectionbody">
<div class="paragraph">
<p>OS X 10.7 and iOS 5 introduced <a href="https://clang.llvm.org/docs/AutomaticReferenceCounting.html">Automatic Reference Counting</a>, or ARC, to improve Objective-C programmer productivity by eliminating boilerplate code and reducing the surface area for reference counting bugs (leaks and over-releases).</p>
</div>
<div class="paragraph">
<p>Before ARC, the <code>-[NSObject retain]</code>, <code>-[NSObject release]</code><sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>, and <code>-[NSObject autorelease]</code><sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup> methods were the exclusive interface to manage object reference counts. And, until OS X 10.8 and iOS 6, the <code>NSObject</code> implementation was part of Foundation, not the Objective-C runtime.</p>
</div>
<div class="paragraph">
<p>The designers of ARC identified a key requirement to improve the likelihood of the feature&#8217;s success, learning from Apple&#8217;s ill-fated attempt to add garbage collection to Objective-C: Automatic Reference Counting must transparently interoperate with manual reference counting in the same process without requiring recompilation of existing code (e.g., a third-party binary-only library).</p>
</div>
<div class="paragraph">
<p>In the early days of macOS, it wasn&#8217;t unheard of for some objects to override the reference counting methods<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup> to use their own implementation, often for performance reasons. ARC had to support transparent interoperability with these custom reference counting implementations to deliver on the aforementioned requirement.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="entry-points">Entry Points</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There are two interfaces for reference counting operations: the long-standing <code>NSObject</code> API and a compiler-private API used by ARC, both of which call into a core implementation. The following two subsections will examine each interface&#8217;s retain implementation, and the next section will discuss the core implementation.</p>
</div>
<div class="sect2">
<h3 id="nsobject">NSObject</h3>
<div class="paragraph">
<p>The <code>-[NSObject retain]</code> implementation<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup> is trivial&mdash;it simply calls <code>_objc_rootRetain</code> to retain <code>self</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/NSObject.mm</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L2502-L2504">2502-2504</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-p">-</span> <span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-nf">retain</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">_objc_rootRetain</span><span class="tok-p">(</span><span class="tok-nb">self</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The term <em>root</em> indicates the root class in the object&#8217;s <a href="https://alwaysprocessing.blog/2023/01/02/objc-class-arch#architecture-diagram">class hierarchy</a> received the <code>-retain</code> message. Therefore, the class does not override <code>-retain</code> or the override calls the superclass method, so the retain operation is guaranteed to use the runtime&#8217;s implementation. (As we&#8217;ll see in the following section, not all entry points have this guarantee.)</p>
</div>
<div class="paragraph">
<p>Next, the <code>_objc_rootRetain</code> function, which is also trivial, calls <code>objc_object::rootRetain()</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/NSObject.mm</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L1875-L1881">1875-1881</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-nf">_objc_rootRetain</span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">obj</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-n">obj</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">obj</span><span class="tok-o">-&gt;</span><span class="tok-n">rootRetain</span><span class="tok-p">();</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>The existence of this function is a historical artifact. In the first implementation of ARC, this function was <em>the</em> <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-493.9/runtime/objc-arr.mm#L810-L826">retain implementation</a>, but it persisted through various refactors in subsequent releases that made it unnecessary. The only other caller of this function is the <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/Object.mm#L50-L53">legacy <code>Object</code> class</a>, which is implemented in Objective-C++, so it could call <code>objc_object::rootRetain()</code> directly.</p>
</div>
</div>
</div>
<div class="paragraph">
<p>Finally, <code>objc_object::rootRetain()</code> calls an overload of <code>rootRetain</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L607-L611">607-611</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-nf">objc_object::rootRetain</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">rootRetain</span><span class="tok-p">(</span><span class="tok-nb">false</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Fast</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The overload called here is the core implementation, which has two parameters:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>tryRetain</code> enables support to load weak references<sup class="footnote">[<a id="_footnoteref_5" class="footnote" href="#_footnotedef_5" title="View footnote.">5</a>]</sup>. The argument is <code>false</code> because a weak reference cannot exercise this code path. (The runtime must first load an object from a weak reference before the object can receive a message, and, by definition, the object reference obtained through the load operation is strong.)</p>
</li>
<li>
<p><code>variant</code> provides context about the call path, enabling the core implementation to elide unnecessary work. Retains performed through <code>NSObject</code> use <code>RRVariant::Fast</code> to skip the check for whether the class has a custom reference counting implementation because performing the operation through the <em>root</em> class is, by definition, not custom.</p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="automatic-reference-counting">Automatic Reference Counting</h3>
<div class="paragraph">
<p>When ARC is enabled, the compiler performs reference counting operations through a compiler-private API added for ARC as a performance optimization. The API allows reference counting operations to call directly into the Objective-C runtime and skip the overhead of sending a message.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/NSObject.mm</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L1772-L1777">1772-1777</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-nf">objc_retain</span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">obj</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">_objc_isTaggedPointerOrNil</span><span class="tok-p">(</span><span class="tok-n">obj</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">obj</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">obj</span><span class="tok-o">-&gt;</span><span class="tok-k">retain</span><span class="tok-p">();</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The function first checks the object pointer value and returns immediately if it does not reference an object on the heap, which may occur in two cases:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>The pointer <code>nil</code>. Sending a message to <code>nil</code> is legal, so this optimization of <code>-[NSObject retain]</code> must also support <code>nil</code> pointers.</p>
</li>
<li>
<p>The pointer is a <a href="https://alwaysprocessing.blog/2023/03/19/objc-tagged-ptr">tagged pointer</a>. A tagged pointer is an implementation detail of the Objective-C runtime not visible to the compiler, so the compiler can not eliminate the retain operation. Tagged pointers do not participate in reference counting (there is no heap allocation to track), so there&#8217;s no need to proceed.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>If the object pointer value references an object on the heap, the function calls <code>objc_object::retain()</code> to perform the retain operation.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L589-L596">589-596</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kr">inline</span><span class="tok-w"> </span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-nf">objc_object::retain</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">isTaggedPointer</span><span class="tok-p">());</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">rootRetain</span><span class="tok-p">(</span><span class="tok-nb">false</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">FastOrMsgSend</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This function calls the core implementation (though <em>root</em> in <code>rootRetain</code> is a misnomer at this point) with:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>false</code> for <code>tryRetain</code>, for the same reason discussed above in the <a href="#nsobject">NSObject</a> entry point.</p>
</li>
<li>
<p><code>RRVariant::FastOrMsgSend</code> for <code>variant</code>. No introspection, whether direct (see <a href="#rootretain">rootRetain</a> below) or indirect (via a message send, see <a href="#nsobject">NSObject</a> above), has occurred, so it&#8217;s not yet known whether the object&#8217;s class overrides any of the reference counting methods (hence the function&#8217;s name does <strong>not</strong> contain the term <em>root</em>).</p>
<div class="paragraph">
<p>The <code>MsgSend</code> part of the <code>variant</code> instructs the core implementation to do the introspection necessary to determine whether the object&#8217;s class overrides the reference counting methods. If it does, the core implementation performs the retain operation by sending the object a <code>-retain</code> message (which may re-enter the runtime via <a href="#nsobject"><code>-&#91;NSObject retain&#93;</code></a>).</p>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="rootretain">rootRetain</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L619-L706"><code>objc_object::rootRetain(bool, RRVariant)</code></a> function is on the larger side, so we&#8217;ll analyze it piece by piece.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> line <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L622">622</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">isTaggedPointer</span><span class="tok-p">()))</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Although the ARC entry point checks for a tagged pointer, the <code>NSObject</code> entry point does not. It&#8217;s not immediately apparent to me why the <code>NSObject</code> implementation doesn&#8217;t perform this check, but it has to happen somewhere, and in this version of the runtime, it&#8217;s here.</p>
</div>
<div class="paragraph">
<p>Next, the runtime loads the object&#8217;s <code>isa</code> value.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L624-L630">624-630</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">sideTableLocked</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">transcribeToSideTable</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-n">isa_t</span><span class="tok-w"> </span><span class="tok-n">oldisa</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">LoadExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-n">isa_t</span><span class="tok-w"> </span><span class="tok-n">newisa</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>isa</code> stores the object&#8217;s <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#extra_rc">retain count</a> on all modern Apple platforms. The Objective-C runtime uses ARM&#8217;s <a href="https://developer.arm.com/documentation/dht0008/a/arm-synchronization-primitives/exclusive-accesses/exclusive-monitors">exclusive monitor</a> synchronization primitive to manage concurrency on the <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-os.h#L180-L215"><code>arm64</code> architecture</a>, which is where the <code>LoadExclusive</code> function gets its name. On <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-os.h#L219-L245">all other architectures</a>, including <code>arm64e</code>, the Objective-C runtime uses <a href="https://en.cppreference.com/w/c/atomic">C11 atomics</a>. (I&#8217;m unsure whether <code>arm64</code> or <code>arm64e</code> is the outlier case here or why.)</p>
</div>
<div class="paragraph">
<p>If the compiler-private API was the entry point for the retain operation, the runtime must check whether the class overrides any of the reference counting methods.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L632-L642">632-642</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">variant</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">FastOrMsgSend</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-c1">// These checks are only meaningful for objc_retain()</span>
<span class="tok-w">  </span><span class="tok-c1">// They are here so that we avoid a re-load of the isa.</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">getDecodedClass</span><span class="tok-p">(</span><span class="tok-nb">false</span><span class="tok-p">)</span><span class="tok-o">-&gt;</span><span class="tok-n">hasCustomRR</span><span class="tok-p">()))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">getDecodedClass</span><span class="tok-p">(</span><span class="tok-nb">false</span><span class="tok-p">)</span><span class="tok-o">-&gt;</span><span class="tok-n">canCallSwiftRR</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">swiftRetain</span><span class="tok-p">.</span><span class="tok-n">load</span><span class="tok-p">(</span><span class="tok-n">memory_order_relaxed</span><span class="tok-p">)((</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">((</span><span class="tok-kt">id</span><span class="tok-p">(</span><span class="tok-o">*</span><span class="tok-p">)(</span><span class="tok-n">objc_object</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-kt">SEL</span><span class="tok-p">))</span><span class="tok-n">objc_msgSend</span><span class="tok-p">)(</span><span class="tok-k">this</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">@selector</span><span class="tok-p">(</span><span class="tok-k">retain</span><span class="tok-p">));</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Custom referencing counting implementations are rare, so the runtime uses its <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-os.h#L163"><code>slowpath()</code></a> macro to hint to the CPU&#8217;s branch prediction unit this path is unlikely to run. <code>getDecodedClass()</code> returns the object&#8217;s <a href="https://alwaysprocessing.blog/2023/01/10/objc-class-graph-impl"><code>Class</code> object</a>, which has a <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-runtime-new.h#L1937-L1939">flag</a> indicating whether the class overrides any reference counting methods. This quick check provides the necessary class introspection for the ARC entry point to support custom reference counting implementations with minimal overhead.</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>The term <em>decoded</em> in <code>getDecodedClass()</code> likely refers to extracting the <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#shiftcls-and-shiftcls_and_sig">class object pointer</a> from the non-pointer <code>isa</code>. The specifics of this function depend on the target architecture:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>arm64_32</code>: The Apple Watch ABI uses 32-bit pointers, so its non-pointer <code>isa</code> stores an <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#indexcls">index</a> into a table with the class object (there aren&#8217;t enough bits to store extra data with a pointer value).</p>
<div class="ulist">
<ul>
<li>
<p>If the <code>isa</code> is not a pointer, the function calls <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L258-L261"><code>classForIndex()</code></a> to get the class object from the table.</p>
</li>
<li>
<p>Otherwise, if the <code>isa</code> is a pointer, it&#8217;s a pointer to the class object, so the function returns the <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L261">isa</a> bits as-is.</p>
</li>
</ul>
</div>
</li>
<li>
<p>On all other target architectures, the function is an alias for <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L263"><code>getClass()</code></a>, which returns the class object by masking out the non-pointer bits from the <code>isa</code> value.</p>
<div class="ulist">
<ul>
<li>
<p><code>arm64e</code>: If <code>isa</code> <a href="https://developer.apple.com/documentation/security/preparing_your_app_to_work_with_pointer_authentication">pointer authentication</a> is enabled, the function extracts the class object pointer value using a mask <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-runtime-new.mm#L160-L173">computed by the compiler</a>. However, the function <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L239-L240">skips authentication</a> because the caller passes <code>false</code> for <code>authenticate</code>. The documented rationale for not authenticating is that authentication occurs as part <code>objc_msgSend</code>, so additional authentications aren&#8217;t needed.</p>
</li>
<li>
<p>Otherwise, the function extracts the class object pointer value using a <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L248">static mask</a> definition.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="paragraph">
<p>If a class has a custom reference counting implementation, the runtime sends the object a <code>-retain</code> message to fulfill the ARC-initiated retain operation. Note the object may then call <code>-[NSObject retain]</code>, but this code block will not execute again as the <code>variant</code> will be <code>RRVariant::Fast</code>.</p>
</div>
<div class="paragraph">
<p>Pure Swift classes (i.e., classes not derived from <code>NSObject</code>) derive from the <a href="https://github.com/apple/swift/blob/swift-5.8.1-RELEASE/stdlib/public/runtime/SwiftObject.h#L41-L78"><code>SwiftObject</code> class</a> (only on Apple platforms) for Objective-C compatibility. Swift uses its own reference counting system, so <code>SwiftObject</code> <a href="https://github.com/apple/swift/blob/swift-5.8.1-RELEASE/stdlib/public/runtime/SwiftObject.mm#L270">implements</a> the <a href="https://github.com/apple/swift/blob/e495eed8914a87aa3403411fbc2cf3f9e6119846/include/swift/Runtime/HeapObject.h#L1108-L1146">reference counting methods</a> to support bridging pure Swift objects to Objective-C. As an optimization for this case, the Objective-C runtime calls the Swift runtime&#8217;s <code>swift_retain()</code> function directly<sup class="footnote">[<a id="_footnoteref_6" class="footnote" href="#_footnotedef_6" title="View footnote.">6</a>]</sup> (vs. retaining the object via a message send).</p>
</div>
<div class="paragraph">
<p>Continuing to the next block.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L644-L651">644-651</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">nonpointer</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-c1">// a Class is a Class forever, so we can perform this check once</span>
<span class="tok-w">  </span><span class="tok-c1">// outside of the CAS loop</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">getDecodedClass</span><span class="tok-p">(</span><span class="tok-nb">false</span><span class="tok-p">)</span><span class="tok-o">-&gt;</span><span class="tok-n">isMetaClass</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><code>Class</code> objects are never deallocated and do not require reference counting. So, if the object is a class object, the function returns it without performing any further work.</p>
</div>
<div class="sect2">
<h3 id="compare-and-swap-loop">Compare and Swap Loop</h3>
<div class="paragraph">
<p>The <a href="https://en.wikipedia.org/wiki/Compare-and-swap">compare-and-swap</a> loop is the heart of the retain implementation. It starts by (re-)initializing the loop&#8217;s start state.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L654-L655">654-655</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-k">do</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">transcribeToSideTable</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-n">newisa</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">oldisa</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>It sets <code>newisa</code> to the current <code>isa</code> value (i.e., <code>oldisa</code>), which the loop will update to reflect the incremented retain count. The following subsection will look at the use of <code>transcribeToSideTable</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L656-L660">656-660</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">nonpointer</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">tryRetain</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">sidetable_tryRetain</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-o">?</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-w"> </span><span class="tok-o">:</span><span class="tok-w"> </span><span class="tok-nb">nil</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">sidetable_retain</span><span class="tok-p">(</span><span class="tok-n">sideTableLocked</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>First, the loop checks if the object instance has a <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#non-pointer-isa">non-pointer <code>isa</code></a>. If it does not, the retain count is recorded in a side table<sup class="footnote">[<a id="_footnoteref_7" class="footnote" href="#_footnotedef_7" title="View footnote.">7</a>]</sup>. This check is performed in the loop because if this thread loses a compare-and-swap, it could be due to another thread mutating the object in a way that removed its use of a non-pointer <code>isa</code>.</p>
</div>
<div class="paragraph">
<p>Next, the loop checks to see if it lost another race.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L661-L673">661-673</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-c1">// don&#39;t check newisa.fast_rr; we already called any RR overrides</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">isDeallocating</span><span class="tok-p">()))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">sideTableLocked</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-n">ASSERT</span><span class="tok-p">(</span><span class="tok-n">variant</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Full</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-n">sidetable_unlock</span><span class="tok-p">();</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">tryRetain</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">nil</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>An object may be deallocating while a thread is attempting to retain it in (at least) three scenarios:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>tryRetain</code> is <code>true</code>, and this thread lost the race to load the weak object before it started deallocation. The function returns <code>nil</code>, indicating it could not obtain a strong reference. In this scenario, the caller, <code>objc_loadWeakRetained()</code>, holds a lock to the weak reference side table, preventing the object from being freed, so the read of the <code>isa</code> from the object pointer is defined behavior.</p>
</li>
<li>
<p>Another thread released the object, causing it to deallocate, usually due to a race condition that occurs when a process concurrently reads from and writes to a <code>strong</code>, <code>nonatomic</code> property. Everything about this scenario is undefined behavior. The function returns <code>self</code> to fulfill the <code>-retain</code> contract, but it will become a <a href="https://en.wikipedia.org/wiki/Dangling_pointer">dangling pointer</a>, almost certainly resulting in a crash in the thread&#8217;s near future. Hitting this code path in a race condition is "lucky." In practice, the <code>isa</code> bits read through the dangling pointer could have sent this function in any number of directions resulting in unpredictable effects.</p>
</li>
<li>
<p>Logic in <code>-dealloc</code> causes a retain to be performed (e.g., the <code>-dealloc</code> implementation passes <code>self</code> to a clean-up routine where the ARC compiler emits a retain/release pair). This scenario is <strong>not</strong> a race condition like the two cases above because the retain occurred on the same thread executing the deallocation. However, this scenario can lead to undefined behavior if the function performing the retain requires the object instance to survive its call scope (e.g., storing <code>self</code> in a <code>strong</code> property of another object) because the pointer will become danging when deallocation completes.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Finally, we get to the actual increment.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L674-L675">674-675</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-kt">uintptr_t</span><span class="tok-w"> </span><span class="tok-n">carry</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">addc</span><span class="tok-p">(</span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RC_ONE</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">carry</span><span class="tok-p">);</span><span class="tok-w">  </span><span class="tok-c1">// extra_rc++</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Recall the non-pointer <code>isa</code> is a bit field with <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#non-pointer-isa-variants">three variants</a>. The value of <code>RC_ONE</code> is the bit that represents a retain count of one when viewing the bit field as an integer. The retain count is stored in the most significant bits of the <code>isa</code>, so an overflow, or carry, will occur if all of the retain count bits are in use (discussed in the following subsection). <code>newisa</code> contains the incremented retain count if no overflow occurs and is ready to be written back to the object instance.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> line <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L691">691</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">while</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">StoreExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">oldisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">bits</span><span class="tok-p">)));</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If the value at <code>&amp;isa()</code> matches the value at <code>&amp;oldisa</code>, the compare-and-swap operation succeeds and writes the value of <code>newisa</code> to <code>&amp;isa()</code>, and the loop ends.</p>
</div>
<div class="paragraph">
<p>Otherwise, the value of <code>&amp;isa()</code> has changed since this thread loaded it into <code>oldisa</code>. The compare-and-swap operation fails and writes the new value at <code>&amp;isa()</code> to <code>&amp;oldisa</code>. The loop continues until the thread wins a compare-and-swap operation or another thread changes the object state to activate one of the above return paths.</p>
</div>
</div>
<div class="sect2">
<h3 id="the-full-variant">The Full Variant</h3>
<div class="paragraph">
<p>If the retain count overflows the bits in the non-pointer <code>isa</code>, the runtime will use a side table to store part of the retain count.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L677-L690">677-690</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">carry</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-c1">// newisa.extra_rc++ overflowed</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">variant</span><span class="tok-w"> </span><span class="tok-o">!=</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Full</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-n">ClearExclusive</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">isa</span><span class="tok-p">().</span><span class="tok-n">bits</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">rootRetain_overflow</span><span class="tok-p">(</span><span class="tok-n">tryRetain</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-c1">// Leave half of the retain counts inline and</span>
<span class="tok-w">    </span><span class="tok-c1">// prepare to copy the other half to the side table.</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">tryRetain</span><span class="tok-w"> </span><span class="tok-o">&amp;&amp;</span><span class="tok-w"> </span><span class="tok-o">!</span><span class="tok-n">sideTableLocked</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-n">sidetable_lock</span><span class="tok-p">();</span>
<span class="tok-w">    </span><span class="tok-n">sideTableLocked</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-n">transcribeToSideTable</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">extra_rc</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">RC_HALF</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-n">newisa</span><span class="tok-p">.</span><span class="tok-n">has_sidetable_rc</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>If this function invocation uses the <code>Fast</code> or <code>FastOrMsgSend</code> variant, it stops its attempt of the retain operation and passes the buck to <code>rootRetain_overflow()</code>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L1372-L1376">1372-1376</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-n">NEVER_INLINE</span><span class="tok-w"> </span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">objc_object</span><span class="tok-o">::</span><span class="tok-n">rootRetain_overflow</span><span class="tok-p">(</span><span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">tryRetain</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">rootRetain</span><span class="tok-p">(</span><span class="tok-n">tryRetain</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Full</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>I assume the purpose of this function is to provide a frame in stack traces to help Apple engineers troubleshoot retain crashes in the runtime, as the interplay of side table locking (which uses a non-reentrant spin lock) can be challenging to reason about.</p>
</div>
<div class="paragraph">
<p>If the retain count overflows in <code>rootRetain</code> with the <code>Full</code> variant, the implementation sends half of the retain count value to a side table and keeps the other half in the non-pointer <code>isa</code>. The retain count is divided in half to minimize the number of side table accesses (requiring fewer CPU instructions and fewer lock acquisitions). If the implementation sent only the overflow bits to the side table, reference counting operations at the overflow boundary value could become a performance drag on the system.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L693-L700">693-700</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">variant</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-n">RRVariant</span><span class="tok-o">::</span><span class="tok-n">Full</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">transcribeToSideTable</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-c1">// Copy the other half of the retain counts to the side table.</span>
<span class="tok-w">      </span><span class="tok-n">sidetable_addExtraRC_nolock</span><span class="tok-p">(</span><span class="tok-n">RC_HALF</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">tryRetain</span><span class="tok-w"> </span><span class="tok-o">&amp;&amp;</span><span class="tok-w"> </span><span class="tok-n">sideTableLocked</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-n">sidetable_unlock</span><span class="tok-p">();</span>
<span class="tok-w">  </span><span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The side table is updated after the compare-and-swap succeeds because another thread may win the race in moving the overflow retain count to the side table. The compare-and-swap loop acquires a lock to the side table, which prevents a race condition if another thread also attempts to read from or write to the side table.</p>
</div>
</div>
<div class="sect2">
<h3 id="return-self">Return self</h3>
<div class="paragraph">
<p>After the compare-and-swap succeeds and, if necessary, the side table is updated, the retain operation is complete.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-object.h</code> line <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-object.h#L693-L700">693-700</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c++"><span></span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The last step is to return <code>self</code><sup class="footnote">[<a id="_footnoteref_8" class="footnote" href="#_footnotedef_8" title="View footnote.">8</a>]</sup> to fulfill the <a href="https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571946-retain"><code>-[NSObject retain</a></code> contract]:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>As a convenience, <code>retain</code> returns <code>self</code> because it may be used in nested expressions.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>Retain has evolved into a highly optimized operation since the first release of Mac OS X over 20 years ago. The same is true for release, autorelease, and dealloc, which we&#8217;ll see soon.</p>
</div>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. This post is long, so I decided to discuss release in the <a href="https://alwaysprocessing.blog/2023/10/01/objc-release">next post</a>.
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. Autorelease is a purely additive feature built on release. I&#8217;ll discuss its implementation in a future post, given it&#8217;s not part of the core retain/release operations.
</div>
<div class="footnote" id="_footnotedef_3">
<a href="#_footnoteref_3">3</a>. The Objective-C runtime considers the <code>retain</code>, <code>release</code>, <code>autorelease</code>, <code>_tryRetain</code>, <code>_isDeallocating</code>, <code>retainCount</code>, <code>allowsWeakReference</code>, and <code>retainWeakReference</code> selectors to be part of the <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-runtime-new.mm#L1073-L1080">reference counting method family</a>. If a class overrides any of these methods, the Objective-C runtime classifies the class as having a custom reference counting implementation.
</div>
<div class="footnote" id="_footnotedef_4">
<a href="#_footnoteref_4">4</a>. The implementation of <code>NSObject</code> moved into the Objective-C runtime in OS X 10.8 and iOS 6 to support the introduction of <code>os_object</code>, which enabled GCD and XPC types to participate in ARC and to work with Foundation collections. Before this move, its implementation in Foundation was likely the same.
</div>
<div class="footnote" id="_footnotedef_5">
<a href="#_footnoteref_5">5</a>. Exploring the implementation of weak references is on the backlog of upcoming posts.
</div>
<div class="footnote" id="_footnotedef_6">
<a href="#_footnoteref_6">6</a>. The Swift runtime depends on the Objective-C runtime, creating a reverse dependency. The Objective-C runtime resolves this by <a href="https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/NSObject.mm#L95-L119">dynamically loading</a> the <code>swift_retain</code> symbol from <code>libswiftCore.dylib</code>.
</div>
<div class="footnote" id="_footnotedef_7">
<a href="#_footnoteref_7">7</a>. A future post will discuss the implementation of the retain count side table.
</div>
<div class="footnote" id="_footnotedef_8">
<a href="#_footnoteref_8">8</a>. Apple started using Objective-C++ to implement the Objective-C runtime in OS X 10.7 and iOS 5, but it wasn’t until OS X 10.9 and iOS 7 that Apple used Objective-C++ to implement the object type itself. A future post will explore this approach and show how <code>this</code> and <code>self</code> are the same.
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[Objective-C memory is managed through a reference counting scheme, which has evolved from a relatively simple API into a sophisticated, highly-optimized implementation while maintaining source and ABI compatibility.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/c7eb8f6e-a4d2-4d14-9dec-278d8e82e500/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/c7eb8f6e-a4d2-4d14-9dec-278d8e82e500/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Home Lab: The Accidental System</title><link href="https://alwaysprocessing.blog/2023/06/11/hl-accidental-system" rel="alternate" type="text/html" title="Home Lab: The Accidental System" /><published>2023-06-11T00:00:00+00:00</published><updated>2023-06-11T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/06/11/hl-accidental-system</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/06/11/hl-accidental-system"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Over the years, I accumulated various systems, devices, and services in responding to an immediate need. However, I didn&#8217;t have a holistic, coherent plan, and the responsive, ad hoc approach created unnecessary churn, especially for the WiFi hardware. In the following sections, I&#8217;ll describe each notable investment and its motivating event, organized chronologically, to show how I accidentally stumbled into building a <a href="https://www.reddit.com/r/homelab/wiki/introduction/">home lab</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="evolution-of-the-home-lab">Evolution of the Home Lab</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="network-version-1">Network, Version 1</h3>
<div class="paragraph">
<p>I moved into a new townhouse in September 2014 and brought the only infra I had at the time: an AirPort Extreme<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>. I installed it in the <a href="https://www.legrand.us/audio-visual/structured-wiring-enclosures/metal-enclosures/28-inch-enclosure-with-hinged-door/p/en2850">wiring enclosure</a> on the third floor, which kept it and the cable modem out of sight. However, the install location (at the end of both the horizontal and vertical footprints of the home) and placement (in a metal box) are both sub-optimal choices for whole-house coverage, so I added a wired <a href="https://support.apple.com/kb/SP651?locale=en_US">AirPort Express 802.11n (2nd Generation)</a> access point on the first floor.</p>
</div>
<div class="paragraph">
<p>I didn&#8217;t have many leaf nodes in my network topology at the time (a phone, a laptop, a work laptop, an Apple TV, and probably a few others), so this setup worked well enough for a few years. (I don&#8217;t consider the leaf nodes part of the home lab, so I&#8217;ll keep any mention brief to stay on point.)</p>
</div>
</div>
<div class="sect2">
<h3 id="network-version-1-1">Network, Version 1.1</h3>
<div class="paragraph">
<p>I had gained a few new housemates by the summer of 2017, and the questionable WiFi system needed to be improved. However, I didn&#8217;t understand the problem, nor was I familiar with contemporary solutions, so I replaced the AirPort Extreme with an <a href="https://support.apple.com/kb/SP680?locale=en_US">AirPort Extreme 802.11ac</a>, which somewhat improved WiFi coverage. We also connected a few devices via Ethernet to mitigate some unresolved issues, bringing overall home network connectivity to "good enough."</p>
</div>
</div>
<div class="sect2">
<h3 id="nas-version-1">NAS, Version 1</h3>
<div class="paragraph">
<p>With more people living in the home and less space to store things, I looked at what I could discard. I had too many hard drives, so I decided to buy a <a href="https://en.wikipedia.org/wiki/Network-attached_storage">network-attached storage (NAS)</a> device to:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Consolidate into a single device.</strong> I had 4 USB 2.0 drives, 1 FireWire 400/800 drive, 2 USB 3.0 drives, and 1 Thunderbolt 2 drive. Apple went all-in on USB-C/Thunderbolt 3 in 2016, so the clock was ticking on my ability to connect some of the drive enclosures. I was particularly concerned about the FireWire enclosure, which not only had an obsolete connector but also had two disks in a <a href="https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_0">RAID 0</a> configuration <em>with my most precious data</em>, and I was not confident this would be readable by another enclosure if that were to become necessary<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>.</p>
</li>
<li>
<p><strong>Improve hardware failure resiliency.</strong> I&#8217;ve had two hard drives fail while in use. One did not have any backups, and that was very painful. I thought I was safe for a while as the drive was new, but it failed within three months of purchase. The other drive failure was a backup drive, which I replaced without additional trouble.</p>
</li>
<li>
<p><strong>Make the data accessible.</strong> I didn&#8217;t have a desk at home, so mounting a drive required putting it somewhere stable (all the drives were spinning disks), finding the correct power adapter and data cable, and plugging everything in. While workable for occasional use, a network mount is far easier to attach and usable anywhere in the home.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>At the time, I thought of network-attached storage as <em>just</em> storage available via a network mount, so I wondered why so many NAS devices had a wide range of processor and RAM specs. Instead of taking time to resolve that confusion, I just bought the lowest spec&#8217;d device I could find with good reviews, the <a href="https://global.synologydownload.com/download/Document/Hardware/DataSheet/DiskStation/16-year/DS216+II/enu/Synology_DS216_PlusII_Data_Sheet_enu.pdf">Synology DS216+II</a>, and installed two <a href="https://documents.westerndigital.com/content/dam/doc-library/en_us/assets/public/western-digital/product/hgst/deskstar-nas/data-sheet-deskstar-nas-internal-drive-kit.pdf">10 TB 7200 RPM Deskstar NAS hard drives</a>, the largest supported capacity, in a <a href="https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_1">RAID 1</a> configuration.</p>
</div>
<div class="paragraph">
<p>The Synology gained more responsibilities over time (which it handled well despite being so woefully under-spec&#8217;d), and the hardware is still running strong.</p>
</div>
</div>
<div class="sect2">
<h3 id="network-version-2">Network, Version 2</h3>
<div class="paragraph">
<p>In the summer of 2018, we tried to set up a projector with an Apple TV on the rooftop deck, but the WiFi coverage needed improvement, and there was no Ethernet wiring nearby. Apple had officially discontinued its AirPort product line, so I decided to replace the AirPort products with a new system. I installed <a href="https://www.amazon.com/dp/B07DPNSW6P/">eero Pros (2nd Generation)</a> where Ethernet was available and added an <a href="https://www.amazon.com/dp/B07DPND6BB/">eero Beacon</a> next to the rooftop deck door. Finally, WiFi coverage was excellent throughout the house and rooftop deck!</p>
</div>
</div>
<div class="sect2">
<h3 id="network-version-2-1">Network, Version 2.1</h3>
<div class="paragraph">
<p>A few months into working from home in the summer of 2020, I was fortunate enough to have <a href="https://www.centurylink.com/internet/">CenturyLink Fiber Internet</a> installed the first day it was available. A 940 Mbps symmetrical connection was a massive improvement over the 50/10 Mbps Xfinity Cable Internet and $20/month less expensive!</p>
</div>
<div class="paragraph">
<p>I had previously moved to a new house and its modem/router setup differed from the townhouse: a media cabinet housed the cable modem and an eero Pro, and the eero Pro had an Ethernet connection to the wiring enclosure with a switch to connect the other access points in the house.</p>
</div>
<div class="paragraph">
<p>The Fiber Internet connection ran into the wiring cabinet from the exterior connection point, necessitating a new router placement. I thought about getting an additional eero Pro for the wiring cabinet but didn&#8217;t want to spend that much money on a router when CenturyLink provided an <a href="https://www.centurylink.com/home/help/internet/modems-and-routers/actiontec-c3000a.html">Actiontec C3000A</a> for free. And I wanted to avoid installing a WiFi access point in a metal box again, especially since it was close to access points in more ideal positions.</p>
</div>
<div class="paragraph">
<p>So, I went with the path of least resistance: I used the Actiontec C3000A as the router in the wiring cabinet and reconfigured the eero Pro in the media cabinet to be just an access point.</p>
</div>
</div>
<div class="sect2">
<h3 id="mac-hosts-version-1">Mac Hosts, Version 1</h3>
<div class="paragraph">
<p>Apple announced its transition from Intel CPUs to Apple Silicon in the summer of 2020. I picked up a <a href="https://support.apple.com/kb/SP782?locale=en_US">Mac mini (2018)</a> to ensure I had an Intel Mac for testing purposes that would last through the tail end of the Intel Mac install base. And I also got a <a href="https://support.apple.com/kb/SP823?locale=en_US">Mac mini (M1, 2020)</a> to have an entry-level, first-generation Apple Silicon Mac for testing.</p>
</div>
</div>
<div class="sect2">
<h3 id="nas-version-1-1">NAS, Version 1.1</h3>
<div class="paragraph">
<p>In late 2020, I added two more services to the Synology, so it was no longer <em>just</em> network-attached storage.</p>
</div>
<div class="paragraph">
<p>We use HomeKit a lot but had a few devices that were not HomeKit compatible. After learning about <a href="https://www.synology.com/en-us/dsm/feature/docker">Synology&#8217;s Docker support</a>, I installed a <a href="https://homebridge.io/">Homebridge</a> container and had the non-compatible devices working with HomeKit in an evening.</p>
</div>
<div class="paragraph">
<p>Before moving to Seattle, I had digitized many years of home video. Now with sufficient uplink bandwidth, and after recovering the files from the disks in the FireWire enclosure, I wanted to make the footage easily watchable by family (and at home). I experimented with <a href="https://www.synology.com/en-global/dsm/feature/video_station">Synology Video Station</a>, which worked, but the experience was clunky. So, I tried <a href="https://www.plex.tv/">Plex</a>. While it was more work to set up, it was more accessible to everyone, which is why I chose it for serving video from the Synology.</p>
</div>
</div>
<div class="sect2">
<h3 id="lab-version-1">Lab, Version 1</h3>
<div class="paragraph">
<p>I had a hard time finding a good location for the Synology. It required Ethernet connectivity but also needed protection from accidental impact. The only location meeting those requirements was the media cabinet for the TV. But, this wasn&#8217;t an ideal location because the Synology created background noise, affecting the use of the room and watching TV.</p>
</div>
<div class="paragraph">
<p>After some consideration, I purchased a <a href="https://www.amazon.com/dp/B08P25PBSR/">wall mount rack</a> to install in the mechanical room. I chose a wall mount to protect the electronics from flooding by keeping them away from the floor and chose a rack for additional protection from accidental impact (vs., e.g., a shelf). The mechanical room doesn&#8217;t have Ethernet, but I was able to run a line from a nearby port in the adjacent room.</p>
</div>
<div class="paragraph">
<p>I would not recommend this approach. The rack transferred the noise from the Synology into the wall into the adjacent room (though, in this case, it was less disruptive). And the rack I chose does not have sufficient depth to support some rack mount servers.</p>
</div>
</div>
<div class="sect2">
<h3 id="nas-version-1-2">NAS, Version 1.2</h3>
<div class="paragraph">
<p>I almost omitted this section because it&#8217;s embarrassing. I did not have a regular backup of my primary computer and didn&#8217;t enable <a href="https://kb.synology.com/en-us/DSM/tutorial/How_to_back_up_files_from_Mac_to_Synology_NAS_with_Time_Machine#x_anchor_id4">Time Machine</a> support on the Synology until September 2021, nearly four years after I purchased it 🤦‍♂️.</p>
</div>
</div>
<div class="sect2">
<h3 id="lab-version-1-1">Lab, Version 1.1</h3>
<div class="paragraph">
<p>When we have high winds, heavy precipitation, or receive a lot of snow, we sometimes experience small power fluctuations or outages. In the late fall and early winter of 2021, we had many such events, and I was worried this might damage or corrupt the disks in the Synology. I purchased a <a href="https://www.amazon.com/dp/B000FBK3QK/">CyberPower 1500VA/900W Uninterruptible Power Supply</a> (UPS) to ensure future fluctuations would not affect the Synology, and to ensure the Synology would have time to shut down during an outage safely.</p>
</div>
</div>
<div class="sect2">
<h3 id="network-version-2-2">Network, Version 2.2</h3>
<div class="paragraph">
<p>By early 2022, I was experiencing a high disruption rate in my work VPN connection. I don&#8217;t know if the Actiontec C3000A router had an increased failure rate, if my home network traffic had increased beyond the router&#8217;s capabilities, or if the problem had always existed but had become more noticeable.</p>
</div>
<div class="paragraph">
<p>As most hypotheses pointed at the router, I did not attempt to root cause the issue and replaced it with a <a href="https://dl.ubnt.com/datasheets/edgemax/EdgeRouter_DS.pdf">Ubiquiti <em>Edge</em>Router 12</a>. The results were fantastic&mdash;there was a notable increase in network throughput and no interrupted connections.</p>
</div>
</div>
<div class="sect2">
<h3 id="nas-version-1-3">NAS, Version 1.3</h3>
<div class="paragraph">
<p>Many companies are exploring calendar management and sharing solutions, but address books and contacts get less attention. I have some use cases for contact sharing, so I installed <a href="https://www.synology.com/en-global/dsm/feature/contacts">Synology Contacts</a> to see how well it could support my needs. It does work well in limited scenarios, and I am using it for now, but I&#8217;m also looking for a more sophisticated solution.</p>
</div>
</div>
<div class="sect2">
<h3 id="nas-version-1-4">NAS, Version 1.4</h3>
<div class="paragraph">
<p>I use <a href="https://netnewswire.com/">NetNewsWire</a> to follow many feeds and blogs. Unfortunately, I&#8217;ve been unable to get <a href="https://netnewswire.com/help/iCloud">iCloud syncing</a> to work for unknown reasons. I wanted to sync feeds and read state across devices, so during the holiday break in December 2022, I set up a <a href="https://www.freshrss.org/">FreshRSS</a> container on the Synology and migrated my feeds to the self-hosted service. Using NetNewsWire as my primary client and FressRSS as the backend has worked quite well.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="summary">Summary</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Over the course of 8+ years, my home computing infrastructure grew from a naïve router/WiFi access point installation to an eclectic mix of hardware supporting a half dozen services. By the end of April 2023, my home lab consisted of the following:</p>
</div>
<div class="paragraph">
<p><strong>Hardware:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.amazon.com/dp/B000FBK3QK/">CyberPower 1500VA/900W UPS</a></p>
</li>
<li>
<p><a href="https://www.amazon.com/dp/B08P25PBSR/">ECHOGEAR 10U Network Rack</a></p>
</li>
<li>
<p>Three <a href="https://www.amazon.com/dp/B07DPNSW6P/">eero Pros (2nd Generation)</a> and one <a href="https://www.amazon.com/dp/B07DPND6BB/">eero Beacon</a></p>
</li>
<li>
<p><a href="https://global.synologydownload.com/download/Document/Hardware/DataSheet/DiskStation/16-year/DS216+II/enu/Synology_DS216_PlusII_Data_Sheet_enu.pdf">Synology DS216+II</a> with two <a href="https://documents.westerndigital.com/content/dam/doc-library/en_us/assets/public/western-digital/product/hgst/deskstar-nas/data-sheet-deskstar-nas-internal-drive-kit.pdf">10 TB 7200 RPM Deskstar NAS hard drives</a></p>
</li>
<li>
<p><a href="https://support.apple.com/kb/SP782?locale=en_US">Mac mini (2018)</a></p>
</li>
<li>
<p><a href="https://support.apple.com/kb/SP823?locale=en_US">Mac mini (M1, 2020)</a></p>
</li>
<li>
<p><a href="https://dl.ubnt.com/datasheets/edgemax/EdgeRouter_DS.pdf">Ubiquiti <em>Edge</em>Router 12</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Services:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.freshrss.org/">FreshRSS</a></p>
</li>
<li>
<p><a href="https://homebridge.io/">Homebridge</a></p>
</li>
<li>
<p><a href="https://www.plex.tv/">Plex</a></p>
</li>
<li>
<p><a href="https://kb.synology.com/en-sg/DSM/help/DSM/AdminCenter/file_winmacnfs_desc?version=7">SMB File Services</a></p>
</li>
<li>
<p><a href="https://www.synology.com/en-global/dsm/feature/contacts">Synology Contacts</a></p>
</li>
<li>
<p><a href="https://kb.synology.com/en-us/DSM/tutorial/How_to_back_up_files_from_Mac_to_Synology_NAS_with_Time_Machine#x_anchor_id4">Time Machine</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>During that time, as requirements changed, I decommissioned and replaced the following hardware:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.centurylink.com/home/help/internet/modems-and-routers/actiontec-c3000a.html">Actiontec C3000A</a></p>
</li>
<li>
<p><a href="https://support.apple.com/kb/SP680?locale=en_US">AirPort Extreme 802.11ac</a></p>
</li>
<li>
<p><a href="https://support.apple.com/kb/SP651?locale=en_US">AirPort Express 802.11n (2nd Generation)</a></p>
</li>
<li>
<p><a href="https://support.apple.com/kb/SP671?locale=en_US">AirPort Extreme 802.11n (2nd Generation)</a></p>
</li>
</ul>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. I no longer have my first AirPort Extreme device and, perhaps unsurprisingly, cannot recall its model. The only thing I know for sure was that it was the short, square box and, therefore, not the sixth generation. I’m pretty sure it had gigabit Ethernet because I was annoyed the AirPort Express did not. I maybe vaguely recall it did not support a guest network and only implemented the 802.11n draft specification, which, if correct, identifies it as the <a href="https://support.apple.com/kb/SP671?locale=en_US">AirPort Extreme 802.11n (2nd Generation)</a>.
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. Unfortunately, my suspicions were correct. The enclosure failed before I could transfer the data, and I could not mount the stripe set using a different enclosure or software RAID. Luckily, I successfully reverse-engineered the striping scheme and reconstructed the volume manually, which I’ll post about in the future.
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[I didn&#8217;t set out to build a home lab, but one emerged anyway. This post shares the choices I made to solve one-off problems and how those choices accumulated into a surprisingly large collection of hardware and services.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/c3fa418f-255e-4235-2204-295939a90300/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/c3fa418f-255e-4235-2204-295939a90300/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Objective-C Internals: Associated References</title><link href="https://alwaysprocessing.blog/2023/06/05/objc-assoc-obj" rel="alternate" type="text/html" title="Objective-C Internals: Associated References" /><published>2023-06-05T00:00:00+00:00</published><updated>2023-06-05T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/06/05/objc-assoc-obj</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/06/05/objc-assoc-obj"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>I remember eagerly awaiting the day we changed the minimum deployment target in what would become <a href="https://en.wikipedia.org/wiki/Microsoft_Office_2016">Microsoft Office 2016 for Mac</a> to Mac OS X 10.6<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>. <a href="https://en.wikipedia.org/wiki/Mac_OS_X_Snow_Leopard">Snow Leopard</a> introduced <em>a lot</em> of new APIs, including <a href="https://developer.apple.com/documentation/DISPATCH">Grand Central Dispatch</a> and <a href="https://en.wikipedia.org/wiki/Blocks_(C_language_extension)">blocks</a>. But, I was most excited to start using Objective-C <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html">Associative References</a> to replace some terrible code.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="the-old-way">The Old Way</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Objective-C&#8217;s greatest strength (and weakness) is its <a href="https://alwaysprocessing.blog/2023/01/02/objc-class-arch#method-dispatch">dynamic method binding</a>. Virtually all major third-party apps (ab)use this feature to fill a functionality gap or mitigate app/system architecture impedance mismatches.</p>
</div>
<div class="paragraph">
<p>The ability to tie an object&#8217;s lifetime to the lifetime of an object instantiated and controlled by a third party (i.e., Apple) was one such functionality gap. Before the runtime provided this feature, an app could implement this functionality by, in part, pre-patching the implementation of <code>-[NSObject dealloc]</code>. The following code sample shows how a third party may have implemented <em>Associative References</em> using this approach.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c"><span></span><span class="tok-cp">#import &lt;Foundation/Foundation.h&gt;</span>
<span class="tok-cp">#import &lt;objc/runtime.h&gt;</span>

<span class="tok-k">static</span><span class="tok-w"> </span><span class="tok-n">OSSpinLock</span><span class="tok-w"> </span><span class="tok-n">s_lock</span><span class="tok-p">;</span><span class="tok-w">               </span><span class="tok-c1">// for main side table</span>
<span class="tok-k">static</span><span class="tok-w"> </span><span class="tok-bp">NSMapTable</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">s_associatedObjects</span><span class="tok-p">;</span><span class="tok-w"> </span><span class="tok-c1">// main side table</span>
<span class="tok-k">static</span><span class="tok-w"> </span><span class="tok-kt">IMP</span><span class="tok-w"> </span><span class="tok-n">s_NSObject_dealloc</span><span class="tok-p">;</span><span class="tok-w">          </span><span class="tok-c1">// original implementation</span>

<span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-nf">APAssociatedObjectSet</span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">object</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">association</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">previousAssociation</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">nil</span><span class="tok-p">;</span>

<span class="tok-w">  </span><span class="tok-c1">// retain does not require the lock, so do it outside of the</span>
<span class="tok-w">  </span><span class="tok-c1">// lock to minimize time spent holding the lock</span>
<span class="tok-w">  </span><span class="tok-p">[</span><span class="tok-n">association</span><span class="tok-w"> </span><span class="tok-k">retain</span><span class="tok-p">];</span>

<span class="tok-w">  </span><span class="tok-n">OSSpinLockLock</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">s_lock</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-n">previousAssociation</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-n">s_associatedObjects</span><span class="tok-w"> </span><span class="tok-n">objectForKey</span><span class="tok-o">:</span><span class="tok-n">object</span><span class="tok-p">];</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">association</span><span class="tok-w"> </span><span class="tok-o">!=</span><span class="tok-w"> </span><span class="tok-nb">nil</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-p">[</span><span class="tok-n">s_associatedObjects</span><span class="tok-w"> </span><span class="tok-n">setObject</span><span class="tok-o">:</span><span class="tok-n">association</span><span class="tok-w"> </span><span class="tok-n">forKey</span><span class="tok-o">:</span><span class="tok-n">object</span><span class="tok-p">];</span>
<span class="tok-w">  </span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-p">[</span><span class="tok-n">s_associatedObjects</span><span class="tok-w"> </span><span class="tok-n">removeObjectForKey</span><span class="tok-o">:</span><span class="tok-n">object</span><span class="tok-p">];</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-n">OSSpinLockUnlock</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">s_lock</span><span class="tok-p">);</span>

<span class="tok-w">  </span><span class="tok-c1">// release outside of the lock in case this is the last</span>
<span class="tok-w">  </span><span class="tok-c1">// release, as the dealloc implementation acquires the lock</span>
<span class="tok-w">  </span><span class="tok-p">[</span><span class="tok-n">previousAssociation</span><span class="tok-w"> </span><span class="tok-k">release</span><span class="tok-p">];</span>
<span class="tok-p">}</span>

<span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-nf">APAssociatedObjectGet</span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">object</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">OSSpinLockLock</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">s_lock</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-n">association</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-n">s_associatedObjects</span><span class="tok-w"> </span><span class="tok-n">objectForKey</span><span class="tok-o">:</span><span class="tok-n">object</span><span class="tok-p">];</span>
<span class="tok-w">  </span><span class="tok-c1">// retain the associated object to ensure it&#39;s not deallocated</span>
<span class="tok-w">  </span><span class="tok-c1">// while in use by the caller in case another thread changes the</span>
<span class="tok-w">  </span><span class="tok-c1">// associated object between now and then</span>
<span class="tok-w">  </span><span class="tok-p">[</span><span class="tok-n">association</span><span class="tok-w"> </span><span class="tok-k">retain</span><span class="tok-p">];</span>
<span class="tok-w">  </span><span class="tok-n">OSSpinLockUnlock</span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">s_lock</span><span class="tok-p">);</span>

<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-n">association</span><span class="tok-w"> </span><span class="tok-n">autorelease</span><span class="tok-p">];</span>
<span class="tok-p">}</span>

<span class="tok-k">static</span><span class="tok-w"> </span><span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-nf">APAssociatedObject_dealloc</span><span class="tok-p">(</span><span class="tok-kt">id</span><span class="tok-w"> </span><span class="tok-nb">self</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-kt">SEL</span><span class="tok-w"> </span><span class="tok-n">_cmd</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-c1">// release any associated object and remove the side table entry</span>
<span class="tok-w">  </span><span class="tok-n">APAssociatedObjectSet</span><span class="tok-p">(</span><span class="tok-nb">self</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-nb">nil</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-p">(</span><span class="tok-o">*</span><span class="tok-n">s_NSObject_dealloc</span><span class="tok-p">)(</span><span class="tok-nb">self</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">_cmd</span><span class="tok-p">);</span>
<span class="tok-p">}</span>

<span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-nf">APAssociatedObjectInitialize</span><span class="tok-p">(</span><span class="tok-kt">void</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">s_lock</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">OS_SPINLOCK_INIT</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-c1">// The key is weak to prevent the object from becoming immortal.</span>
<span class="tok-w">  </span><span class="tok-c1">// The value is weak to explicitly control the retain count to</span>
<span class="tok-w">  </span><span class="tok-c1">// prevent dealloc reentrancy deadlocks.</span>
<span class="tok-w">  </span><span class="tok-n">s_associatedObjects</span><span class="tok-o">=</span><span class="tok-p">[</span><span class="tok-bp">NSMapTable</span><span class="tok-w"> </span><span class="tok-n">mapTableWithWeakToWeakObjects</span><span class="tok-p">];</span>

<span class="tok-w">  </span><span class="tok-c1">// Pre-patch -[NSObject dealloc] to clean up s_associatedObjects</span>
<span class="tok-w">  </span><span class="tok-n">Method</span><span class="tok-w"> </span><span class="tok-n">m</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">class_getInstanceMethod</span><span class="tok-p">([</span><span class="tok-bp">NSObject</span><span class="tok-w"> </span><span class="tok-k">class</span><span class="tok-p">],</span>
<span class="tok-w">                                     </span><span class="tok-k">@selector</span><span class="tok-p">(</span><span class="tok-n">dealloc</span><span class="tok-p">));</span>
<span class="tok-w">  </span><span class="tok-n">s_NSObject_dealloc</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">method_getImplementation</span><span class="tok-p">(</span><span class="tok-n">m</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-n">method_setImplementation</span><span class="tok-p">(</span><span class="tok-n">m</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">IMP</span><span class="tok-p">)</span><span class="tok-o">&amp;</span><span class="tok-n">APAssociatedObject_dealloc</span><span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Although the implementation is only 59 lines, including white space and comments, there are a few things I want to call out:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>This implementation supports 0 or 1 object associations but could support an arbitrary number of associations, like <code>objc_setAssociatedObject()</code>, with minor revisions. Alternatively, the client could use an <code>NSMutableDictionary</code> to associate an arbitrary number of objects.</p>
</li>
<li>
<p>Every <code>-dealloc</code> needs to acquire a lock to perform bookkeeping (in addition to the runtime and allocator lock acquisition(s)). We saw in a previous post that the runtime has a <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#has_assoc-has_cxx_dtor-weakly_referenced-and-has_sidetable_rc">fast deallocation path</a> for object instances that <em>do not</em> have an associated reference (in addition to other criteria), enabling it to avoid locking overhead for most cases.</p>
</li>
<li>
<p>Removing an association may cause the associated object to deallocate, which, in turn, may cause its associated objects to deallocate. So, the implementation must avoid recursion while holding the lock, as <code>OSSpinLock</code> is not reentrant.</p>
</li>
<li>
<p>Pre-patching <code>-dealloc</code> on the class of the object gaining an association is not a viable approach for two reasons:</p>
<div class="olist loweralpha">
<ol class="loweralpha" type="a">
<li>
<p>A class hierarchy may have multiple patches. For example, after setting an associated object on an <code>NSObject</code> and another on <code>NSView</code>, all <code>NSView</code> instances, including subclasses, call into the patch twice during deallocation. An implementation could handle this case but at the cost of additional complexity.</p>
</li>
<li>
<p>Calling into the correct <code>-dealloc</code> from the patch becomes more challenging. Continuing with the above example, if an <code>NSTableView</code> is deallocating, how does the patch know whether it should call the <code>-[NSView dealloc]</code> implementation or the <code>-[NSObject dealloc]</code> implementation? (The class identity of <code>self</code> is always <code>NSTableView</code>.) A significant amount of bookkeeping would be required to track where an object is in its dealloc chain and to handle additional deallocations that occur as part of its deallocation.</p>
</li>
</ol>
</div>
</li>
<li>
<p>Objective-C Automatic Reference Counting (ARC) didn&#8217;t debut until OS X 10.7 Lion. So I want to highlight two things that are no longer relevant to the modern Objective-C programmer:</p>
<div class="ulist">
<ul>
<li>
<p>The <code>retain</code> and <code>autorelease</code> calls in <code>APAssociatedObjectGet()</code> guarantee the returned object lives through the current autorelease scope. Without this, another thread may cause the object to deallocate between its retrieval from the map table and its return to the caller.</p>
</li>
<li>
<p>The use of <em>weak</em> in the map table&#8217;s <code>mapTableWithWeakToWeakObjects</code> factory method does <strong>not</strong> have ARC&#8217;s zeroing weak reference semantics. Instead, it&#8217;s the equivalent of ARC&#8217;s <code>unsafe_unretained</code>.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>APAssociatedObjectInitialize()</code> could have an <code>__attribute__((constructor))</code> to initialize the feature before <code>main()</code> is called. I left that out as major apps usually have a sophisticated initialization system that would call this function.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Next, let&#8217;s see how Apple&#8217;s Objective-C runtime implements this feature.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="the-apple-way">The Apple Way</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The above third-party implementation and commentary align shockingly well with Apple&#8217;s implementation. (I say shocking because I wrote it before looking up Apple&#8217;s implementation<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>.)</p>
</div>
<div class="paragraph">
<p>First, let&#8217;s look at <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime.mm#L657-L661"><code>objc_setAssociatedObject()</code></a>, which simply calls <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L159-L220"><code>_object_set_associative_reference()</code></a>.</p>
</div>
<div class="listingblock">
<div class="title"><code>runtime/objc-references.mm</code> lines <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L170-L219">170-219</a></div>
<div class="content">
<pre class="pygments highlight"><code data-lang="c++"><span></span><span class="tok-n">DisguisedPtr</span><span class="tok-o">&lt;</span><span class="tok-n">objc_object</span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-n">disguised</span><span class="tok-p">{(</span><span class="tok-n">objc_object</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-n">object</span><span class="tok-p">};</span>
<span class="tok-n">ObjcAssociation</span><span class="tok-w"> </span><span class="tok-n">association</span><span class="tok-p">{</span><span class="tok-n">policy</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">value</span><span class="tok-p">};</span>

<span class="tok-c1">// retain the new value (if any) outside the lock.</span>
<span class="tok-n">association</span><span class="tok-p">.</span><span class="tok-n">acquireValue</span><span class="tok-p">();</span>

<span class="tok-kt">bool</span><span class="tok-w"> </span><span class="tok-n">isFirstAssociation</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">false</span><span class="tok-p">;</span>
<span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-n">AssociationsManager</span><span class="tok-w"> </span><span class="tok-n">manager</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-n">AssociationsHashMap</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-nf">associations</span><span class="tok-p">(</span><span class="tok-n">manager</span><span class="tok-p">.</span><span class="tok-n">get</span><span class="tok-p">());</span>

<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">value</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-k">auto</span><span class="tok-w"> </span><span class="tok-n">refs_result</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">associations</span><span class="tok-p">.</span><span class="tok-n">try_emplace</span><span class="tok-p">(</span><span class="tok-n">disguised</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">ObjectAssociationMap</span><span class="tok-p">{});</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">refs_result</span><span class="tok-p">.</span><span class="tok-n">second</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-cm">/* it&#39;s the first association we make */</span>
<span class="tok-w">      </span><span class="tok-n">isFirstAssociation</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-p">}</span>

<span class="tok-w">    </span><span class="tok-cm">/* establish or replace the association */</span>
<span class="tok-w">    </span><span class="tok-k">auto</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">refs</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">refs_result</span><span class="tok-p">.</span><span class="tok-n">first</span><span class="tok-o">-&gt;</span><span class="tok-n">second</span><span class="tok-p">;</span>
<span class="tok-w">    </span><span class="tok-k">auto</span><span class="tok-w"> </span><span class="tok-n">result</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">refs</span><span class="tok-p">.</span><span class="tok-n">try_emplace</span><span class="tok-p">(</span><span class="tok-n">key</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">std</span><span class="tok-o">::</span><span class="tok-n">move</span><span class="tok-p">(</span><span class="tok-n">association</span><span class="tok-p">));</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">result</span><span class="tok-p">.</span><span class="tok-n">second</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-n">association</span><span class="tok-p">.</span><span class="tok-n">swap</span><span class="tok-p">(</span><span class="tok-n">result</span><span class="tok-p">.</span><span class="tok-n">first</span><span class="tok-o">-&gt;</span><span class="tok-n">second</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-k">auto</span><span class="tok-w"> </span><span class="tok-n">refs_it</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">associations</span><span class="tok-p">.</span><span class="tok-n">find</span><span class="tok-p">(</span><span class="tok-n">disguised</span><span class="tok-p">);</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">refs_it</span><span class="tok-w"> </span><span class="tok-o">!=</span><span class="tok-w"> </span><span class="tok-n">associations</span><span class="tok-p">.</span><span class="tok-n">end</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-k">auto</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">refs</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">refs_it</span><span class="tok-o">-&gt;</span><span class="tok-n">second</span><span class="tok-p">;</span>
<span class="tok-w">      </span><span class="tok-k">auto</span><span class="tok-w"> </span><span class="tok-n">it</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">refs</span><span class="tok-p">.</span><span class="tok-n">find</span><span class="tok-p">(</span><span class="tok-n">key</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">it</span><span class="tok-w"> </span><span class="tok-o">!=</span><span class="tok-w"> </span><span class="tok-n">refs</span><span class="tok-p">.</span><span class="tok-n">end</span><span class="tok-p">())</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">        </span><span class="tok-n">association</span><span class="tok-p">.</span><span class="tok-n">swap</span><span class="tok-p">(</span><span class="tok-n">it</span><span class="tok-o">-&gt;</span><span class="tok-n">second</span><span class="tok-p">);</span>
<span class="tok-w">        </span><span class="tok-n">refs</span><span class="tok-p">.</span><span class="tok-n">erase</span><span class="tok-p">(</span><span class="tok-n">it</span><span class="tok-p">);</span>
<span class="tok-w">        </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">refs</span><span class="tok-p">.</span><span class="tok-n">size</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">          </span><span class="tok-n">associations</span><span class="tok-p">.</span><span class="tok-n">erase</span><span class="tok-p">(</span><span class="tok-n">refs_it</span><span class="tok-p">);</span>
<span class="tok-w">        </span><span class="tok-p">}</span>
<span class="tok-w">      </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span>

<span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">isFirstAssociation</span><span class="tok-p">)</span>
<span class="tok-w">  </span><span class="tok-n">object</span><span class="tok-o">-&gt;</span><span class="tok-n">setHasAssociatedObjects</span><span class="tok-p">();</span>

<span class="tok-c1">// release the old value (outside of the lock).</span>
<span class="tok-n">association</span><span class="tok-p">.</span><span class="tok-n">releaseHeldValue</span><span class="tok-p">();</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Given the alignment with the previous section, I&#8217;ll simply highlight key similarities and differences relative to my implementation.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-private.h#L983-L1031"><code>DisguisedPtr</code></a> is used to inhibit heap tracing in tools like <code>leaks</code>.</p>
</li>
<li>
<p>The <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L49-L100"><code>ObjcAssociation</code></a> helper object implements the association policy (storage with <code>assign</code>, <code>retain</code>, or <code>copy</code> semantics, and whether reads are <code>atomic</code> or <code>nonatomic</code>).</p>
</li>
<li>
<p>The <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L108-L123"><code>AssociationsManager</code></a> is an <a href="https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization">RAII</a> convenience object to lock and unlock the <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L45">associations spinlock</a> (now an <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/Threading/darwin.h#L194-L224">unfair lock</a>).</p>
</li>
<li>
<p>Object associations are stored using a hash map (specifically LLVM&#8217;s <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/llvm-DenseMap.h">DenseMap</a>). A <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L103">top-level hash map</a> maps object pointers to an <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L102">associations hash map</a>, which maps keys to <code>ObjcAssociation</code>s (the object and its retain policy).</p>
</li>
<li>
<p>Associating a <code>nil</code> value removes any previously associated object.</p>
</li>
<li>
<p>When an object gains its first association, the runtime updates its state to turn off the <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#has_assoc-has_cxx_dtor-weakly_referenced-and-has_sidetable_rc">fast deallocation path</a>.</p>
</li>
<li>
<p>The release of any previously associated object takes place outside of the lock.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Like the setter, <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime.mm#L642-L646"><code>objc_getAssociatedObject()</code></a> simply calls <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L137-L157"><code>_object_get_associative_reference()</code></a>. The get path is straightforward, so there&#8217;s nothing for me to comment on! 🙊</p>
</div>
<div class="paragraph">
<p>Apple&#8217;s implementation provides a curious function, <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime.mm#L663-L668"><code>objc_removeAssociatedObjects()</code></a>. I&#8217;m honestly not sure why this is a public API&mdash;the comment in <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/runtime.h#L1650-L1664"><code>runtime.h</code></a> advises against using it (and for a good reason):</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>The main purpose of this function is to make it easy to return an object to a "pristine state”. You should not use this function for general removal of associations from objects, since it also removes associations that other clients may have added to the object. Typically you should use <code>objc_setAssociatedObject</code> with a nil value to clear an association.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>Like the getter and setter functions, <code>objc_removeAssociatedObjects()</code> calls <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L222-L269"><code>_object_remove_associations()</code></a>. But, this internal function takes one additional parameter: <code>bool deallocating</code>, which is <code>false</code> when called by <code>objc_removeAssociatedObjects()</code>. This internal function has only one other caller, <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L8569"><code>objc_destructInstance()</code></a>, which, unsurprisingly, passes <code>true</code> for <code>deallocating</code>.</p>
</div>
<div class="paragraph">
<p>So, what does the <code>deallocating</code> flag do? A <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L238">comment</a> in the function explains its purpose:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>If we are not deallocating, then <code>SYSTEM_OBJECT</code> associations are preserved.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>Apple has an internal policy flag, <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm#L42"><code>OBJC_ASSOCIATION_SYSTEM_OBJECT</code></a>, which prevents its associated objects from being removed by <code>objc_removeAssociatedObjects()</code>. You can shoot yourself in the foot using this function, but Apple will prevent you from violating their assumptions.</p>
</div>
<div class="paragraph">
<p>I suspect this is why the association key has a type of <code>void *</code>: pointer keys are hard to identify in Apple&#8217;s frameworks and subsequently (ab)use in third party-apps vs., for example, string keys which are easy-ish to find and use (e.g., <code>NSNotificationName</code>).</p>
</div>
<div class="sect2">
<h3 id="tagged-pointer-objects">Tagged Pointer Objects</h3>
<div class="paragraph">
<p>What happens when setting an associated object on a <a href="https://alwaysprocessing.blog/2023/03/19/objc-tagged-ptr">tagged pointer object</a>? The effect is the same as assigning the object to a global variable with the same storage policy: the object remains until a new value is assigned. So, setting an associated object on a tagged pointer object will effectively leak the associated object.</p>
</div>
<div class="paragraph">
<p>The associated object implementation has no code paths to handle tagged pointers (not even to log a warning in the console). So, the runtime stores the tagged pointer in the associations hash map where it lives indefinitely because tagged pointer objects never deallocate.</p>
</div>
<div class="paragraph">
<p>Another side effect of tagged pointer objects is that they effectively <a href="https://en.wikipedia.org/wiki/Interning_(computer_science)">intern all values</a>. While some types like <code>NSNumber</code> are known to implement <a href="https://github.com/apple-oss-distributions/CF/blob/CF-1153.18/CFNumber.c#L1037">some form of interning</a>, <code>NSString</code> did not have such behavior. But, the <code>NSString</code> tagged pointer code path is aggressive enough that <a href="https://markavitale.com/objc-tagged-pointers#the-explanation">localized strings loaded from disk</a> may yield a tagged pointer object! Thus, any code setting associated objects on types of <code>NSString</code> may find associated objects stomping on each other if the string instances are tagged pointer objects instead of discrete instances.</p>
</div>
<div class="paragraph">
<p>Although the use of tagged pointer objects is considered an internal implementation detail, take a look at the <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-internal.h#L447-L509">classes that use tagged pointers</a> and avoid using associated objects with any objects with those types.</p>
</div>
</div>
<div class="sect2">
<h3 id="a-closer-look-at-assign-storage">A Closer Look At assign Storage</h3>
<div class="paragraph">
<p>In writing the post, I realized I&#8217;ve been misusing this API for over a decade 🤦‍♂️. The comment next to the <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/runtime.h#L1639"><code>OBJC_ASSOCIATION_ASSIGN</code></a> policy says:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Specifies a weak reference to the associated object.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>As mentioned in <a href="#the-old-way">The Old Way</a> section, before ARC, the term <em>weak</em> is equivalent to ARC&#8217;s <code>unsafe_unretained</code>; this flag does <strong>not</strong> use ARC zeroing weak reference semantics. Take a look through the <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-references.mm">implementation</a> for any use of <em>weak</em>. There isn&#8217;t any!</p>
</div>
<div class="paragraph">
<p>I have a lot of grepping and code reviewing to do this week&#8230;&#8203;</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A third-party implementation of <em>Associated References</em> can nearly match what Apple can do with a first-party implementation, with the main first-party advantage being the availability of a fast deallocation path for objects without associated objects. New runtime optimizations (i.e., tagged pointer objects) may cause unexpected behavior for code associating objects with objects whose uniqueness and lifetime may change across OS versions. And, historical context is essential&mdash;the assumptions underlying documentation may change over time, warping its meaning.</p>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. By the time Office 2016 launched, macOS 10.12 Sierra was the current release. So, following the <em>n-2</em> pattern, Office&#8217;s minimum deployment target was OS X 10.10 Yosemite.
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. I did make one revision after reading through Apple&#8217;s implementation, which was to perform the associated object&#8217;s retain outside of the lock.
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[A comparison of Apple&#8217;s Associated References implementation and one I wrote for historical context, with additional notes about use with tagged pointer objects and what the assign association policy actually does.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/ae4bf923-51a5-438e-60a7-ecd731bb7f00/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/ae4bf923-51a5-438e-60a7-ecd731bb7f00/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Site Refresh</title><link href="https://alwaysprocessing.blog/2023/05/30/site-refresh" rel="alternate" type="text/html" title="Site Refresh" /><published>2023-05-30T00:00:00+00:00</published><updated>2023-05-30T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/05/30/site-refresh</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/05/30/site-refresh"><![CDATA[<div class="paragraph">
<p>The new post hiatus was unintentional&mdash;an inordinate amount of time was spent designing the new theme and improving the functionality of the site.</p>
</div>
<div class="paragraph">
<p>I have not done design work in my career (perhaps implied by the topics I choose to write about), so updating the visuals to something that doesn&#8217;t make your eyes bleed took many, many attempts. I think it landed in a good place and am very appreciative of the feedback received along the way.</p>
</div>
<div class="paragraph">
<p>In addition to the new layout, theme, and art, version 3 of the site also features:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Brief summaries of each post</p>
</li>
<li>
<p>Rich post links (title, summary, and image)</p>
</li>
<li>
<p>A <em>By Line</em> in posts, which includes the author and estimated read time in addition to the publish date</p>
</li>
<li>
<p>More CSS breakpoints for improved screen utilization</p>
</li>
<li>
<p>An <a href="https://alwaysprocessing.blog/all/posts">All Posts</a> page to index blog content</p>
</li>
<li>
<p><em>Series</em>, which collects related posts into a single page index, starting with <a href="https://alwaysprocessing.blog/series/objc-internals">Objective-C Internals</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>I also added a site <a href="https://alwaysprocessing.blog/changelog">changelog</a> to document the new features, updates, and <s>churn</s> fixes. There&#8217;s a surprising number of corner cases for something as simple as this&hellip;</p>
</div>
<div class="paragraph">
<p>Now, back to posting arcane technical articles! 😁</p>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[The site has undergone extensive updates, receiving a new layout, theme, and art, along with a few usability improvements. With this time-consuming investment complete, I&#8217;ll again have bandwidth to regularly post new technical content.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/2b9a0302-ed23-43b4-42ef-23d72e00de00/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/2b9a0302-ed23-43b4-42ef-23d72e00de00/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Objective-C Internals: Tagged Pointer Objects</title><link href="https://alwaysprocessing.blog/2023/03/19/objc-tagged-ptr" rel="alternate" type="text/html" title="Objective-C Internals: Tagged Pointer Objects" /><published>2023-03-19T00:00:00+00:00</published><updated>2023-03-19T00:00:00+00:00</updated><id>https://alwaysprocessing.blog/2023/03/19/objc-tagged-ptr</id><content type="html" xml:base="https://alwaysprocessing.blog/2023/03/19/objc-tagged-ptr"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Tagged pointer objects is a private feature of the Objective-C runtime that Apple uses to optimize some core Foundation classes (pun intended).</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="what-is-a-tagged-pointer">What is a tagged pointer?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Most memory allocators guarantee a minimum alignment for each allocation. For example, <code>malloc()</code> on Apple&#8217;s platforms guarantees an alignment that "can be used for any data type, including AltiVec- and SSE-related types."</p>
</div>
<div class="paragraph">
<p>In practice, all allocations are 16-byte aligned, so the bottom four bits of any pointer are always zero. Sometimes it is advantageous to use this fact and store additional information in those bits (which requires updating all uses of the pointer value to handle the extra bits correctly). When unused bits in a pointer store additional information, the pointer is often referred to as <em>tagged</em>.</p>
</div>
<div class="paragraph">
<p>The <code>isa</code> pointer in Objective-C objects may be tagged, the details of which were discussed in the <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa#non-pointer-isa">Non-Pointer isa</a> section of <a href="https://alwaysprocessing.blog/2023/01/19/objc-class-isa">The Many Uses of isa</a> post earlier in this series.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tagged-pointer-objects">Tagged Pointer Objects</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Tagged pointer objects in Objective-C are a special type of object pointer. If the object pointer is tagged, the data held by the class instance represented by the tagged pointer is encoded entirely into the pointer value itself. No heap allocation occurs.</p>
</div>
<div class="paragraph">
<p>Eliminating the heap allocation can significantly reduce the cost of, for example, an <code>NSArray</code> of <code>NSNumber</code>s. An <code>NSNumber</code> allocated on the heap uses a minimum of 16 bytes (since the allocation is 16-byte aligned) plus the 8 bytes for its pointer. However, an <code>NSNumber</code> encoded into a tagged pointer uses only the 8 bytes for its pointer, saving both memory and time by not calling the allocator.</p>
</div>
<div class="sect2">
<h3 id="tag-identity">Tag Identity</h3>
<div class="paragraph">
<p>The bit used to identify a pointer as tagged varies by platform. <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-internal.h#L755-L759">objc-internal.h</a> provides a convenience function to the runtime implementation that identifies whether a pointer is tagged. macOS and Catalyst apps on Intel processors use bit 0 to identify a tagged pointer object, and everything else uses bit 63.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="c++"><span></span><span class="tok-cp">#if __arm64__</span>
<span class="tok-w">  </span><span class="tok-c1">// ARM64 uses a new tagged pointer scheme...</span>
<span class="tok-cp"># define _OBJC_TAG_MASK (1UL&lt;&lt;63)</span>
<span class="tok-cp">#elif (TARGET_OS_OSX || TARGET_OS_MACCATALYST) &amp;&amp; __x86_64__</span>
<span class="tok-w">  </span><span class="tok-c1">// 64-bit Mac - tag bit is LSB</span>
<span class="tok-cp"># define _OBJC_TAG_MASK 1UL</span>
<span class="tok-cp">#else</span>
<span class="tok-w">  </span><span class="tok-c1">// Everything else - tag bit is MSB</span>
<span class="tok-cp"># define _OBJC_TAG_MASK (1UL&lt;&lt;63)</span>
<span class="tok-cp">#endif</span>

<span class="tok-k">static</span><span class="tok-w"> </span><span class="tok-kr">inline</span><span class="tok-w"> </span><span class="tok-kt">bool</span>
<span class="tok-nf">_objc_isTaggedPointer</span><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">ptr</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">((</span><span class="tok-kt">uintptr_t</span><span class="tok-p">)</span><span class="tok-n">ptr</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">_OBJC_TAG_MASK</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-n">_OBJC_TAG_MASK</span><span class="tok-p">;</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Eliminating the heap allocation to store the object&#8217;s value also eliminates the storage for the <code>isa</code> pointer. So, the tagged pointer object scheme reserves some bits to identify the object&#8217;s class.</p>
</div>
<div class="paragraph">
<p>At the time of this writing, the Objective-C runtime has two schemes to reserve class identity bits: one reserves 3 bits for objects with 60-bit payloads, and the other reserves 11 bits for objects with 52-bit payloads.</p>
</div>
<div class="paragraph">
<p>Up to 7 class types can use the 60-bit payload variant (with the eighth type being a special case to identify the 52-bit payload variant). And up to 256 class types can use the 52-bit payload variant. <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-internal.h#L447-L509">objc-internal.h</a> has an enum that provides some symbolic identity for various class identity bit values.</p>
</div>
<div class="paragraph">
<p>When the runtime requires the <code>isa</code> pointer for an object, it calls <code>objc_object::getIsa()</code><sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup> (defined in <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-object.h#L76-L92">objc-object.h</a>), which returns the <code>isa</code> instance variable for objects allocated on the heap and the <code>isa</code> pointer stored in one of the tag class arrays for tagged pointer objects.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="c++"><span></span><span class="tok-kr">inline</span><span class="tok-w"> </span><span class="tok-n">Class</span>
<span class="tok-nf">objc_object::getIsa</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">fastpath</span><span class="tok-p">(</span><span class="tok-o">!</span><span class="tok-n">isTaggedPointer</span><span class="tok-p">()))</span><span class="tok-w"> </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">ISA</span><span class="tok-p">(</span><span class="tok-cm">/*authenticated*/</span><span class="tok-nb">true</span><span class="tok-p">);</span>

<span class="tok-w">  </span><span class="tok-k">extern</span><span class="tok-w"> </span><span class="tok-n">objc_class</span><span class="tok-w"> </span><span class="tok-n">OBJC_CLASS_$___NSUnrecognizedTaggedPointer</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-kt">uintptr_t</span><span class="tok-w"> </span><span class="tok-n">slot</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">ptr</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">uintptr_t</span><span class="tok-p">)</span><span class="tok-k">this</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-n">Class</span><span class="tok-w"> </span><span class="tok-n">cls</span><span class="tok-p">;</span>

<span class="tok-w">  </span><span class="tok-n">slot</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">ptr</span><span class="tok-w"> </span><span class="tok-o">&gt;&gt;</span><span class="tok-w"> </span><span class="tok-n">_OBJC_TAG_SLOT_SHIFT</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">_OBJC_TAG_SLOT_MASK</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-n">cls</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">objc_tag_classes</span><span class="tok-p">[</span><span class="tok-n">slot</span><span class="tok-p">];</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">slowpath</span><span class="tok-p">(</span><span class="tok-n">cls</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">Class</span><span class="tok-p">)</span><span class="tok-o">&amp;</span><span class="tok-n">OBJC_CLASS_$___NSUnrecognizedTaggedPointer</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-n">slot</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">ptr</span><span class="tok-w"> </span><span class="tok-o">&gt;&gt;</span><span class="tok-w"> </span><span class="tok-n">_OBJC_TAG_EXT_SLOT_SHIFT</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">_OBJC_TAG_EXT_SLOT_MASK</span><span class="tok-p">;</span>
<span class="tok-w">      </span><span class="tok-n">cls</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">objc_tag_ext_classes</span><span class="tok-p">[</span><span class="tok-n">slot</span><span class="tok-p">];</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">cls</span><span class="tok-p">;</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>When system frameworks initialize at the start of a process, they call <code>&#95;objc_registerTaggedPointerClass()</code> to set the <code>Class</code> object for the given tag value. We can observe this by adding a symbolic breakpoint for that function and print its arguments when called:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>(lldb) reg re x0 x1
      x0 = 0x0000000000000003
      x1 = 0x0000000203a994f0  (void *)0x0000000203a99518: __NSCFNumber</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="anatomy-of-a-message-send">Anatomy of a Message Send</h3>
<div class="paragraph">
<p>Any code that operates on the pointer value must specifically handle tagged pointers, as unconditionally dereferencing the pointer will almost certainly result in a runtime crash.</p>
</div>
<div class="paragraph">
<p>The first step in <code>objc_msgSend()</code> <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/Messengers.subproj/objc-msg-arm64.s#L578-L596">checks for a tagged pointer</a> to determine how to load the object&#8217;s <code>isa</code> pointer to find its class&#8217;s methods, similar to the <code>objc_object:getIsa()</code> implementation above. After the <code>isa</code> pointer is loaded, whether or not the pointer is tagged is immaterial to the remainder of the message send logic.</p>
</div>
<div class="paragraph">
<p>When an Objective-C class that supports tagged pointer objects receives a message, it must check whether its <code>self</code> pointer is tagged and handle that case appropriately. The following is my disassembly of <code>-[NSNumber integerValue]</code> to show, in part, how <code>NSNumber</code> tagged pointers work (per my interpretation of the disassembly of the following functions on macOS 12.6.2 for arm64).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="objective-c"><span></span><span class="tok-k">@implementation</span> <span class="tok-nc">__NSCFNumber</span>
<span class="tok-p">-</span> <span class="tok-p">(</span><span class="tok-n">NSInteger</span><span class="tok-p">)</span><span class="tok-nf">integerValue</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">[</span><span class="tok-nb">self</span><span class="tok-w"> </span><span class="tok-n">longValue</span><span class="tok-p">];</span>
<span class="tok-p">}</span>

<span class="tok-p">-</span> <span class="tok-p">(</span><span class="tok-kt">long</span><span class="tok-p">)</span><span class="tok-nf">longValue</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-kt">long</span><span class="tok-w"> </span><span class="tok-n">longValue</span><span class="tok-p">;</span>
<span class="tok-w">  </span><span class="tok-n">CFNumberGetValue</span><span class="tok-p">((</span><span class="tok-k">__bridge</span><span class="tok-w"> </span><span class="tok-n">CFNumberRef</span><span class="tok-p">)</span><span class="tok-nb">self</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">kCFNumberSInt64Type</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">longValue</span><span class="tok-p">);</span>
<span class="tok-w">  </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">longValue</span><span class="tok-p">;</span>
<span class="tok-p">}</span>
<span class="tok-k">@end</span>

<span class="tok-kt">Boolean</span><span class="tok-w"> </span><span class="tok-n">CFNumberGetValue</span><span class="tok-p">(</span><span class="tok-n">CFNumberRef</span><span class="tok-w"> </span><span class="tok-n">number</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">CFNumberType</span><span class="tok-w"> </span><span class="tok-n">theType</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-n">valuePtr</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">  </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">_objc_isTaggedPointer</span><span class="tok-p">(</span><span class="tok-n">number</span><span class="tok-p">))</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">_objc_getTaggedPointerTag</span><span class="tok-p">(</span><span class="tok-n">number</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-o">==</span><span class="tok-w"> </span><span class="tok-n">OBJC_TAG_NSNumber</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-kt">long</span><span class="tok-w"> </span><span class="tok-n">localMemory</span><span class="tok-p">;</span>
<span class="tok-w">      </span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-o">?:</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">void</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-o">&amp;</span><span class="tok-n">localMemory</span><span class="tok-p">;</span>

<span class="tok-w">      </span><span class="tok-kt">uintptr_t</span><span class="tok-w"> </span><span class="tok-n">value</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">_objc_getTaggedPointerValue</span><span class="tok-p">(</span><span class="tok-n">number</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-kt">uintptr_t</span><span class="tok-w"> </span><span class="tok-n">shift</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">value</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-mh">0x08</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-o">?</span><span class="tok-w"> </span><span class="tok-mh">0x4</span><span class="tok-w"> </span><span class="tok-o">:</span><span class="tok-w"> </span><span class="tok-mh">0x6</span><span class="tok-p">;</span>

<span class="tok-w">      </span><span class="tok-n">CFNumberType</span><span class="tok-w"> </span><span class="tok-n">type</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">__CFNumberTypeTable</span><span class="tok-p">[</span><span class="tok-n">theType</span><span class="tok-p">].</span><span class="tok-n">canonicalType</span><span class="tok-p">;</span>
<span class="tok-w">      </span><span class="tok-k">if</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">type</span><span class="tok-w"> </span><span class="tok-o">&lt;=</span><span class="tok-w"> </span><span class="tok-n">kCFNumberFloat64Type</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">        </span><span class="tok-n">value</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">value</span><span class="tok-w"> </span><span class="tok-o">&gt;&gt;</span><span class="tok-w"> </span><span class="tok-n">shift</span><span class="tok-p">;</span>

<span class="tok-w">        </span><span class="tok-k">switch</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">type</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">        </span><span class="tok-k">case</span><span class="tok-w"> </span><span class="tok-no">kCFNumberSInt8Type</span><span class="tok-p">:</span>
<span class="tok-w">          </span><span class="tok-o">*</span><span class="tok-p">(</span><span class="tok-kt">uint8_t</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">uint8_t</span><span class="tok-p">)</span><span class="tok-n">value</span><span class="tok-p">;</span>
<span class="tok-w">          </span><span class="tok-k">break</span><span class="tok-p">;</span>
<span class="tok-w">        </span><span class="tok-k">case</span><span class="tok-w"> </span><span class="tok-no">kCFNumberSInt16Type</span><span class="tok-p">:</span>
<span class="tok-w">          </span><span class="tok-o">*</span><span class="tok-p">(</span><span class="tok-kt">uint16_t</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">uint16_t</span><span class="tok-p">)</span><span class="tok-n">value</span><span class="tok-p">;</span>
<span class="tok-w">          </span><span class="tok-k">break</span><span class="tok-p">;</span>
<span class="tok-w">        </span><span class="tok-k">case</span><span class="tok-w"> </span><span class="tok-no">kCFNumberSInt32Type</span><span class="tok-p">:</span>
<span class="tok-w">          </span><span class="tok-o">*</span><span class="tok-p">(</span><span class="tok-kt">uint32_t</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">uint32_t</span><span class="tok-p">)</span><span class="tok-n">value</span><span class="tok-p">;</span>
<span class="tok-w">          </span><span class="tok-k">break</span><span class="tok-p">;</span>
<span class="tok-w">        </span><span class="tok-k">case</span><span class="tok-w"> </span><span class="tok-no">kCFNumberSInt64Type</span><span class="tok-p">:</span>
<span class="tok-w">          </span><span class="tok-o">*</span><span class="tok-p">(</span><span class="tok-kt">uint64_t</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">uint64_t</span><span class="tok-p">)</span><span class="tok-n">value</span><span class="tok-p">;</span>
<span class="tok-w">          </span><span class="tok-k">break</span><span class="tok-p">;</span>
<span class="tok-w">        </span><span class="tok-k">case</span><span class="tok-w"> </span><span class="tok-no">kCFNumberFloat32Type</span><span class="tok-p">:</span>
<span class="tok-w">          </span><span class="tok-o">*</span><span class="tok-p">(</span><span class="tok-kt">float</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">float</span><span class="tok-p">)</span><span class="tok-n">value</span><span class="tok-p">;</span>
<span class="tok-w">          </span><span class="tok-k">break</span><span class="tok-p">;</span>
<span class="tok-w">        </span><span class="tok-k">case</span><span class="tok-w"> </span><span class="tok-no">kCFNumberFloat64Type</span><span class="tok-p">:</span>
<span class="tok-w">          </span><span class="tok-o">*</span><span class="tok-p">(</span><span class="tok-kt">double</span><span class="tok-w"> </span><span class="tok-o">*</span><span class="tok-p">)</span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-kt">double</span><span class="tok-p">)</span><span class="tok-n">value</span><span class="tok-p">;</span>
<span class="tok-w">          </span><span class="tok-k">break</span><span class="tok-p">;</span>
<span class="tok-w">        </span><span class="tok-p">}</span>
<span class="tok-w">        </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-nb">true</span><span class="tok-p">;</span>
<span class="tok-w">      </span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">        </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-n">__CFNumberGetValueCompat</span><span class="tok-p">(</span><span class="tok-n">number</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">theType</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">valuePtr</span><span class="tok-p">);</span>
<span class="tok-w">      </span><span class="tok-p">}</span>
<span class="tok-w">    </span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">      </span><span class="tok-n">theType</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">__CFNumberTypeTable</span><span class="tok-p">[</span><span class="tok-n">theType</span><span class="tok-p">].</span><span class="tok-n">canonicalType</span><span class="tok-p">;</span>
<span class="tok-w">      </span><span class="tok-k">return</span><span class="tok-w"> </span><span class="tok-p">[(</span><span class="tok-k">__bridge</span><span class="tok-w"> </span><span class="tok-kt">id</span><span class="tok-p">)</span><span class="tok-n">number</span><span class="tok-w"> </span><span class="tok-n">_getValue</span><span class="tok-o">:</span><span class="tok-n">valuePtr</span><span class="tok-w"> </span><span class="tok-n">forType</span><span class="tok-o">:</span><span class="tok-n">theType</span><span class="tok-p">];</span>
<span class="tok-w">    </span><span class="tok-p">}</span>
<span class="tok-w">  </span><span class="tok-p">}</span><span class="tok-w"> </span><span class="tok-k">else</span><span class="tok-w"> </span><span class="tok-p">{</span>
<span class="tok-w">    </span><span class="tok-c1">// ...</span>
<span class="tok-w">  </span><span class="tok-p">}</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>I&#8217;ll share a few comments and observations about the manually decompiled code above:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>The private <code>&#95;&#95;NSCFNumber</code> subclass (in this code path) doesn&#8217;t operate on the value of <code>self</code> and therefore does not need to handle the tagged pointer case. The <code>-integerValue</code> method acts as an alias for the <code>longValue</code> method, which calls into CoreFoundation to do the heavy lifting. Given <code>CFNumberRef</code> and <code>NSNumber &#42;</code> are <a href="https://alwaysprocessing.blog/2023/02/16/objc-unrealized-classes#toll-free-bridging">toll-free bridged</a>, it makes sense to see that one type is a wrapper of the other.</p>
</li>
<li>
<p>Apple&#8217;s implementation of <code>CFNumber</code> almost certainly includes <a href="https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-internal.h#L755-L825">objc-internal.h</a> for access to functions that simplify working with tagged pointer objects.</p>
</li>
<li>
<p>It seems that either 4 or 6 bits of the payload act as bitflags for other <code>CFNumber</code> features, but whatever functionality that may be is not used by this function.</p>
</li>
<li>
<p>The <a href="https://github.com/apple-oss-distributions/CF/blob/CF-1153.18/CFNumber.c#L385-L424"><code>__CFNumberTypeTable</code></a> maps the public-facing type values to the corresponding fixed size type, simplifying the value extraction logic.</p>
</li>
<li>
<p>Apple has a private type, <a href="https://github.com/apple-oss-distributions/CF/blob/CF-1153.18/CFNumber.c#L423"><code>kCFNumberSInt128Type</code></a>, not handled by the switch statement, so the call to <code>&#95;&#95;CFNumberGetValueCompat()</code> is necessary for that case.</p>
</li>
<li>
<p>Floating point numbers with a non-zero fractional value do not use the tagged pointer object optimization, given the logic in the <code>switch</code> statement does not handle that scenario.</p>
</li>
<li>
<p>I am intrigued by the call to <code>-&#95;getValue:forType:</code>. It implies another private subclass uses the tag pointer object optimization, but I didn&#8217;t trace through the various code paths to attempt to identify it.</p>
</li>
</ol>
</div>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. The use of C++ to implement the Objective-C runtime is pretty clever, in my opinion. I&#8217;ll discuss the mechanics of how that works in a future post.
</div>
</div>]]></content><author><name>Brian T. Kelley</name><email>brian@briantkelley.com</email></author><summary type="html"><![CDATA[Tagged pointer objects (a private runtime feature) optimize performance by storing an object&#8217;s data in its pointer value, eliminating the object&#8217;s heap allocation. This post looks at the implementation of NSNumber to highlight the use of and implications of this optimization.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/0349e4f7-98ee-4653-764c-48a72cea8000/public" /><media:content medium="image" url="https://alwaysprocessing.blog/cdn-cgi/imagedelivery/WFfM6PwcxpVzRRpdvDWNtg/0349e4f7-98ee-4653-764c-48a72cea8000/public" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>