No Object Instantiation In Loops

ID

swift.no_objects_in_loops

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Efficiency

Language

Swift

Tags

efficiency, loops

Description

Reports a heavy object being instantiated on every iteration of an enclosing loop. Allocating a non-trivial type — JSONEncoder, JSONDecoder, Regex, NSRegularExpression, XMLParser, PropertyListSerialization, custom classes — inside a hot loop wastes CPU on construction and forces ARC / GC to clean up short-lived instances.

for s in items {
    let d = JSONDecoder()                 // FLAW
    out.append(try d.decode(T.self, from: s))
}

Rationale

Constructors that look cheap can do real work: parser state machines, backing buffer allocation, internal caches. When the same constructor runs N times in a tight loop, every iteration pays that fixed cost. The fix is almost always to allocate once and reuse, which is also clearer intent.

Remediation

Hoist the allocation out of the loop, or cache it on a static / module-level constant when the cost is per-process:

// 1) hoist
let d = JSONDecoder()
for s in items {
    out.append(try d.decode(T.self, from: s))
}

// 2) cache per process
private static let decoder = JSONDecoder()

When not to fire

The check is intentionally conservative and skips:

  • trivial value-type constructors (Int(), String(), Array(), Dictionary(), Set(), Bool(), primitive numeric kinds) — those are cheap and idiomatic;

  • Foundation formatter types — those are covered by the more focused swift.cache_date_formatters rule;

  • allocations nested inside a closure literal, function declaration, or initializer between the call and the enclosing loop — those run at most once per iteration (one-shot callback, lazy registration).