I am trying to swizzle the backgroundColor property on UIView.
Before I swizzle, I do the following:
@implementation UIView (Cat1)
+(void)load {
NSArray *selectors = @[
//Highliter swizzling
NSStringFromSelector(@selector(setBackgroundColor:)),
NSStringFromSelector(@selector(backgroundColor))
];
[[self appearance] setBackgroundColor:[UIColor redColor]];
for (NSString *name in selectors) {
SEL originalSelector = NSSelectorFromString(name);
SEL swizzledSelector = NSSelectorFromString([NSString stringWithFormat:@"swizzled_%@",name]);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
class_addMethod(self,
originalSelector,
class_getMethodImplementation(self, originalSelector),
method_getTypeEncoding(originalMethod));
class_addMethod(self,
swizzledSelector,
class_getMethodImplementation(self, swizzledSelector),
method_getTypeEncoding(swizzledMethod));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
then, when the swizzled method is called, I don't do anything beside calling the original:
-(void)swizzled_setBackgroundColor:(UIColor *)color {
[self iio_swizzled_setBackgroundColor:color];
}
Then, the following crash occurs:2017-03-08 19:04:37.863 Develop-InsertViewer[69285:7916872] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Please file a radar on UIKit if you see this assertion.'
*** First throw call stack:
(
0 CoreFoundation 0x0000000108506e65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x0000000107c7ddeb objc_exception_throw + 48
2 CoreFoundation 0x0000000108506cca +[NSException raise:format:arguments:] + 106
3 Foundation 0x00000001078ca5a2 -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:] + 169
4 UIKit 0x0000000106227b35 PushNextClassForSettingIMP + 469
5 UIKit 0x000000010622810a TaggingAppearanceObjectSetterIMP + 37
6 InsertFramework 0x00000001071332be -[UIView(Cat1) swizzled_setBackgroundColor:] + 62
7 UIKit 0x0000000105d78fca -[UILabel _commonInit] + 238
8 UIKit 0x0000000105d7913d -[UILabel initWithFrame:] + 94
9 UIKit 0x0000000105b9cc4e -[UIView init] + 62
10 0x0000000105533300 _TTOFCSo7UILabelcfT_S_ + 16
11 0x0000000105532e44 _TFCSo7UILabelCfT_S_ + 68
12 0x0000000105569be3 _TFC12InsertViewer21StarterViewControllercfT5coderCSo7NSCoder_GSqS0__ + 51
13 0x0000000105569d2d _TToFC12InsertViewer21StarterViewControllercfT5coderCSo7NSCoder_GSqS0__ + 45
14 UIKit 0x0000000105ecb7db -[UIClassSwapper initWithCoder:] + 241
15 UIKit 0x000000010609f822 UINibDecoderDecodeObjectForValue + 705
16 UIKit 0x000000010609f558 -[UINibDecoder decodeObjectForKey:] + 278
17 UIKit 0x0000000105ecb4b1 -[UIRuntimeConnection initWithCoder:] + 180
18 UIKit 0x000000010609f822 UINibDecoderDecodeObjectForValue + 705
19 UIKit 0x000000010609f9e3 UINibDecoderDecodeObjectForValue + 1154
20 UIKit 0x000000010609f558 -[UINibDecoder decodeObjectForKey:] + 278
21 UIKit 0x0000000105eca6c3 -[UINib instantiateWithOwner:options:] + 1255
22 UIKit 0x0000000106232c40 -[UIStoryboard instantiateViewControllerWithIdentifier:] + 181
23 UIKit 0x0000000106232d93 -[UIStoryboard instantiateInitialViewController] + 69
24 UIKit 0x0000000105b0dfa6 -[UIApplication _loadMainStoryboardFileNamed:bundle:] + 94
25 UIKit 0x0000000105b0e2d6 -[UIApplication _loadMainInterfaceFile] + 260
26 UIKit 0x0000000105b0cb54 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1390
27 UIKit 0x0000000105b09e7b -[UIApplication workspaceDidEndTransaction:] + 188
28 FrontBoardServices 0x000000010b41e754 -[FBSSerialQueue _performNext] + 192
29 FrontBoardServices 0x000000010b41eac2 -[FBSSerialQueue _performNextFromRunLoopSource] + 45
30 CoreFoundation 0x0000000108432a31 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
31 CoreFoundation 0x000000010842895c __CFRunLoopDoSources0 + 556
32 CoreFoundation 0x0000000108427e13 __CFRunLoopRun + 867
33 CoreFoundation 0x0000000108427828 CFRunLoopRunSpecific + 488
34 UIKit 0x0000000105b097cd -[UIApplication _run] + 402
35 UIKit 0x0000000105b0e610 UIApplicationMain + 171
36 Develop-InsertViewer 0x00000001055ae752 main + 114
37 libdyld.dylib 0x00000001095a192d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
This also happens if I do the [self appearance] on other class, such as UIToolBar
Any idea??
The appearance system bottlenecks all the properties to a single implementation TaggingAppearanceObjectSetterIMP
. That implementation checks the name of the selector (using _UIAppearanceTagObjectForSelector
) and looks that up in an associated object cache (stored in __UIAppearanceCustomizedSelectorsAssociationKey
). If it's not there already, it calls _TagForSelectorWithAxes
to figure out what is mapped to that selector, and update the cache.
You stomped right into the middle of a fairly complicated piece of runtime trickery with your own runtime trickery. It's not shocking that this blew up.... :D
The basic problem, at a minimum, is that the name of the method must be setBackgroundColor
, not swizzled_setBackgroundColor
. You can work around that somewhat by swizzling in an actual implementation function, rather than a method. You can look at NSNotificationCenter+RNSwizzle.m
for an example of doing that by hand.
I would not be surprised if, after fixing that, you still ran into other subtle problems. My recommendation is (well, my recommendation is "never swizzle production code, it's way too fragile," but if you're ignoring that recommendation…) pull up the appearance code in Hopper and trace through the implementations so you can make sure everything lines up. It's not that complicated, and Hopper's pseudo-code mode will pretty much show you what's going on (it took me about 2 minutes to work out what I've posted here). That said, this is a very tricky piece of implementation detail, and it could easily change without warning (leading to new crashes). If you can do this without swizzling, I strongly recommend that.