2323
2424NS_ASSUME_NONNULL_BEGIN
2525
26- @interface FIRComponentContainer () {
27- dispatch_queue_t _containerQueue;
28- }
26+ @interface FIRComponentContainer ()
2927
3028// / The dictionary of components that are registered for a particular app. The key is an NSString
3129// / of the protocol.
@@ -69,8 +67,6 @@ - (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)all
6967 _app = app;
7068 _cachedInstances = [NSMutableDictionary <NSString *, id > dictionary];
7169 _components = [NSMutableDictionary <NSString *, FIRComponentCreationBlock> dictionary];
72- _containerQueue =
73- dispatch_queue_create (" com.google.FirebaseComponentContainer" , DISPATCH_QUEUE_SERIAL);
7470
7571 [self populateComponentsFromRegisteredClasses: allRegistrants forApp: app];
7672 }
@@ -103,7 +99,10 @@ - (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(
10399 (component.instantiationTiming == FIRInstantiationTimingEagerInDefaultApp &&
104100 [app isDefaultApp ]);
105101 if (shouldInstantiateEager || shouldInstantiateDefaultEager) {
106- [self instantiateInstanceForProtocol: component.protocol withBlock: component.creationBlock];
102+ @synchronized (self) {
103+ [self instantiateInstanceForProtocol: component.protocol
104+ withBlock: component.creationBlock];
105+ }
107106 }
108107 }
109108 }
@@ -116,6 +115,8 @@ - (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(
116115// / - Call the block to create an instance if possible,
117116// / - Validate that the instance returned conforms to the protocol it claims to,
118117// / - Cache the instance if the block requests it
118+ // /
119+ // / Note that this method assumes the caller already has @sychronized on self.
119120- (nullable id )instantiateInstanceForProtocol : (Protocol *)protocol
120121 withBlock : (FIRComponentCreationBlock)creationBlock {
121122 if (!creationBlock) {
@@ -140,9 +141,7 @@ - (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
140141
141142 // The instance is ready to be returned, but check if it should be cached first before returning.
142143 if (shouldCache) {
143- dispatch_sync (_containerQueue, ^{
144- self.cachedInstances [protocolName] = instance;
145- });
144+ self.cachedInstances [protocolName] = instance;
146145 }
147146
148147 return instance;
@@ -153,47 +152,35 @@ - (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
153152- (nullable id )instanceForProtocol : (Protocol *)protocol {
154153 // Check if there is a cached instance, and return it if so.
155154 NSString *protocolName = NSStringFromProtocol (protocol);
156- __block id cachedInstance;
157- dispatch_sync (_containerQueue, ^{
158- cachedInstance = self.cachedInstances [protocolName];
159- });
160155
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+ }
163164 }
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;
168166}
169167
170168#pragma mark - Lifecycle
171169
172170- (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+ }
188179 }
189- }
190180
191- instancesCopy = nil ;
192-
193- // Empty the cache.
194- dispatch_sync (_containerQueue, ^{
181+ // Empty the cache.
195182 [self .cachedInstances removeAllObjects ];
196- });
183+ }
197184}
198185
199186@end
0 commit comments