-
-
Notifications
You must be signed in to change notification settings - Fork 313
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Crash for Custom ViewController init on iOS 15 (#1361)
Swizzle all custom ViewControllers. First, fetch all subclasses of ViewController and then swizzle them. As there is no straightforward way to get all sub-classes in Objective-C, the code first retrieves all classes in the runtime, iterates over all classes, and checks for every class if UIViewController is a parent. Cause loading all classes can take a few milliseconds, do this on a background thread, which should be fine because the SDK swizzles the root view controller and its children. Previously, the code intercepted the ViewController initializers with swizzling to swizzle the lifecycle methods. This approach led to ViewControllers crashing when using a convenience initializer, see GH-1355. The error occurred because our swizzling logic adds the method to swizzle if the class doesn't implement it. It seems like adding an extra initializer causes problems with the rules for initialization in Swift, see https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID216. Fixes GH-1355
- Loading branch information
1 parent
74d66ff
commit 75e7281
Showing
14 changed files
with
325 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#import <Foundation/Foundation.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface SentrySubClassFinder : NSObject | ||
|
||
/** | ||
* Returns an array of subclasses of the parentClass. You shouldn't call this on the main thread as | ||
* this uses objc_getClassList internally, which can take up to 60 ms to complete. | ||
*/ | ||
+ (NSArray<Class> *)getSubclassesOf:(Class)parentClass; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#import "SentrySubClassFinder.h" | ||
#import "SentryLog.h" | ||
#import <Foundation/Foundation.h> | ||
#import <objc/runtime.h> | ||
|
||
@implementation SentrySubClassFinder | ||
|
||
+ (NSArray<Class> *)getSubclassesOf:(Class)parentClass | ||
{ | ||
int numClasses = objc_getClassList(NULL, 0); | ||
Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses); | ||
numClasses = objc_getClassList(classes, numClasses); | ||
|
||
NSMutableArray<Class> *result = [NSMutableArray new]; | ||
|
||
if (numClasses <= 0) { | ||
[SentryLog logWithMessage:@"No classes found when retrieving class list." | ||
andLevel:kSentryLevelError]; | ||
return result; | ||
} | ||
|
||
for (NSInteger i = 0; i < numClasses; i++) { | ||
Class superClass = classes[i]; | ||
do { | ||
superClass = class_getSuperclass(superClass); | ||
} while (superClass && superClass != parentClass); | ||
|
||
if (superClass != nil) { | ||
[result addObject:classes[i]]; | ||
} | ||
} | ||
|
||
free(classes); | ||
|
||
return result; | ||
} | ||
|
||
@end |
Oops, something went wrong.