Understanding Objective‑C Blocks: Syntax, Usage, and Underlying Implementation
This article explains what Objective‑C blocks are, why they are useful, how to declare and use them in various contexts, and dives into the compiler‑generated data structures, copy/dispose mechanisms, __block semantics, memory‑management pitfalls, and reference‑cycle avoidance.
Understanding Objective‑C Blocks
Blocks are the Objective‑C implementation of closures. A block captures referenced local variables and the code that operates on them, allowing callbacks and deferred execution.
What is a Block?
A Block in Objective‑C is a closure that consists of a captured variable structure and a function pointer. It can be thought of as an anonymous function object.
What is a Block Used For?
Blocks enable optional parameter passing for callbacks, asynchronous tasks, delayed execution, and lifetime extension of objects.
Syntax
As a local variable: returnType (^blockName)(parameterTypes) = ^returnType(parameters) { ... }; // example void (^successBlock)(NSDictionary *params) = ^void(NSDictionary *params) { ... };
As a property: @property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes); // example @property (nonatomic, copy, nullable) void (^failBlock)(NSError *error);
As a method parameter: - (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName; // example - (void)URLRequestCompletion:(void (^ _Nullable)(NSDictionary *))successBlock { ... }
As a callback after a method call: [someObject someMethodThatTakesABlock:^returnType (parameters) { ... }]; // example [UIView animateWithDuration:0.5f animations:^(void){ ... }];
As a C function parameter: void SomeFunctionThatTakesABlock(returnType (^blockName)(parameterTypes)); // example void testCFunc(void (^successBlock)(char *name)) { ... }
As a type alias: typedef returnType (^TypeName)(parameterTypes); // example successBlock block = ^void(NSDictionary *params) { ... };
Typical Usage Scenarios
Delayed execution
Time‑consuming background tasks
Asynchronous operations
Extending the lifetime of an instance or object
Underlying Implementation
The compiler translates a block into a set of structs. The main structures are Block_descriptor_1 (size information) and Block_layout (isa pointer, flags, invoke function, and captured variables).
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size;
};
struct Block_layout {
void *isa;
volatile int32_t flags;
int32_t reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 *descriptor;
// captured variables follow
};When a block is created on the stack, its isa points to _NSConcreteStackBlock . If the block escapes the stack, Block_copy allocates heap memory, copies the contents, updates isa to _NSConcreteMallocBlock , and retains captured objects.
// Simplified Block_copy pseudo‑code
struct Block_layout *result = malloc(aBlock->descriptor->size);
memmove(result, aBlock, aBlock->descriptor->size);
result->isa = _NSConcreteMallocBlock;
_Block_call_copy_helper(result, aBlock);
return result;Copy and Dispose Helpers
Each block has generated copy and dispose functions that manage retained objects:
static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src) {
_Block_object_assign(&dst->number, src->number, BLOCK_FIELD_IS_OBJECT);
}
static void __main_block_dispose_0(struct __main_block_impl_0 *src) {
_Block_object_dispose(src->number, BLOCK_FIELD_IS_OBJECT);
}__block Modifier
The __block storage qualifier moves a captured variable from the stack to a heap‑allocated structure, allowing the block to modify it after copying. The generated struct contains forwarding pointers so that reads and writes always refer to the latest storage.
struct __Block_byref_a_0 {
void *__isa;
struct __Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a) = 12; // modify captured variable
}Memory‑Management Pitfalls
Blocks capture strong references by default, which can create retain cycles (e.g., a block stored in an object that also references the object). Break cycles by using __weak or weak references inside the block.
__weak typeof(self) weakSelf = self;
self.printName = ^(NSString *name) {
weakSelf.view.backgroundColor = [UIColor redColor];
NSLog(@"name is %@", name);
};Conclusion
Understanding the block syntax, the compiler‑generated data structures, and the memory‑management rules (copy, dispose, __block, __weak) is essential for writing safe and efficient iOS code.
References
https://blog.ibireme.com/2013/11/27/objc-block/
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
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.