Use when debugging memory leaks from blocks, blocks assigned to self or properties, network callbacks, or crashes from deallocated objects - systematic weak-strong pattern diagnosis with mandatory diagnostic rules
View on GitHubCharlesWiltgen/Axiom
axiom
.claude-plugin/plugins/axiom/skills/axiom-objc-block-retain-cycles/SKILL.md
January 16, 2026
Select agents to install to:
npx add-skill https://github.com/CharlesWiltgen/Axiom/blob/main/.claude-plugin/plugins/axiom/skills/axiom-objc-block-retain-cycles/SKILL.md -a claude-code --skill axiom-objc-block-retain-cyclesInstallation paths:
.claude/skills/axiom-objc-block-retain-cycles/# Objective-C Block Retain Cycles ## Overview Block retain cycles are the #1 cause of Objective-C memory leaks. When a block captures `self` and is stored on that same object (directly or indirectly through an operation/request), you create a circular reference: self → block → self. **Core principle** 90% of block memory leaks stem from missing or incorrectly applied weak-strong patterns, not genuine Apple framework bugs. ## Red Flags — Suspect Block Retain Cycle If you see ANY of these, suspect a block retain cycle, not something else: - Memory grows steadily over time during normal app use - UIViewController instances not deallocating (verified in Instruments) - Crash: "Sending message to deallocated instance" from network/async callback - Network requests or animations prevent view controller from closing - Weak reference becomes nil unexpectedly in a block - NSLog, NSAssert, or string formatting hiding self references - Completion handler fires after the view controller "should be gone" - ❌ **FORBIDDEN** Rationalizing as "It's probably normal memory usage" - Memory leaks are never "normal" - Apps should return to baseline memory after user dismisses a screen - Do not rationalize this as "good enough" or "monitor it later" **Critical distinction** Block retain cycles accumulate silently. A single cycle might be 100KB, but after 50 screens viewed, you have 5MB of dead memory. **MANDATORY: Test on real device (oldest supported model) after fixes, not just simulator.** ## Mandatory First Steps **ALWAYS run these FIRST** (before changing code): ```objc // 1. Identify the leak with Allocations instrument // In Xcode: Xcode > Open Developer Tool > Instruments // Choose Allocations template // Perform an action (open/close a screen with the suspected block) // Check if memory doesn't return to baseline // Record: "Memory baseline: X MB, after action: Y MB, still allocated: Z objects" // 2. Use Memory Debugger to trace the cycle // Run app, pause at suspec