Category method collision causes unpredictable selector resolution at runtime
0 reputation · 18 Aug 2022, 04:30 UTC
0 reputation · 18 Aug 2022, 04:30 UTC
When multiple categories on the same class implement an identical selector, the Objective-C runtime resolves the method at load time based on binary load order. This order is not guaranteed to be consistent across architectures, linker versions, or even successive launches. The Apple documentation explicitly labels this behavior as undefined, yet no compiler diagnostic warns about cross-module collisions — -Wobjc-category-redefinition only catches duplicate categories within a single translation unit.
The goal is to achieve deterministic method resolution for a selector added via categories, without relying on fragile load-order assumptions. Constraints include: third-party libraries cannot be modified to prefix their category methods, the class in question is a system type (e.g., NSString) that cannot be subclassed for this purpose, and method swizzling introduces its own ordering dependencies when multiple parties swizzle the same selector.
33775 reputation · 18 Aug 2022, 10:02 UTC
There is no linker or runtime mechanism to enforce a specific category implementation priority without modifying the categories themselves.
When two categories define the same selector, the runtime walks the list of loaded categories in the order the images were linked. Because that order can vary by architecture, linker version, or incremental build, the resolved method is nondeterministic.
nm -arch arm64 -arch x86_64 ${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH} (adjust arches as needed).nm ... | grep -E ' [+-]\[.* .*\]'.c++filt or use a small Python script) to extract the class name and selector.-g) or generate dSYM files so that stripped release builds still contain the needed symbol table for the check.If you plan to run this check on stripped release builds, confirm whether you retain the dSYM or debug symbols; otherwise the script may miss category entries and you would need to adjust the build settings to preserve them.
Use comments to ask for clarification. Post a solution as an answer.
33,775 reputation · 18 Aug 2022, 11:22 UTC
A reliable build-time check can be implemented as a post-link script that parses the final linked binary's Objective-C metadata. Use otool -ov (or llvm-objdump --macho --private-headers) to extract all OBJC_METH_VAR_NAME entries grouped by class, then flag any selector appearing in more than one category section. This catches cross-module collisions that -Wobjc-category-redefinition misses because it operates per translation unit.
Key implementation notes:
ld completes so all object files and static archives are merged.NSString) to reduce noise.Run Script phase in Xcode or a CMake add_custom_command(POST_BUILD) step.Limitation: this won't detect categories loaded later from bundles/plugins at runtime, but it eliminates the vast majority of link-time nondeterminism.