This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-swift-arc-gc-retain-cycle-fencing

Kotlin Native ARC-to-GC Cross-Bridge Retain Cycle Fencing and Lifecycle Management

Enforces strict boundary contracts between Apple Automatic Reference Counting (ARC) and Kotlin/Native Concurrent Garbage Collector runtimes. Precludes hidden cross-heap retain cycles created when Swift closures or delegate instances cross the Objective-C bridging header into long-lived Kotlin coroutine scopes or class graphs.

Prohibited Anti-Pattern: Cross-Runtime Retain Cycles via Strong Swift Closures or Delegate Retention in Kotlin/Native Objects

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Native ARC-to-GC Cross-Bridge Retain Cycle Fencing and Lifecycle Management - Enforces strict boundary contracts between Apple Automatic Reference Counting (ARC) and Kotlin/Native Concurrent Garbage Collector runtimes. Precludes hidden cross-heap retain cycles created when Swift closures or delegate instances cross the Objective-C bridging header into long-lived Kotlin coroutine scopes or class graphs.
globs: iosApp/**/*.swift, shared/**/*.kt, **/*ViewModel.kt, **/*Presenter.kt, **/*Bridge.swift
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-swift-arc-gc-retain-cycle-fencing"
---

# Kotlin Native ARC-to-GC Cross-Bridge Retain Cycle Fencing and Lifecycle Management

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Cross-Runtime Retain Cycles via Strong Swift Closures or Delegate Retention in Kotlin/Native Objects

## System Constraints
You are a Principal Mobile Architect specializing in Kotlin Multiplatform (KMP) runtime internals, Objective-C/Swift interop bridging, and dual-runtime memory management. The target codebase bridges Apple's ARC (reference count increment/decrement) and Kotlin/Native's Concurrent Tracing Garbage Collector. You must enforce zero-tolerance policies for cross-heap retain cycles, ensuring that object references traversing the bridging header are isolated, explicitly scoped, and deterministically cleared.

## Strict Guidelines
- Engineers MUST ALWAYS implement an explicit `close()` or `clear()` lifecycle teardown method on Kotlin objects that expose coroutine flows, delegates, or callbacks to Swift.
- Engineers MUST NOT pass Swift `self` strongly into Kotlin callback closures or delegate interfaces without capturing `[weak self]` at the Swift call site.
- Code SHOULD wrap Swift listener interfaces in Kotlin using `WeakReference` or explicitly nullified delegate slots when bridge longevity outlives immediate view hierarchy frames.
- Services MUST cancel underlying Kotlin `CoroutineScope` instances deterministically when a consumer UI controller in Swift reaches `deinit` or SwiftUI `onDisappear`.
- Components NEVER store long-lived references to Swift-conforming protocol implementations inside global or singleton Kotlin Native objects without a mandatory unbinding mechanism.

## Bad vs Good Implementations
### Swift Interop Retain Cycle in Coroutine Subscription vs Explicit Teardown

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Kotlin wrapper holds a strong reference to the Swift closure, which strongly retains the UIViewController. The UIViewController strongly retains the Kotlin ViewModel wrapper. ARC and Kotlin/Native GC operate independently; neither can detect or collect this cross-runtime cycle, causing permanent memory leaks.
```swift
// Swift View Controller subscribing to Kotlin ViewModel Flow
final class ProfileViewController: UIViewController {
    private let viewModel = ProfileViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // CRITICAL BUG: Strong reference cycle across runtimes.
        // ProfileViewModel holds closure -> closure captures self strongly -> self holds viewModel.
        // ARC cannot decrement viewModel wrapper; Kotlin GC cannot release closure holding Swift self.
        viewModel.observeState { state in
            self.render(state: state)
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Weak capture `[weak self]` breaks the ARC back-reference, allowing the ViewController to deallocate. In `deinit`, explicit invocation of `stateWatcher.close()` and `viewModel.close()` cancels coroutines and severs the forward pointer from Kotlin Native's GC root set.
```swift
// Kotlin multiplatform interface defining deterministic disposal
// shared/src/commonMain/kotlin/ProfileViewModel.kt:
// class ProfileViewModel : Closeable {
//     fun observeState(onEach: (ProfileState) -> Unit): AutoCloseable
//     override fun close()
// }

final class ProfileViewController: UIViewController {
    private let viewModel = ProfileViewModel()
    private var stateWatcher: AutoCloseable?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Explicit weak self capture fends against ARC retention
        stateWatcher = viewModel.observeState { [weak self] state in
            guard let self = self else { return }
            self.render(state: state)
        }
    }
    
    deinit {
        // Deterministic cancellation tears down bridging reference in Kotlin GC
        stateWatcher?.close()
        viewModel.close()
    }
}
```

### Kotlin Native Delegate Storage: Strong Interface Bridge vs Nullable Disconnected Delegate

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Swift classes conforming to LocationListener generate Objective-C adapter objects retained indefinitely in Kotlin Native's heap array, keeping Swift objects alive via hidden ARC retain increments.
```kotlin
// shared/src/commonMain/kotlin/LocationTracker.kt
interface LocationListener {
    fun onLocationUpdate(lat: Double, lon: Double)
}

class LocationTracker {
    // ANTI-PATTERN: Strong retention of Obj-C/Swift bridge adapter in persistent engine
    private val listeners = mutableListOf<LocationListener>()
    
    fun addListener(listener: LocationListener) {
        listeners.add(listener)
    }
    // No removeListener or clear mechanism; Swift instances implementing LocationListener leak forever
}
```

#### Production Standard (Correct)
> **Standard**: Returns a deterministic `Subscription` handle allowing the Swift consumer to remove itself during lifecycle transitions, and provides `clearAll()` for component destruction.
```kotlin
// shared/src/commonMain/kotlin/LocationTracker.kt
interface LocationListener {
    fun onLocationUpdate(lat: Double, lon: Double)
}

interface Subscription {
    fun unsubscribe()
}

class LocationTracker {
    private val listeners = mutableSetOf<LocationListener>()
    
    fun subscribe(listener: LocationListener): Subscription {
        listeners.add(listener)
        return object : Subscription {
            override fun unsubscribe() {
                listeners.remove(listener)
            }
        }
    }
    
    fun clearAll() {
        listeners.clear()
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Kotlin Native ARC-to-GC Cross-Bridge Retain Cycle Fencing and Lifecycle Management

Kotlin Native ARC-to-GC Cross-Bridge Retain Cycle Fencing and Lifecycle Management defines an industrial-grade behavioral standard for mobile engineering. Enforces strict boundary contracts between Apple Automatic Reference Counting (ARC) and Kotlin/Native Concurrent Garbage Collector runtimes. Precludes hidden cross-heap retain cycles created when Swift closures or delegate instances cross the Objective-C bridging header into long-lived Kotlin coroutine scopes or class graphs. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Cross-Runtime Retain Cycles via Strong Swift Closures or Delegate Retention in Kotlin/Native Objects Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftCross-Runtime Retain Cycles via Strong Swift Closures or Delegate Retention in Kotlin/Native ObjectsCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS implement an explicit `close()` or `clear()` lifecycle teardown method on Kotlin objects that expose coroutine flows, delegates, or callbacks to Swift.

Engineers MUST ALWAYS implement an explicit `close()` or `clear()` lifecycle teardown method on Kotlin objects that expose coroutine flows, delegates, or callbacks to Swift.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NOT pass Swift `self` strongly into Kotlin callback closures or delegate interfaces without capturing `[weak self]` at the Swift call site.

Engineers MUST NOT pass Swift `self` strongly into Kotlin callback closures or delegate interfaces without capturing `[weak self]` at the Swift call site.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Code SHOULD wrap Swift listener interfaces in Kotlin using `WeakReference` or explicitly nullified delegate slots when bridge longevity outlives immediate view hierarchy frames.

Code SHOULD wrap Swift listener interfaces in Kotlin using `WeakReference` or explicitly nullified delegate slots when bridge longevity outlives immediate view hierarchy frames.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Services MUST cancel underlying Kotlin `CoroutineScope` instances deterministically when a consumer UI controller in Swift reaches `deinit` or SwiftUI `onDisappear`.

Services MUST cancel underlying Kotlin `CoroutineScope` instances deterministically when a consumer UI controller in Swift reaches `deinit` or SwiftUI `onDisappear`.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Components NEVER store long-lived references to Swift-conforming protocol implementations inside global or singleton Kotlin Native objects without a mandatory unbinding mechanism.

Components NEVER store long-lived references to Swift-conforming protocol implementations inside global or singleton Kotlin Native objects without a mandatory unbinding mechanism.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Kotlin Native ARC-to-GC Cross-Bridge Retain Cycle Fencing and Lifecycle Management critical for AI coding assistants?

Enforces strict boundary contracts between Apple Automatic Reference Counting (ARC) and Kotlin/Native Concurrent Garbage Collector runtimes. Precludes hidden cross-heap retain cycles created when Swift closures or delegate instances cross the Objective-C bridging header into long-lived Kotlin coroutine scopes or class graphs. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Cross-Runtime Retain Cycles via Strong Swift Closures or Delegate Retention in Kotlin/Native Objects) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

Kotlin