Claude
Skills
Sign in
Back

Objective-C Blocks and GCD

Included with Lifetime
$97 forever

Use when blocks (closures) and Grand Central Dispatch in Objective-C for concurrent programming including block syntax, capture semantics, dispatch queues, dispatch groups, and patterns for thread-safe asynchronous code.

General

What this skill does


# Objective-C Blocks and GCD

## Introduction

Blocks are Objective-C's closure implementation, providing anonymous functions
that capture surrounding context. Grand Central Dispatch (GCD) is Apple's
low-level API for managing concurrent operations using dispatch queues rather
than threads directly.

Blocks enable functional programming patterns, callbacks, and clean asynchronous
API design. GCD simplifies concurrency by abstracting thread management into
queues that automatically distribute work across available CPU cores. Together,
they form the foundation for modern Objective-C concurrent programming.

This skill covers block syntax and semantics, capture behavior, GCD queues and
dispatch functions, synchronization primitives, and patterns for safe concurrent
code.

## Block Syntax and Usage

Blocks are first-class objects that encapsulate code and can capture variables
from their defining scope.

```objectivec
// Basic block syntax
void (^simpleBlock)(void) = ^{
    NSLog(@"Hello from block");
};
simpleBlock(); // Call block

// Block with parameters
int (^addBlock)(int, int) = ^(int a, int b) {
    return a + b;
};
int result = addBlock(5, 3); // 8

// Block with return type
NSString *(^greetBlock)(NSString *) = ^NSString *(NSString *name) {
    return [NSString stringWithFormat:@"Hello, %@", name];
};
NSString *greeting = greetBlock(@"Alice");

// Blocks as method parameters
- (void)fetchDataWithCompletion:
    (void (^)(NSData *data, NSError *error))completion {
    dispatch_async(dispatch_get_global_queue(
        DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Simulate network call
        NSData *data = [@"response" dataUsingEncoding:NSUTF8StringEncoding];

        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(data, nil);
            }
        });
    });
}

// Using block-based API
- (void)loadData {
    [self fetchDataWithCompletion:^(NSData *data, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error);
        } else {
            NSLog(@"Data: %@", data);
        }
    }];
}

// Typedef for block types
typedef void (^CompletionBlock)(BOOL success);
typedef void (^DataBlock)(NSData *data, NSError *error);
typedef NSString *(^TransformBlock)(NSString *input);

- (void)performOperationWithCompletion:(CompletionBlock)completion {
    // Async operation
    dispatch_async(dispatch_get_global_queue(
        DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Work
        BOOL success = YES;

        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(success);
            }
        });
    });
}

// Blocks in collections
NSArray *blocks = @[
    ^{ NSLog(@"Block 1"); },
    ^{ NSLog(@"Block 2"); },
    ^{ NSLog(@"Block 3"); }
];

for (void (^block)(void) in blocks) {
    block();
}

// Block properties
@interface AsyncOperation : NSObject
@property (nonatomic, copy) CompletionBlock completion;
@property (nonatomic, copy) DataBlock dataHandler;
@end

@implementation AsyncOperation
@end
```

Blocks must be copied when stored in properties or collections to move them from
stack to heap storage.

## Block Capture Semantics

Blocks capture variables from their defining scope, with different behaviors for
different storage types and qualifiers.

```objectivec
// Capturing local variables
void captureExample(void) {
    NSInteger x = 10;

    void (^block)(void) = ^{
        NSLog(@"x = %ld", (long)x); // Captures value of x
    };

    x = 20;
    block(); // Prints "x = 10" (captured at block creation)
}

// __block qualifier for mutable capture
void mutableCaptureExample(void) {
    __block NSInteger counter = 0;

    void (^incrementBlock)(void) = ^{
        counter++; // Can modify counter
    };

    incrementBlock();
    incrementBlock();
    NSLog(@"Counter: %ld", (long)counter); // 2
}

// Capturing self in methods
@interface Counter : NSObject
@property (nonatomic, assign) NSInteger count;
- (void)incrementAsync;
@end

@implementation Counter

- (void)incrementAsync {
    // Strong capture of self
    dispatch_async(dispatch_get_global_queue(
        DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        self.count++; // Captures self strongly
    });
}

@end

// Weak-strong dance for self
@interface ViewController : UIViewController
@property (nonatomic, strong) NSTimer *timer;
@end

@implementation ViewController

- (void)startTimer {
    __weak typeof(self) weakSelf = self;

    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                 repeats:YES
                                                   block:^(NSTimer *timer) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return;

        // Safe to use strongSelf
        [strongSelf updateUI];
    }];
}

- (void)updateUI {
    NSLog(@"Updating UI");
}

- (void)dealloc {
    [self.timer invalidate];
}

@end

// Capturing objects vs primitives
void objectCaptureExample(void) {
    NSMutableString *string = [NSMutableString stringWithString:@"Hello"];

    void (^block)(void) = ^{
        [string appendString:@" World"]; // Can mutate object
        NSLog(@"%@", string);
    };

    block(); // Prints "Hello World"
}

// Block retain cycles
@interface NetworkManager : NSObject
@property (nonatomic, copy) void (^completion)(NSData *data);
@end

@implementation NetworkManager

- (void)fetchData {
    __weak typeof(self) weakSelf = self;

    self.completion = ^(NSData *data) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return;

        [strongSelf processData:data];
    };
}

- (void)processData:(NSData *)data {
    NSLog(@"Processing: %@", data);
}

@end

// Capturing __block objects
void blockObjectExample(void) {
    __block NSMutableArray *array = [NSMutableArray array];

    void (^addBlock)(id) = ^(id object) {
        [array addObject:object]; // Can mutate and reassign
    };

    addBlock(@"Item 1");
    addBlock(@"Item 2");

    array = [NSMutableArray array]; // Can reassign
}
```

Use `__weak` to avoid retain cycles when capturing self, and `__block` to allow
mutation of captured variables.

## Dispatch Queues

GCD uses dispatch queues to manage concurrent execution, with serial queues
executing tasks sequentially and concurrent queues executing them in parallel.

```objectivec
// Main queue (serial, main thread)
dispatch_async(dispatch_get_main_queue(), ^{
    // Update UI
    NSLog(@"On main thread");
});

// Global concurrent queues
dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(
    DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t defaultQueue = dispatch_get_global_queue(
    DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t lowPriorityQueue = dispatch_get_global_queue(
    DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(
    DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

// Async execution on global queue
dispatch_async(defaultQueue, ^{
    // Background work
    NSLog(@"Background work");

    dispatch_async(dispatch_get_main_queue(), ^{
        // Update UI on main queue
        NSLog(@"UI update");
    });
});

// Custom serial queue
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);

dispatch_async(serialQueue, ^{
    NSLog(@"Task 1");
});

dispatch_async(serialQueue, ^{
    NSLog(@"Task 2");
});

// Custom concurrent queue
dispatch_queue_t concurrentQueue = dispatch_queue_create(
    "com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(concurrentQueue, ^{
    NSLog(@"Concurrent task 1");
});

dispatch_async(concurrentQueue, ^{
    NSLog(@"Concurrent task 2");
});

// Synchronous dispatch (blocks until complete)
__block NSString *result;
dispatch_sync(serialQueue, ^{
    result = @"Computed value";
});
NSLog(@"Result: %@", result);

// Dispatch after (delayed execution)
dispatch_after(dispatch_time(DISPA

Related in General