What hidden retains and autoreleases does calling Objective-C add?

3.6.4 · L3 · Learning

Predict first

A hot loop calls an Objective-C getter that returns an autoreleased object. What do you see in the profile?

// A: nothing, Swift is free of ObjC cost
// B: objc_retain / objc_release and a growing autorelease pool
// C: only swift_allocObject
  • A
  • B
  • C

Mental model. Objective-C hands you a book “on loan until closing time”. Swift photocopies the loan slip right away to be safe, and in a long day those slips pile up.

Objective-C methods that return objects usually return them autoreleased (+0): the callee does not give you ownership, it just promises the object lives until the pool drains. Swift must take its own strong reference immediately, so each such call adds a retain, and a release later. Clever runtime handshakes (objc_retainAutoreleasedReturnValue paired with objc_autoreleaseReturnValue) let the pair cancel out in many cases, but not when a call is not inlined or crosses an unusual boundary.

Bridging adds more. Moving a String to an NSString or a Swift array to an NSArray creates wrapper objects and sometimes conversion work (see the bridging cost question in the Foundation section). In a tight loop over Objective-C-backed APIs these show up as objc_retain, objc_release and a growing autorelease pool. The cure is the same as before: convert to Swift types once at the boundary, keep the loop in pure Swift, and wrap batch work in autoreleasepool.

Swift retains the +0 result it receives and the pool holds the autoreleased object until it drains, so you see Objective-C retain/release traffic.

Common traps

  • Calling an Objective-C getter in a hot loop. Each call can retain and autorelease its result.
  • Assuming bridging is free because it is called toll-free. Some conversions allocate or copy.

They might follow up with

  • How do you spot it in a profile? objc_retain, objc_release and objc_autoreleaseReturnValue near the top.
  • What is the pool for? It holds autoreleased objects until the end of the iteration or block. See the autorelease pool question.

Related questions

  • 3.1.5 What is an autorelease pool, and when does a Swift loop need one?
  • 2.9.3 What does bridging cost, and where does it show up in a profile?
  • 3.6.3 What is Unmanaged, and what do takeRetainedValue and takeUnretainedValue mean?

← What is Unmanaged, and what do takeRetainedValue and…