Token导航 LogoToken导航

Agent Skill · 研究检索

axiom-objc-block-retain-cycles(axiom objc 块保留循环)

axiom-objc-block-retain-cycles 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

axiom-objc-block-retain-cycles 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 24,178。

总安装 4,080周安装 170GitHub Stars 873下载 1,360核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:axiom-objc-block-retain-cycles(axiom objc 块保留循环)
来源仓库:https://github.com/charleswiltgen/axiom
仓库路径:skills/axiom-objc-block-retain-cycles
安装命令:
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-objc-block-retain-cycles
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-objc-block-retain-cycles

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

专门识别 Objective-C 块(block)导致的保留循环问题,防止内存泄漏。适用于 UIViewController 无法释放、网络请求持续持有对象等典型症状排查。提供 weak-strong 模式应用准则与 Instruments 检测工具使用流程。

使用前应开启 Zombie Objects 或 Address Sanitizer 辅助定位可疑 block 引用。建议对异步回调、定时器闭包及 KVO 观察者注册处重点审查捕获上下文的方式。

来源仓库内路径:skills/axiom-objc-block-retain-cycles

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/charleswiltgen/axiom --skill axiom-objc-block-…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

axiom-objc-block-retain-cycles 怎么安装?

在终端执行 npx skills add https://github.com/charleswiltgen/axiom --skill axiom-objc-block-retain-cyc…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

axiom-objc-block-retain-cycles 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

axiom-objc-block-retain-cycles 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

axiom-objc-block-retain-cycles 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

axiom-objc-block-retain-cycles 用的人多吗?

公开数据是累计安装 4,080 次,近一周安装 170 次,下载 1,360 次,GitHub 873 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

axiom-objc-block-retain-cycles 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
axiom-objc-block-retain-cycles

axiom objc 块保留循环 · 本页

4,080873需要联网MIT通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

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):

// 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 suspected code location
// Debug > Debug Memory Graph
// Search for the view controller that should be deallocated
// Right-click > Show memory graph
// Look for arrows pointing back to self (the cycle)
// Record: "ViewController retained by: [operation/block/property]"

// 3. Check if block is assigned to self or self's properties
// Search for: setBlock:, completion:, handler:, callback:
// Check: Is the block stored in self.property?
// Check: Is the block passed to something that retains it (network operation)?
// Record: "Block assigned to: [property or operation]"

// 4. Search for self references in the block
// Look for: [self method], self.property, self-> access
// Look for HIDDEN self references:
//   - NSLog(@"Value: %@", self.property)
//   - NSAssert(self.isValid, @"message")
//   - Format strings: @"Name: %@", self.name
// Record: "self references found in block: [list]"

// Example output:
// Memory not returning to baseline ✓
// ViewController retained by: AFHTTPRequestOperation
// Operation retains: successBlock
// Block references self: [self updateUI], NSLog with self.property
// → DIAGNOSIS: Block retain cycle confirmed
What this tells you
  • Memory stays high → Leak confirmed, not false alarm
  • ViewController retained by operation → Block is the culprit
  • Block references self → Pattern: weak-strong needed
  • Hidden self in NSLog/NSAssert → Need to check ALL macro calls
  • No self references found → Maybe not a block cycle, investigate elsewhere
MANDATORY INTERPRETATION

Before changing ANY code, you must confirm ONE of these:

  1. If memory doesn't return to baseline AND ViewController still allocated → Block retain cycle exists
  2. If memory returns to baseline → Not a retain cycle, investigate other causes
  3. If cycle exists but you can't find self references → Check for hidden references (macros, indirect property access)
  4. If you find the cycle but don't understand the chain → Trace backward through retained objects in Memory Graph
If diagnostics are contradictory or unclear
  • STOP. Do NOT proceed to patterns yet
  • Add more diagnostics: Print the object graph, list retained objects
  • Ask: "If memory is low, why is the ViewController still allocated?"
  • Run Instruments > Leaks instrument if memory graph is confusing

Decision Tree

Block memory leak suspected?
├─ Memory stays high after dismiss?
│  ├─ YES
│  │  ├─ ViewController still allocated in Memory Graph?
│  │  │  ├─ YES → Proceed to patterns
│  │  │  └─ NO → Not a block cycle, check other leaks
│  │  └─ NO → Not a leak, normal memory usage
│  │
│  └─ Crash: "Sending message to deallocated instance"?
│     ├─ Happens in block/callback?
│     │  ├─ YES → Block captured weakSelf but it became nil
│     │  │  └─ Apply Pattern 4 (Guard condition is wrong or missing)
│     │  └─ NO → Different crash, not block-related
│     └─ Crash is timing-dependent (only on device)?
│        └─ YES → Weak reference timing issue, apply Pattern 2
│
├─ Block assigned to self or self.property?
│  ├─ YES → Apply Pattern 1 (weak-strong mandatory)
│  ├─ Assigned through network operation/timer/animation?
│  │  └─ YES → Apply Pattern 1 (operation retains block indirectly)
│  └─ Block called immediately (inline execution)?
│     ├─ YES → Optional to use weak-strong (no cycle possible)
│     │  └─ But recommend for consistency with other blocks
│     └─ NO → Block stored or passed to async method → Use Pattern 1
│
├─ Multiple nested blocks?
│  └─ YES → Apply Pattern 3 (must guard ALL nested blocks)
│
├─ Block contains NSAssert, NSLog, or string format with self?
│  └─ YES → Apply Pattern 2 (macro hides self reference)
│
└─ Implemented weak-strong pattern but still leaking?
   ├─ Check: Is weakSelf used EVERYWHERE?
   ├─ Check: No direct `self` references mixed in?
   ├─ Check: Nested blocks also guarded?
   └─ Check: No __unsafe_unretained used?

Common Patterns

Pattern Selection Rules (MANDATORY)

Apply ONE pattern at a time, in this order
  1. Always start with Pattern 1 (Weak-Strong Basics)

- If block assigned to self or self's properties → Pattern 1 - If block passed to operation/request that retains it → Pattern 1 - Only proceed to Pattern 2 if pattern still leaks

  1. Then Pattern 2 (Hidden self in Macros)

- Only if memory still leaks after applying Pattern 1 - Check for NSAssert, NSLog, string formatting - If found, apply Pattern 2

  1. Then Pattern 3 (Nested Blocks)

- Only if block has nested callbacks - Each nested block needs its own guard - If found, apply Pattern 3

  1. Then Pattern 4 (Guard Condition Edge Cases)

- Only if crash happens with weakSelf approach - Check guard condition is correct - Verify strongSelf used everywhere

FORBIDDEN
  • ❌ Applying multiple patterns at once
  • ❌ Skipping Pattern 1 because "I already know weak-strong"
  • ❌ Using __unsafe_unretained as workaround
  • ❌ Using strong self "just this once"
  • ❌ Rationalizing: "The block is too small for a leak"

Pattern 1: Weak-Strong Pattern (MANDATORY)

PRINCIPLE Any block that captures self must use weak-strong pattern if block is retained by self (directly or transitively).

❌ WRONG (Creates retain cycle)
[self.networkManager GET:@"url" success:^(id response) {
    self.data = response;  // self is retained by block
    [self updateUI];       // block is retained by operation
} failure:^(NSError *error) {
    [self handleError:error];  // CYCLE!
}];
✅ CORRECT (Breaks the cycle)
__weak typeof(self) weakSelf = self;
[self.networkManager GET:@"url" success:^(id response) {
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        strongSelf.data = response;
        [strongSelf updateUI];
    }
} failure:^(NSError *error) {
    __weak typeof(self) weakSelf2 = self;
    typeof(self) strongSelf = weakSelf2;
    if (strongSelf) {
        [strongSelf handleError:error];
    }
}];
Why this works
  1. __weak typeof(self) weakSelf = self; creates a weak reference outside the block
  2. Block captures weakSelf (weak reference), not self (strong reference)
  3. When block executes, convert to strongSelf (temporary strong ref)
  4. Check if strongSelf is nil (object was deallocated)
  5. Use strongSelf for the duration of the block
  6. strongSelf released when block exits → No cycle
Important details
  • Declare weakSelf OUTSIDE the block, not inside
  • Use typeof(self) for type safety (works in both ARC and non-ARC)
  • Guard condition MUST use if (strongSelf), not just declare it
  • Never use direct self inside the block once weakSelf is declared
  • Apply to EVERY block that captures self
  • ANY block that captures self must use weak-strong pattern

- This includes: [self method], self.property, self->ivar - Property access (self.property = value) captures self just like method calls

  • Blocks passed to frameworks:

- If framework documentation says 'block is called asynchronously' → Use weak-strong pattern (framework retains the block) - If framework documentation says 'block is called immediately' → Still safe to use weak-strong (better practice) - If unsure about framework behavior → Always use weak-strong (doesn't hurt)

Capturing variables (avoiding indirect self references)
// ✅ SAFE: Capture simple values extracted from self
__weak typeof(self) weakSelf = self;
[self.manager fetch:^(id response) {
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        NSString *name = strongSelf.name;  // Extract value
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Name: %@", name);  // Captured the STRING, not self
        });
    }
}];

// ❌ WRONG: Capture properties directly in nested blocks
__weak typeof(self) weakSelf = self;
[self.manager fetch:^(id response) {
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Name: %@", strongSelf.name);  // Captures strongSelf again!
        });
    }
}];

When nesting blocks, extract simple values first, then pass them to the inner block. This avoids creating an indirect capture of self through property access.

Time cost 30 seconds per block


Pattern 2: Hidden self in Macros

PRINCIPLE Macros like NSAssert, NSLog, and string formatting can secretly capture self. You must check them.

❌ WRONG (NSAssert captures self)
[self.button setTapAction:^{
    NSAssert(self.isValidState, @"State must be valid");  // self captured!
    [self doWork];  // Another self reference
}];
// Leak exists even though you think only [self doWork] captures self
✅ CORRECT (Check for hidden captures)
__weak typeof(self) weakSelf = self;
[self.button setTapAction:^{
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        // NSAssert still references self indirectly through strongSelf
        NSAssert(strongSelf.isValidState, @"State must be valid");
        [strongSelf doWork];
    }
}];
Common hidden self references
  • NSAssert(self.condition,...) → Use strongSelf instead
  • NSLog(@"Value: %@", self.property) → Use strongSelf.property
  • NSError *error = [NSError errorWithDomain:@"MyApp"...] → Safe, doesn't capture self
  • String formatting: @"Name: %@", self.name → Use strongSelf.name
  • Inline conditionals: self.flag? @"yes": @"no" → Use strongSelf.flag
How to find them
  1. Search block for all instances of self.
  2. Mark them: [self method], self.property, self->ivar
  3. Check if any are inside macro calls (NSAssert, NSLog, etc.)
  4. Replace with strongSelf

Time cost 1 minute per block to audit


Pattern 3: Nested Blocks (Each Needs Guard)

PRINCIPLE Nested blocks create a chain: outer block captures self, inner block captures outer block variable (which holds strongSelf), creating a new cycle. Each nested block needs its own weak-strong pattern.

❌ WRONG (Guarded outer block only)
__weak typeof(self) weakSelf = self;
[self.manager fetchData:^(NSArray *result) {
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        // Inner block captures strongSelf!
        [strongSelf.analytics trackEvent:@"Fetched"
                              completion:^{
            strongSelf.cachedData = result;  // Still strong reference!
            [strongSelf updateUI];
        }];
    }
}];
✅ CORRECT (Guard every nested block)
__weak typeof(self) weakSelf = self;
[self.manager fetchData:^(NSArray *result) {
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        // Declare new weak reference for inner block
        __weak typeof(strongSelf) weakSelf2 = strongSelf;

        [strongSelf.analytics trackEvent:@"Fetched"
                              completion:^{
            typeof(strongSelf) strongSelf2 = weakSelf2;
            if (strongSelf2) {
                strongSelf2.cachedData = result;
                [strongSelf2 updateUI];
            }
        }];
    }
}];
Why this works
  • Each nesting level needs its own weakSelf/strongSelf pair
  • Outer block: weakSelf → strongSelf
  • Inner block: weakSelf2 → strongSelf2
  • Each level is independent and safe
Important details
  • Don't reuse the same weakSelf variable in nested blocks
  • Each nesting level gets a new pair (weakSelf2, strongSelf2)
  • Guard condition MANDATORY for each level
  • Use consistent naming: weakSelf, weakSelf2, weakSelf3 (for readability)
Common nested block patterns that need Pattern 3
  • Completion handlers in callbacks
  • dispatch_async(queue, ^{...})
  • dispatch_after(time, queue, ^{...})
  • [NSTimer scheduledTimerWithTimeInterval:... block:^{...}]
  • [UIView animateWithDuration:... animations:^{...}]

Each of these is a block that might capture strongSelf, requiring its own weak-strong pattern.

Example with dispatch_async
__weak typeof(self) weakSelf = self;
[self.manager fetchData:^(NSArray *result) {
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        __weak typeof(strongSelf) weakSelf2 = strongSelf;

        dispatch_async(dispatch_get_main_queue(), ^{
            typeof(strongSelf) strongSelf2 = weakSelf2;
            if (strongSelf2) {
                strongSelf2.data = result;
                [strongSelf2 updateUI];
            }
        });
    }
}];

Time cost 1 minute per nesting level


Pattern 4: Guard Condition Edge Cases

PRINCIPLE The guard condition if (strongSelf) must be correct. Common mistakes: forgetting the guard, wrong condition, or mixing self and strongSelf.

❌ WRONG (Multiple guard failures)
__weak typeof(self) weakSelf = self;
[self.button setTapAction:^{
    typeof(self) strongSelf = weakSelf;
    // MISTAKE 1: Forgot guard condition
    self.counter++;  // CRASH! self is deallocated, accessing freed object

    // MISTAKE 2: Guard exists but used wrong variable
    if (weakSelf) {
        [weakSelf doWork];  // weakSelf is weak, might become nil again
    }

    // MISTAKE 3: Mixed self and strongSelf
    if (strongSelf) {
        self.flag = YES;  // Used self instead of strongSelf!
        [strongSelf doWork];
    }
}];
✅ CORRECT (Proper guard and consistent usage)
__weak typeof(self) weakSelf = self;
[self.button setTapAction:^{
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
        // CORRECT: Use strongSelf everywhere, never self
        strongSelf.counter++;
        strongSelf.flag = YES;
        [strongSelf doWork];
    }
    // If strongSelf is nil, entire block skips gracefully
}];
Why this works
  1. if (strongSelf) checks if object still exists
  2. If it does, strongSelf is a strong reference (safe)
  3. If it doesn't (object deallocated), block skips
  4. Using strongSelf everywhere prevents accidental self references
Critical rules (MANDATORY, no exceptions)
  • ✅ ALWAYS check if (strongSelf) before using it
  • ✅ ALWAYS use strongSelf inside the if block, NEVER direct self
  • ✅ strongSelf is guaranteed valid for the entire block scope
  • ❌ NEVER use if (!strongSelf) return; (confuses logic)
  • ❌ NEVER skip the guard to "save code"
  • ❌ NEVER mix weakSelf and strongSelf access
  • ❌ NEVER use strongSelf without guard (GUARANTEED crash)
What happens if you get it wrong
  • No guard: Crashes with "Sending message to deallocated instance"
  • Wrong condition: Object still deallocated, still crashes
  • Mixed self/strongSelf: One accidental self defeats entire pattern
  • Using strongSelf without guard: GUARANTEED crash when object is deallocated
Inside the guard
if (strongSelf) {
    strongSelf.data1 = value1;
    [strongSelf doWork1];
    [strongSelf doWork2];  // All safe
}
// ❌ WRONG: Using strongSelf after guard ends
strongSelf.data = value2;  // CRASH! Outside guard
What NOT to do
// ❌ FORBIDDEN: strongSelf without guard guarantees crash
typeof(self) strongSelf = weakSelf;
strongSelf.data = value;  // CRASH if weakSelf is nil!

// ✅ MANDATORY: Always guard before using strongSelf
if (strongSelf) {
    strongSelf.data = value;  // Safe
}

Time cost 10 seconds per block to verify guard is correct


Quick Reference Table

IssueCheckFix
Memory not returning to baselineDoes ViewController still exist in Memory Graph?Apply Pattern 1 (weak-strong)
Crash: "message to deallocated instance"Is guard condition missing or wrong?Apply Pattern 4 (correct guard)
Applied weak-strong but still leakingAre ALL self references using strongSelf?Check for mixed self/strongSelf
Block contains NSAssert or NSLogDo they reference self?Apply Pattern 2 (use strongSelf in macros)
Nested blocksIs weak-strong applied to EACH level?Apply Pattern 3 (guard every block)
Not sure if block creates cycleIs block assigned to self or self.property?If yes, apply Pattern 1

When You're Stuck After 30 Minutes

If you've spent >30 minutes and the leak still exists:

STOP. You either
  1. Skipped a mandatory diagnostic step (most common)
  2. Didn't apply weak-strong to ALL blocks (nested blocks missed)
  3. Have hidden self reference (NSAssert, NSLog, string format)
  4. Applied pattern but mixed in direct self references
  5. Have a different kind of leak (not block-related)
MANDATORY checklist before claiming "skill didn't work"
  • I ran all 4 diagnostic blocks (Allocations, Memory Graph, block search, self reference search)
  • I confirmed memory doesn't return to baseline in Instruments
  • I confirmed ViewController is still allocated (not deallocated)
  • I traced the retention chain (what's holding the ViewController?)
  • I found ALL blocks that capture self (global search: [self in the file)
  • I checked for hidden self references (NSAssert, NSLog, string formatting)
  • I applied weak-strong pattern to outer blocks
  • I applied weak-strong pattern to nested blocks (every nesting level)
  • I verified NO direct self references remain (only strongSelf)
  • I ran Instruments again and memory returned to baseline
  • I tested on real device, not just simulator
  • I cleared Xcode derived data between runs
If ALL boxes are checked and still leaking
  • You have a non-block leak (Core Data, timer, delegate, notification)
  • Use Instruments > Leaks instrument to identify the actual cycle
  • Profile for 2-3 minutes: open screen, close screen, repeat 5 times
  • Look at "Leaks" panel—it shows exactly what's not being released
  • Time cost: 15-30 minutes to identify the real culprit
If you identify it's NOT a block leak
  • Do not rationalize: "Maybe blocks are fine, I'll ship anyway"
  • Find the actual cycle (could be delegate, timer, property observer, notification)
  • Fix the real issue, not a false positive
Time cost transparency
  • Pattern 1: 30 seconds per block
  • Pattern 2: 1 minute per block (audit for hidden self)
  • Pattern 3: 1 minute per nesting level
  • Nested diagnostics if stuck: 15-30 minutes
  • Total for straightforward leak: 5-10 minutes

Common Mistakes

❌ Forgetting the guard condition

  • strongSelf.property = value; without if (strongSelf)
  • Crash when object is deallocated
  • Fix: ALWAYS use if (strongSelf) {...}

❌ Mixing self and strongSelf in same block

  • self.flag = YES; [strongSelf doWork];
  • One direct self reference defeats the entire pattern
  • Fix: ONLY use strongSelf inside the block

❌ Applying pattern to outer block only

  • Nested block still captures strongSelf strongly
  • Still leaks
  • Fix: Apply weak-strong to EVERY block

❌ Using __unsafe_unretained as "workaround"

  • ❌ FORBIDDEN pattern—unsafe and crashes
  • Creates crashes when object is deallocated
  • Not a solution, worse problem
  • Fix: Use weak-strong pattern instead

❌ Not checking for hidden self references

  • NSLog(@"Value: %@", self.property) in a block
  • Leak still exists even after applying weak-strong
  • Fix: Audit for NSAssert, NSLog, string formatting

❌ Rationalizing "it's a small leak"

  • Single block leak might be 100KB
  • After 50 screens, accumulates to 5MB
  • Eventually app crashes from memory pressure
  • Fix: Fix every block leak, don't rationalize

❌ Assuming blocks in system frameworks are safe

  • UIView animations, AFNetworking, dispatch, timers
  • ALL can retain blocks that reference self
  • Fix: Apply weak-strong pattern regardless of source

❌ Testing only in simulator

  • Simulator memory pressure is different
  • Leak might not appear until real device under load
  • Fix: Test on real device, oldest supported model

Real-World Impact

Before Block memory leak debugging 2-3 hours per issue

  • Run Allocations, not sure what to look at
  • Search everywhere, no clear diagnostic path
  • Try random fixes, hope one works
  • Ship anyway after sunk cost fallacy
  • Customer reports crashes or slowdown

After 5-10 minutes with systematic diagnosis

  • Run Allocations, confirm memory not returning to baseline
  • Memory Graph shows exactly what's retained
  • Find all blocks capturing self with global search
  • Apply weak-strong pattern (30 seconds per block)
  • Test in Instruments, memory returns to baseline
  • Done

Key insight Block retain cycles are 100% preventable with weak-strong pattern. There are no exceptions, no "special cases" where strong self is acceptable.


Last Updated: 2025-11-30 Status: TDD-tested with pressure scenarios Framework: Objective-C, blocks (closure), ARC

平台分布

Claude Code

30.3%
按下载量换算412

Codex

21.25%
按下载量换算289

OpenCode

17.84%
按下载量换算243

Antigravity

12.81%
按下载量换算174

Cursor

8.22%
按下载量换算112

windsurf

3.18%
按下载量换算43

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

继续浏览同类 Skills