Function or variable defined in a header rather than declared
ID |
objectivec.correctness.definitions_in_headers |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Header Hygiene |
Language |
Objective-C |
Description
Reports a definition - a function with a body, or a variable with an initialiser - written at file scope in an Objective-C header. static, inline, NS_INLINE and extern forms are not reported, and neither is a plain declaration. The rule scans *.h only, because that is the only place the defect exists; a definition in the matching .m is exactly where it belongs.
Rationale
A header is textually pasted into every file that imports it, so a definition in one becomes a separate copy of the same external symbol in every translation unit that sees it. The second copy is a duplicate symbol link error, and because the header may well be imported by only one .m today, the build stays green until an unrelated file imports it months later and the failure surfaces far from the change that caused it. Precompiled headers and umbrella headers make that reach wider still. The trailing NSString *const kFoo = @"…"; in a public header is the usual Objective-C form of this.
The following code illustrates the pattern detected by this rule:
// FLAGGED: Function or variable defined in a header rather than declared
CGFloat XYCanvasAspectRatio(CGFloat width, CGFloat height) {
if (height == 0.0) {
return 0.0;
}
return width / height;
}
Remediation
Leave the declaration in the header and move the definition to the matching .m: extern NSString *const XYFoo; in the header, NSString *const XYFoo = @"…"; in the implementation, and a bare prototype instead of a function body. Where a small function must stay header-only for inlining, mark it static inline (or NS_INLINE), which gives each translation unit its own private copy instead of a competing external one.