Understanding Objective‑C Blocks: Implementation, Memory Management, and Cycle Avoidance
This article explains the origin and concept of Objective‑C Blocks, details their internal structures and memory‑management mechanisms across MRR and ARC environments, demonstrates how to use and copy Blocks, and provides guidance on avoiding retain cycles through __block and __weak modifiers.
The article begins with a brief history of Objective‑C, highlighting key milestones such as the introduction of properties (2006), Blocks (Mac OS X 10.6 / iOS 4.0), and ARC (iOS 5). It then introduces the concept of closures, defining a closure as a function that captures free variables, and positions Apple’s Block as a C‑level implementation of this idea.
Apple’s official description of Blocks is quoted, emphasizing that a Block is a C‑level syntactic and runtime feature that can capture variables from the stack or heap. The article shows a simple Block usage example:
int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};Three Block storage types are described: _NSConcreteStackBlock (lives on the stack), _NSConcreteMallocBlock (heap‑allocated after copy), and _NSConcreteGlobalBlock (global, no captured variables). The article explains how the compiler decides which type to create based on whether external variables are used.
The internal layout of a Block is detailed through the structs defined in Block_private.h . The key fields are isa , flags (which include reference‑count and type information), invoke (function pointer), and a descriptor that may contain copy/dispose helpers. The flags values such as BLOCK_HAS_COPY_DISPOSE , BLOCK_IS_GLOBAL , and BLOCK_NEEDS_FREE guide the runtime’s copy and release behavior.
Memory‑management functions are examined: _Block_copy creates a heap copy for stack Blocks, sets the appropriate flags, and calls _Block_call_copy_helper to invoke any generated copy helpers. For heap Blocks, _Block_copy merely increments the reference count. The article also covers _Block_object_assign and _Block_object_dispose , which manage captured object‑type variables and __block variables.
When a captured variable is an Objective‑C object, the copy helper calls _Block_retain_object . In MRR this function is wired to retain , while under ARC it is a no‑op because ARC’s strong semantics already retain the object. For __block variables, the runtime creates a Block_byref wrapper; copying this wrapper invokes _Block_byref_copy , which in turn may call _Block_object_assign for object‑type variables.
The article provides a concise table summarizing how different captured variable types are managed (global/static, primitive, object, __block , nested Block). It then demonstrates how to avoid retain cycles: in MRR by using __block to prevent _Block_retain_object , and in ARC by capturing self weakly with __weak (or __unsafe_unretained ) before using it inside the Block.
To illustrate the compiler’s transformation, the author shows how a simple Objective‑C method containing a Block is rewritten by clang -rewrite-objc into C++‑style structs ( __block_impl , concrete Block impl structs, descriptor structs) and a separate function for the Block body. This reveals how the compiler automatically fills in isa , flags , and the function pointer, and how captured variables become fields of the generated struct.
Finally, the article notes that ARC eliminates the need for manual copy of Blocks when they are returned from methods or stored in strong / copy properties, but developers must still be aware of retain cycles and use __weak or __block appropriately.
JD Retail Technology
Official platform of JD Retail Technology, delivering insightful R&D news and a deep look into the lives and work of technologists.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.