Root Cause Analysis of iOS Crash Caused by Method Swizzle on NSString
The app crashed on launch because a third‑party SDK swizzled NSString’s stringByAppendingString: with a method whose name conflicted with the app’s own swizzle, causing recursive calls through the __NSCFString class‑cluster and a stack overflow; renaming the methods with a unique prefix and excluding __NSCFString from swizzling resolves the issue.
Background: A third‑party SDK was integrated into a karaoke app, causing the app to crash on launch. The crash was traced to a method‑swizzling implementation that added protection code for NSString.
Initial investigation showed the app hanging then crashing with a stack trace indicating a recursive call of -[NSString stringByAppendingString:] . The method had been swizzled to a safe version, but due to naming conflict with the SDK, the swizzled method called itself repeatedly, leading to a stack overflow.
Further analysis revealed that the swizzle was applied not only to NSString and NSMutableString but also to the private class __NSCFString , which is part of the NSString class cluster. Because __NSCFString does not implement stringByAppendingString: itself, the runtime looked up the implementation in the superclass, which had already been swapped, creating a loop.
The article explains the concept of class clusters in Objective‑C and demonstrates how to inspect the private class using runtime methods such as _shortMethodDescription and _ivarDescription . Sample code snippets are shown for the swizzle implementation:
+ (void)swizzleStringByAppendingString:(Class)cls {
// create new IMP
IMP newImp = imp_implementationWithBlock(^id(id self, NSString *aString) {
if (aString != nil) {
return [self ksSafe_StringByAppendingString:aString];
} else {
return self;
}
});
class_addMethod(cls, @selector(ksSafe_StringByAppendingString:), newImp, "@@:");
[cls swizzleInstanceMethodWithOriginSEL:@selector(stringByAppendingString:)
swizzledSEL:@selector(ksSafe_StringByAppendingString:)];
} + (void)swizzleInstanceMethodWithOriginSEL:(SEL)originSEL swizzledSEL:(SEL)swizzledSEL {
Method originMethod = class_getInstanceMethod(self, originSEL);
Method swizzleMethod = class_getInstanceMethod(self, swizzledSEL);
[self swizzleMethodWithOriginSEL:originSEL originMethod:originMethod
swizzledSEL:swizzledSEL swizzleMethod:swizzleMethod class:self];
}The runtime logic first tries to add the new method; if the method already exists it exchanges implementations, otherwise it adds the method and then swaps.
Because __NSCFString inherited the swizzled implementation from its superclass, the added safe method and the original method formed a circular call chain, ultimately causing a stack overflow.
Solution: rename the app’s swizzle methods with a unique prefix (e.g., KS ) to avoid name collisions, and stop swizzling stringByAppendingString: on the private __NSCFString class. After these changes the app launches without crashing.
Take‑away: when extending system classes, always use a distinct prefix for category methods, and be cautious about swizzling methods on class‑cluster members, as they may not implement the selector directly.
Tencent Music Tech Team
Public account of Tencent Music's development team, focusing on technology sharing and communication.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.