Objective-C performance guide (v0)
Foundation collection, string, and logging checklists. Distinct from swiftperf.
Scope: Objective-C (ARC or MRC). Lint is a line-window scan of
.m/.h. Speedup claims cite a local bench, an upstream source, or are marked provisional. Corpus residual counts are lint hit counts, not wall-time savings.
Profile first (Instruments Time Profiler / Allocations). Measure on device or macOS before escalating.
Contents
1. Collections
[[NSMutableArray alloc] init] then many addObject: calls can resize repeatedly. Prefer arrayWithCapacity: when n is known. No lint rule covers this: the capacity rules were removed after a jitter-controlled retest showed no reliable speedup.
2. Strings and hot I/O
stringByAppendingString: in a loop allocates many immutable strings. Prefer NSMutableString. Keep NSLog out of tight loops (gate or use os_log carefully).
Lint: oc-nsstring-append-hot, oc-nslog-hot (default off).
3. Formatters, JSON, and regular expressions
[[NSDateFormatter alloc] init], [NSDateFormatter new], NSJSONSerialization (JSONObjectWithData / dataWithJSONObject), and NSRegularExpression (regularExpressionWithPattern) inside a loop repeat that setup on every iteration. Create the formatter or expression once, and parse or encode a payload once when the data allows it. These lints are hint. Each has a JSONL microbench and stays hint. They are not part of the 2026-10-22 corpus-ready verdict. They do not revive the removed array-capacity rules.
stringWithContentsOfFile: of one path was about 450× versus reading once. A path that changes every iteration cannot be hoisted.
[NSDate date] every iteration was about 769× versus one sample. A loop that needs a fresh timestamp has nothing to hoist. NSDateFormatter stays its own rule.
valueForKey: every iteration was about 124× versus reading the field. A name that is not a fixed field cannot be rewritten as a direct read. This is the same family as Go FieldByName.
Lint: oc-dateformatter-hot, oc-json-hot, oc-regex-hot, oc-file-read-hot, oc-clock-hot, oc-value-for-key-hot (default hint).
Lint map
| ID | Smell | Default |
|---|---|---|
| oc-nsstring-append-hot | stringByAppendingString in loop | off |
| oc-nslog-hot | NSLog in loop | off |
| oc-dateformatter-hot | date formatter alloc/init or new in a loop | hint (JSONL present; not corpus-triaged) |
| oc-json-hot | JSON parse or encode in a loop | hint (JSONL present; not corpus-triaged) |
| oc-regex-hot | regular expression compiled in a loop | hint (JSONL present; not corpus-triaged) |
Escalate only with evidence/oc-*.jsonl — see evidence-policy.md. The two array-capacity rules (OC001, OC004) were removed; see rule-ids.md. oc-dateformatter-hot, oc-json-hot, and oc-regex-hot have a JSONL microbench and stay hint. They are not part of the 2026-10-22 corpus-ready verdict.
References
- XPerf playbook
- swiftperf (sibling)