Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/*
2
 * Copyright 2018 Google
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
#import "FirebaseCore/Sources/Private/FIRComponentContainer.h"
18
 
19
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
20
#import "FirebaseCore/Sources/Private/FIRComponent.h"
21
#import "FirebaseCore/Sources/Private/FIRLibrary.h"
22
#import "FirebaseCore/Sources/Private/FIRLogger.h"
23
 
24
NS_ASSUME_NONNULL_BEGIN
25
 
26
@interface FIRComponentContainer ()
27
 
28
/// The dictionary of components that are registered for a particular app. The key is an `NSString`
29
/// of the protocol.
30
@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRComponentCreationBlock> *components;
31
 
32
/// Cached instances of components that requested to be cached.
33
@property(nonatomic, strong) NSMutableDictionary<NSString *, id> *cachedInstances;
34
 
35
/// Protocols of components that have requested to be eagerly instantiated.
36
@property(nonatomic, strong, nullable) NSMutableArray<Protocol *> *eagerProtocolsToInstantiate;
37
 
38
@end
39
 
40
@implementation FIRComponentContainer
41
 
42
// Collection of all classes that register to provide components.
43
static NSMutableSet<Class> *sFIRComponentRegistrants;
44
 
45
#pragma mark - Public Registration
46
 
47
+ (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass {
48
  static dispatch_once_t onceToken;
49
  dispatch_once(&onceToken, ^{
50
    sFIRComponentRegistrants = [[NSMutableSet<Class> alloc] init];
51
  });
52
 
53
  [self registerAsComponentRegistrant:klass inSet:sFIRComponentRegistrants];
54
}
55
 
56
+ (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass
57
                                inSet:(NSMutableSet<Class> *)allRegistrants {
58
  [allRegistrants addObject:klass];
59
}
60
 
61
#pragma mark - Internal Initialization
62
 
63
- (instancetype)initWithApp:(FIRApp *)app {
64
  return [self initWithApp:app registrants:sFIRComponentRegistrants];
65
}
66
 
67
- (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)allRegistrants {
68
  self = [super init];
69
  if (self) {
70
    _app = app;
71
    _cachedInstances = [NSMutableDictionary<NSString *, id> dictionary];
72
    _components = [NSMutableDictionary<NSString *, FIRComponentCreationBlock> dictionary];
73
 
74
    [self populateComponentsFromRegisteredClasses:allRegistrants forApp:app];
75
  }
76
  return self;
77
}
78
 
79
- (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(FIRApp *)app {
80
  // Keep track of any components that need to eagerly instantiate after all components are added.
81
  self.eagerProtocolsToInstantiate = [[NSMutableArray alloc] init];
82
 
83
  // Loop through the verified component registrants and populate the components array.
84
  for (Class<FIRLibrary> klass in classes) {
85
    // Loop through all the components being registered and store them as appropriate.
86
    // Classes which do not provide functionality should use a dummy FIRComponentRegistrant
87
    // protocol.
88
    for (FIRComponent *component in [klass componentsToRegister]) {
89
      // Check if the component has been registered before, and error out if so.
90
      NSString *protocolName = NSStringFromProtocol(component.protocol);
91
      if (self.components[protocolName]) {
92
        FIRLogError(kFIRLoggerCore, @"I-COR000029",
93
                    @"Attempted to register protocol %@, but it already has an implementation.",
94
                    protocolName);
95
        continue;
96
      }
97
 
98
      // Store the creation block for later usage.
99
      self.components[protocolName] = component.creationBlock;
100
 
101
      // Queue any protocols that should be eagerly instantiated. Don't instantiate them yet
102
      // because they could depend on other components that haven't been added to the components
103
      // array yet.
104
      BOOL shouldInstantiateEager =
105
          (component.instantiationTiming == FIRInstantiationTimingAlwaysEager);
106
      BOOL shouldInstantiateDefaultEager =
107
          (component.instantiationTiming == FIRInstantiationTimingEagerInDefaultApp &&
108
           [app isDefaultApp]);
109
      if (shouldInstantiateEager || shouldInstantiateDefaultEager) {
110
        [self.eagerProtocolsToInstantiate addObject:component.protocol];
111
      }
112
    }
113
  }
114
}
115
 
116
#pragma mark - Instance Creation
117
 
118
- (void)instantiateEagerComponents {
119
  // After all components are registered, instantiate the ones that are requesting eager
120
  // instantiation.
121
  @synchronized(self) {
122
    for (Protocol *protocol in self.eagerProtocolsToInstantiate) {
123
      // Get an instance for the protocol, which will instantiate it since it couldn't have been
124
      // cached yet. Ignore the instance coming back since we don't need it.
125
      __unused id unusedInstance = [self instanceForProtocol:protocol];
126
    }
127
 
128
    // All eager instantiation is complete, clear the stored property now.
129
    self.eagerProtocolsToInstantiate = nil;
130
  }
131
}
132
 
133
/// Instantiate an instance of a class that conforms to the specified protocol.
134
/// This will:
135
///   - Call the block to create an instance if possible,
136
///   - Validate that the instance returned conforms to the protocol it claims to,
137
///   - Cache the instance if the block requests it
138
///
139
/// Note that this method assumes the caller already has @sychronized on self.
140
- (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
141
                                    withBlock:(FIRComponentCreationBlock)creationBlock {
142
  if (!creationBlock) {
143
    return nil;
144
  }
145
 
146
  // Create an instance using the creation block.
147
  BOOL shouldCache = NO;
148
  id instance = creationBlock(self, &shouldCache);
149
  if (!instance) {
150
    return nil;
151
  }
152
 
153
  // An instance was created, validate that it conforms to the protocol it claims to.
154
  NSString *protocolName = NSStringFromProtocol(protocol);
155
  if (![instance conformsToProtocol:protocol]) {
156
    FIRLogError(kFIRLoggerCore, @"I-COR000030",
157
                @"An instance conforming to %@ was requested, but the instance provided does not "
158
                @"conform to the protocol",
159
                protocolName);
160
  }
161
 
162
  // The instance is ready to be returned, but check if it should be cached first before returning.
163
  if (shouldCache) {
164
    self.cachedInstances[protocolName] = instance;
165
  }
166
 
167
  return instance;
168
}
169
 
170
#pragma mark - Internal Retrieval
171
 
172
- (nullable id)instanceForProtocol:(Protocol *)protocol {
173
  // Check if there is a cached instance, and return it if so.
174
  NSString *protocolName = NSStringFromProtocol(protocol);
175
 
176
  id cachedInstance;
177
  @synchronized(self) {
178
    cachedInstance = self.cachedInstances[protocolName];
179
    if (!cachedInstance) {
180
      // Use the creation block to instantiate an instance and return it.
181
      FIRComponentCreationBlock creationBlock = self.components[protocolName];
182
      cachedInstance = [self instantiateInstanceForProtocol:protocol withBlock:creationBlock];
183
    }
184
  }
185
  return cachedInstance;
186
}
187
 
188
#pragma mark - Lifecycle
189
 
190
- (void)removeAllCachedInstances {
191
  @synchronized(self) {
192
    // Loop through the cache and notify each instance that is a maintainer to clean up after
193
    // itself.
194
    for (id instance in self.cachedInstances.allValues) {
195
      if ([instance conformsToProtocol:@protocol(FIRComponentLifecycleMaintainer)] &&
196
          [instance respondsToSelector:@selector(appWillBeDeleted:)]) {
197
        [instance appWillBeDeleted:self.app];
198
      }
199
    }
200
 
201
    // Empty the cache.
202
    [self.cachedInstances removeAllObjects];
203
  }
204
}
205
 
206
- (void)removeAllComponents {
207
  @synchronized(self) {
208
    [self.components removeAllObjects];
209
  }
210
}
211
 
212
@end
213
 
214
NS_ASSUME_NONNULL_END