Claude
Skills
Sign in
Back

Objective-C ARC Patterns

Included with Lifetime
$97 forever

Use when automatic Reference Counting in Objective-C including strong/weak references, retain cycles, ownership qualifiers, bridging with Core Foundation, and patterns for memory-safe code without manual retain/release.

General

What this skill does


# Objective-C ARC Patterns

## Introduction

Automatic Reference Counting (ARC) is Objective-C's memory management system
that automatically inserts retain and release calls at compile time. ARC
eliminates most manual memory management while providing deterministic memory
behavior and preventing common memory bugs like use-after-free and double-free.

Unlike garbage collection, ARC provides immediate deallocation when reference
counts reach zero, making it suitable for resource-constrained environments like
iOS. Understanding ARC's ownership rules, qualifiers, and patterns is essential
for writing memory-safe Objective-C code and avoiding retain cycles.

This skill covers strong and weak references, ownership qualifiers, retain
cycles, Core Foundation bridging, and best practices for ARC-based memory
management.

## Strong and Weak References

Strong references maintain ownership of objects and prevent deallocation, while
weak references observe objects without preventing deallocation.

```objectivec
// Strong references (default)
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray *friends;
@property (nonatomic, strong) UIImage *photo;
@end

@implementation Person
@end

// Weak references
@interface ViewController : UIViewController
@property (nonatomic, weak) id<ViewControllerDelegate> delegate;
@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
@end

@implementation ViewController
@end

@protocol ViewControllerDelegate <NSObject>
- (void)viewControllerDidFinish:(ViewController *)controller;
@end

// Using strong and weak
void strongWeakExample(void) {
    Person *person = [[Person alloc] init];
    person.name = @"Alice"; // Strong reference to NSString

    __weak Person *weakPerson = person; // Weak reference
    NSLog(@"Weak person: %@", weakPerson.name);

    person = nil; // Person deallocated
    NSLog(@"After nil: %@", weakPerson); // weakPerson is now nil
}

// Unowned references (unsafe_unretained)
@interface NodeOld : NSObject
@property (nonatomic, unsafe_unretained) NodeOld *parent;
@property (nonatomic, strong) NSArray<NodeOld *> *children;
@end

@implementation NodeOld
@end

// Weak vs unowned
@interface CommentOld : NSObject
@property (nonatomic, strong) NSString *text;
@property (nonatomic, weak) PostOld *post; // Weak: can be nil
@end

@interface PostOld : NSObject
@property (nonatomic, strong) NSArray<CommentOld *> *comments;
@end

@implementation CommentOld
@end

@implementation PostOld
@end

// Strong captures in blocks
void blockCaptureExample(void) {
    Person *person = [[Person alloc] init];
    person.name = @"Bob";

    // Strong capture
    void (^strongBlock)(void) = ^{
        NSLog(@"%@", person.name); // Captures person strongly
    };

    // Weak capture to avoid retain cycle
    __weak Person *weakPerson = person;
    void (^weakBlock)(void) = ^{
        NSLog(@"%@", weakPerson.name); // Captures weakly
    };

    strongBlock();
    weakBlock();
}
```

Strong references increment retain count, while weak references are
automatically set to nil when the object deallocates, preventing dangling
pointers.

## Retain Cycles and Breaking Them

Retain cycles occur when objects hold strong references to each other, preventing
deallocation. Breaking cycles requires weak or unowned references.

```objectivec
// Retain cycle example
@interface Parent : NSObject
@property (nonatomic, strong) NSArray<Child *> *children;
@end

@interface Child : NSObject
@property (nonatomic, weak) Parent *parent; // Weak to break cycle
@property (nonatomic, strong) NSString *name;
@end

@implementation Parent
- (void)dealloc {
    NSLog(@"Parent deallocated");
}
@end

@implementation Child
- (void)dealloc {
    NSLog(@"Child deallocated");
}
@end

void noCycleExample(void) {
    Parent *parent = [[Parent alloc] init];
    Child *child = [[Child alloc] init];
    child.name = @"Alice";
    child.parent = parent; // Weak reference

    parent.children = @[child]; // Strong reference
    // When parent goes out of scope, both deallocate
}

// Delegate pattern without cycles
@protocol DataSourceDelegate <NSObject>
- (void)dataSourceDidUpdate:(id)source;
@end

@interface DataSource : NSObject
@property (nonatomic, weak) id<DataSourceDelegate> delegate;
- (void)fetchData;
@end

@implementation DataSource
- (void)fetchData {
    // Fetch data
    [self.delegate dataSourceDidUpdate:self];
}

- (void)dealloc {
    NSLog(@"DataSource deallocated");
}
@end

// Block retain cycles
@interface NetworkManager : NSObject
@property (nonatomic, strong) NSString *baseURL;
- (void)fetchDataWithCompletion:(void (^)(NSData *data))completion;
@end

@implementation NetworkManager
- (void)fetchDataWithCompletion:(void (^)(NSData *))completion {
    // Simulate async work
    dispatch_async(dispatch_get_global_queue(
        DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *data = [@"response" dataUsingEncoding:NSUTF8StringEncoding];
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(data);
        });
    });
}

- (void)dealloc {
    NSLog(@"NetworkManager deallocated");
}
@end

// Avoiding block cycles with weak-strong dance
@interface ViewController2 : UIViewController
@property (nonatomic, strong) NetworkManager *networkManager;
@end

@implementation ViewController2
- (void)loadData {
    __weak typeof(self) weakSelf = self;
    [self.networkManager fetchDataWithCompletion:^(NSData *data) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return;

        // Use strongSelf safely
        NSLog(@"Data loaded: %@", strongSelf.view);
    }];
}

- (void)dealloc {
    NSLog(@"ViewController2 deallocated");
}
@end

// Observer pattern cycles
@interface Observable : NSObject
@property (nonatomic, strong) NSMutableArray *observers;
- (void)addObserver:(id)observer;
- (void)removeObserver:(id)observer;
- (void)notifyObservers;
@end

@implementation Observable
- (instancetype)init {
    self = [super init];
    if (self) {
        _observers = [NSMutableArray array];
    }
    return self;
}

- (void)addObserver:(id)observer {
    // Use NSPointerArray for weak references
    [self.observers addObject:[NSValue valueWithPointer:(__bridge void *)observer]];
}

- (void)removeObserver:(id)observer {
    [self.observers removeObject:[NSValue valueWithPointer:(__bridge void *)observer]];
}

- (void)notifyObservers {
    for (NSValue *value in self.observers) {
        id observer = (__bridge id)(void *)[value pointerValue];
        if (observer) {
            // Notify observer
        }
    }
}
@end
```

Always use weak references for delegates, parent pointers, and observers to
break retain cycles. Use the weak-strong dance in blocks for safe self access.

## Ownership Qualifiers

ARC provides ownership qualifiers that explicitly control memory management
behavior for variables and properties.

```objectivec
// Property ownership qualifiers
@interface Container : NSObject

// Strong: default for object pointers
@property (nonatomic, strong) id strongProperty;

// Weak: doesn't prevent deallocation
@property (nonatomic, weak) id weakProperty;

// Copy: creates a copy of the object
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSArray *items;

// Assign: for non-object types
@property (nonatomic, assign) NSInteger count;
@property (nonatomic, assign) CGFloat value;

// Unsafe_unretained: weak without nil-ing
@property (nonatomic, unsafe_unretained) id unsafeProperty;

@end

@implementation Container
@end

// Variable qualifiers
void qualifierExamples(void) {
    // __strong: default qualifier
    __strong NSString *strongString = @"Hello";

    // __weak: weak reference
    __weak NSString *weakString = strongString;

    // __unsafe_unretained: unmanaged weak
    __unsafe_unretained NSString *unsafeString = strongString;

    // __autoreleasing: for out parameters
    NSError * __autoreleasing error;
}

// Copy property be

Related in General