23
23
24
24
NS_ASSUME_NONNULL_BEGIN
25
25
26
- @interface FIRComponentContainer () {
27
- dispatch_queue_t _containerQueue;
28
- }
26
+ @interface FIRComponentContainer ()
29
27
30
28
// / The dictionary of components that are registered for a particular app. The key is an NSString
31
29
// / of the protocol.
@@ -69,8 +67,6 @@ - (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)all
69
67
_app = app;
70
68
_cachedInstances = [NSMutableDictionary <NSString *, id > dictionary];
71
69
_components = [NSMutableDictionary <NSString *, FIRComponentCreationBlock> dictionary];
72
- _containerQueue =
73
- dispatch_queue_create (" com.google.FirebaseComponentContainer" , DISPATCH_QUEUE_SERIAL);
74
70
75
71
[self populateComponentsFromRegisteredClasses: allRegistrants forApp: app];
76
72
}
@@ -103,7 +99,10 @@ - (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(
103
99
(component.instantiationTiming == FIRInstantiationTimingEagerInDefaultApp &&
104
100
[app isDefaultApp ]);
105
101
if (shouldInstantiateEager || shouldInstantiateDefaultEager) {
106
- [self instantiateInstanceForProtocol: component.protocol withBlock: component.creationBlock];
102
+ @synchronized (self) {
103
+ [self instantiateInstanceForProtocol: component.protocol
104
+ withBlock: component.creationBlock];
105
+ }
107
106
}
108
107
}
109
108
}
@@ -116,6 +115,8 @@ - (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(
116
115
// / - Call the block to create an instance if possible,
117
116
// / - Validate that the instance returned conforms to the protocol it claims to,
118
117
// / - Cache the instance if the block requests it
118
+ // /
119
+ // / Note that this method assumes the caller already has @sychronized on self.
119
120
- (nullable id )instantiateInstanceForProtocol : (Protocol *)protocol
120
121
withBlock : (FIRComponentCreationBlock)creationBlock {
121
122
if (!creationBlock) {
@@ -140,9 +141,7 @@ - (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
140
141
141
142
// The instance is ready to be returned, but check if it should be cached first before returning.
142
143
if (shouldCache) {
143
- dispatch_sync (_containerQueue, ^{
144
- self.cachedInstances [protocolName] = instance;
145
- });
144
+ self.cachedInstances [protocolName] = instance;
146
145
}
147
146
148
147
return instance;
@@ -153,47 +152,35 @@ - (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
153
152
- (nullable id )instanceForProtocol : (Protocol *)protocol {
154
153
// Check if there is a cached instance, and return it if so.
155
154
NSString *protocolName = NSStringFromProtocol (protocol);
156
- __block id cachedInstance;
157
- dispatch_sync (_containerQueue, ^{
158
- cachedInstance = self.cachedInstances [protocolName];
159
- });
160
155
161
- if (cachedInstance) {
162
- return cachedInstance;
156
+ id cachedInstance;
157
+ @synchronized (self) {
158
+ cachedInstance = self.cachedInstances [protocolName];
159
+ if (!cachedInstance) {
160
+ // Use the creation block to instantiate an instance and return it.
161
+ FIRComponentCreationBlock creationBlock = self.components [protocolName];
162
+ cachedInstance = [self instantiateInstanceForProtocol: protocol withBlock: creationBlock];
163
+ }
163
164
}
164
-
165
- // Use the creation block to instantiate an instance and return it.
166
- FIRComponentCreationBlock creationBlock = self.components [protocolName];
167
- return [self instantiateInstanceForProtocol: protocol withBlock: creationBlock];
165
+ return cachedInstance;
168
166
}
169
167
170
168
#pragma mark - Lifecycle
171
169
172
170
- (void )removeAllCachedInstances {
173
- // Loop through the cache and notify each instance that is a maintainer to clean up after itself.
174
- // Design note: we're getting a copy here, unlocking the cached instances, iterating over the
175
- // copy, then locking and removing all cached instances. A race condition *could* exist where a
176
- // new cached instance is created between the copy and the removal, but the chances are slim and
177
- // side-effects are significantly smaller than including the entire loop in the `dispatch_sync`
178
- // block (access to the cache from inside the block would deadlock and crash).
179
- __block NSDictionary <NSString *, id > *instancesCopy;
180
- dispatch_sync (_containerQueue, ^{
181
- instancesCopy = [self .cachedInstances copy ];
182
- });
183
-
184
- for (id instance in instancesCopy.allValues ) {
185
- if ([instance conformsToProtocol: @protocol (FIRComponentLifecycleMaintainer)] &&
186
- [instance respondsToSelector: @selector (appWillBeDeleted: )]) {
187
- [instance appWillBeDeleted: self .app];
171
+ @synchronized (self) {
172
+ // Loop through the cache and notify each instance that is a maintainer to clean up after
173
+ // itself.
174
+ for (id instance in self.cachedInstances .allValues ) {
175
+ if ([instance conformsToProtocol: @protocol (FIRComponentLifecycleMaintainer)] &&
176
+ [instance respondsToSelector: @selector (appWillBeDeleted: )]) {
177
+ [instance appWillBeDeleted: self .app];
178
+ }
188
179
}
189
- }
190
180
191
- instancesCopy = nil ;
192
-
193
- // Empty the cache.
194
- dispatch_sync (_containerQueue, ^{
181
+ // Empty the cache.
195
182
[self .cachedInstances removeAllObjects ];
196
- });
183
+ }
197
184
}
198
185
199
186
@end
0 commit comments