14
14
#import " PFAssert.h"
15
15
#import " PFMacros.h"
16
16
#import " PFObject.h"
17
+ #import " PFObject+Subclass.h"
17
18
#import " PFObjectSubclassInfo.h"
18
19
#import " PFPropertyInfo_Private.h"
19
20
#import " PFPropertyInfo_Runtime.h"
@@ -108,17 +109,6 @@ - (instancetype)init {
108
109
return self;
109
110
}
110
111
111
- + (instancetype )defaultController {
112
- if (!defaultController_) {
113
- defaultController_ = [[PFObjectSubclassingController alloc ] init ];
114
- }
115
- return defaultController_;
116
- }
117
-
118
- + (void )clearDefaultController {
119
- defaultController_ = nil ;
120
- }
121
-
122
112
// /--------------------------------------
123
113
#pragma mark - Public
124
114
// /--------------------------------------
@@ -131,6 +121,33 @@ + (void)clearDefaultController {
131
121
return result;
132
122
}
133
123
124
+ - (void )scanForUnregisteredSubclasses : (BOOL )shouldSubscribe {
125
+ // NOTE: Potential race-condition here - if another thread dynamically loads a bundle, we may end up accidentally
126
+ // Skipping a bundle. Not entirely sure of the best solution to that here.
127
+ if (shouldSubscribe) {
128
+ @weakify (self);
129
+ [[NSNotificationCenter defaultCenter ] addObserverForName: NSBundleDidLoadNotification
130
+ object: nil
131
+ queue: nil
132
+ usingBlock: ^(NSNotification *note) {
133
+ @strongify (self);
134
+ [self _registerSubclassesInBundle: note.object];
135
+ }];
136
+ }
137
+ NSArray *bundles = [[NSBundle allFrameworks ] arrayByAddingObjectsFromArray: [NSBundle allBundles ]];
138
+ for (NSBundle *bundle in bundles) {
139
+ // Skip bundles that aren't loaded yet.
140
+ if (!bundle.loaded || !bundle.executablePath ) {
141
+ continue ;
142
+ }
143
+ // Filter out any system bundles
144
+ if ([[bundle bundlePath ] hasPrefix: @" /System/" ] || [[bundle bundlePath ] hasPrefix: @" /Library/" ]) {
145
+ continue ;
146
+ }
147
+ [self _registerSubclassesInBundle: bundle];
148
+ }
149
+ }
150
+
134
151
- (void )registerSubclass : (Class <PFSubclassing>)kls {
135
152
pf_sync_with_throw (_registeredSubclassesAccessQueue, ^{
136
153
[self _rawRegisterSubclass: kls];
@@ -324,4 +341,40 @@ - (void)_rawRegisterSubclass:(Class)kls {
324
341
_registeredSubclasses[[kls parseClassName ]] = subclassInfo;
325
342
}
326
343
344
+ - (void )_registerSubclassesInBundle : (NSBundle *)bundle {
345
+ PFConsistencyAssert (bundle.loaded , @" Cannot register subclasses in a bundle that hasn't been loaded!" );
346
+ dispatch_sync (_registeredSubclassesAccessQueue, ^{
347
+ Class pfObjectClass = [PFObject class ];
348
+ unsigned bundleClassCount = 0 ;
349
+
350
+ // NSBundle's executablePath does not resolve symlinks (sadface). Unfortunately, objc_copyClassNamesForImage()
351
+ // Only takes absolute paths, as it does a direct string compare. This causes issues when using a framework
352
+ // which uses the `Versions/XXX` format, vs. just having the binary be at the root of the `.framework` bundle.
353
+ NSString *realPath = [[bundle executablePath ] stringByResolvingSymlinksInPath ];
354
+ const char **classNames = objc_copyClassNamesForImage ([realPath UTF8String ], &bundleClassCount);
355
+ for (unsigned i = 0 ; i < bundleClassCount; i++) {
356
+ Class bundleClass = objc_getClass (classNames[i]);
357
+ // For obvious reasons, don't register the PFObject class.
358
+ if (bundleClass == pfObjectClass) {
359
+ continue ;
360
+ }
361
+ // NOTE: (richardross) Cannot use isSubclassOfClass here. Some classes may be part of a system bundle (even
362
+ // though we attempt to filter those out) that may be an internal class which doesn't inherit from NSObject.
363
+ // Scary, I know!
364
+ for (Class kls = bundleClass; kls != nil ; kls = class_getSuperclass (kls)) {
365
+ if (kls == pfObjectClass) {
366
+ // Do class_conformsToProtocol as late in the checking as possible, as its SUUUPER slow.
367
+ // Behind the scenes this is a strcmp (lolwut?)
368
+ if (class_conformsToProtocol (bundleClass, @protocol (PFSubclassing)) &&
369
+ !class_conformsToProtocol (bundleClass, @protocol (PFSubclassingSkipAutomaticRegistration))) {
370
+ [self _rawRegisterSubclass: bundleClass];
371
+ }
372
+ break ;
373
+ }
374
+ }
375
+ }
376
+ free (classNames);
377
+ });
378
+ }
379
+
327
380
@end
0 commit comments