Hi all,
I incurred into a change of behavior that is not documented, so I am not sure if it is an issue or it is expected.
In particular, calling class_addMethod started to return false for some UIKit related classes on iOS 14 Beta while on iOS <13.5 was returning true.
A tests that shows the change in behavior, that succeed on iOS 13.5 and fails on iOS 14 Beta is the following:
This seems, now, to swizzle wrongly methods of a subclass and in fact create an infinite recursive loop.
Any ideas?
I incurred into a change of behavior that is not documented, so I am not sure if it is an issue or it is expected.
In particular, calling class_addMethod started to return false for some UIKit related classes on iOS 14 Beta while on iOS <13.5 was returning true.
A tests that shows the change in behavior, that succeed on iOS 13.5 and fails on iOS 14 Beta is the following:
Code Block language @import ObjectiveC; @import UIKit; @import XCTest; static BOOL class_addMethodSuccedeed; static BOOL UINavigationBarDidMoveToWindowCalled; @interface TestCrashTests : XCTestCase @end @implementation TestCrashTests - (void)testClassAddMethod { XCTAssertTrue(class_addMethodSuccedeed); [[[UINavigationBar alloc] initWithFrame:CGRectZero] didMoveToWindow]; XCTAssertTrue(UINavigationBarDidMoveToWindowCalled); } + (void)swizzle:(Class)class methodName:(NSString*)methodName { SEL originalMethod = NSSelectorFromString(methodName); SEL newMethod = NSSelectorFromString([NSString stringWithFormat:@"%@%@", @"override_", methodName]); [self swizzle:class from:originalMethod to:newMethod]; } + (void)swizzle:(Class)class from:(SEL)original to:(SEL)new { Method originalMethod = class_getInstanceMethod(class, original); Method newMethod = class_getInstanceMethod(class, new); if (class_addMethod(class, original, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { class_replaceMethod(class, new, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); class_addMethodSuccedeed = YES; } else { method_exchangeImplementations(originalMethod, newMethod); } } + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self swizzle:[UINavigationBar class] methodName:@"didMoveToWindow"]; [self swizzle:[UIView class] methodName:@"didMoveToWindow"]; }); } @end @implementation UIView (Swizzle) - (void)override_didMoveToWindow { [self override_didMoveToWindow]; } @end @implementation UINavigationBar (Swizzle) - (void)override_didMoveToWindow { UINavigationBarDidMoveToWindowCalled = YES; [self override_didMoveToWindow]; } @end
This seems, now, to swizzle wrongly methods of a subclass and in fact create an infinite recursive loop.
Any ideas?
This is just because those classes have gained overrides of those methods. class_addMethod returns false if the class already contains a method with that selector, but it returns true if the method is merely inherited from a superclass. It's expected that framework classes may gain (or lose) methods in OS updates, so your code needs to be able to handle both ways.
That said, the code you posted here looks correct for both cases, and indeed when I try it out here it seems to work fine. If you're still having trouble and the above doesn't address it, let me know what I might try differently and I'll see if I can take another look.
That said, the code you posted here looks correct for both cases, and indeed when I try it out here it seems to work fine. If you're still having trouble and the above doesn't address it, let me know what I might try differently and I'll see if I can take another look.