/** Tell the promise is pending or not. */ @property(nonatomic, readonly) BOOL isPending;
/** Tell the promise is fulfilled or not. */ @property(nonatomic, readonly) BOOL isFulfilled;
/** Tell the promise is rejected or not. */ @property(nonatomic, readonly) BOOL isRejected;
/** If fulfilled, value store into this property. */ @property(nonatomic, readonly, nullable) Value value;
/** If reject, error store into this property */ @property(nonatomic, readonly, nullable) NSError *error;
关键构造方法:
/** Create a promise without constructor. Which means, you should control when the job begins.
@return The `COPromise` instance */ + (instancetype)promise; /** Create a promise with constructor. the job begans when someone observing on it.
@param constructor the constructor block. @return The `COPromise` instance */ + (instancetype)promise:(COPromiseConstructor)constructor; /** Create a promise with constructor. the job begans when someone observing on it. @param constructor the constructor block. @param queue the dispatch_queue_t that the job run. @return The `COPromise` instance */ + (instancetype)promise:(COPromiseConstructor)constructor onQueue:(dispatch_queue_t _Nullable )queue;
关键方法:
/** Fulfill the promise with a return value. @param value the value fulfilled. */ - (void)fulfill:(nullable Value)value;
/** Reject the promise with a error @param error the error. */ - (void)reject:(NSError * _Nullable)error;
/** Cancel the job. @discussion If you want a `COPromise` be cancellable, you must make the job cancel in `onCancel:`. */ - (void)cancel;
/** Set the onCancelBlock. @param onCancelBlock will execute on the promise cancelled. */ - (void)onCancel:(COPromiseOnCancelBlock _Nullable )onCancelBlock;
/** Chained observe the promise fulfilled. @param work the observer worker. @return The chained promise instance. */ - (COPromise *)then:(COPromiseThenWorkBlock)work;
/** Observe the promises rejected. @param reject the reject dealing worker. @return The chained promise instance. */ - (COPromise *)catch:(COPromiseCatchWorkBlock)reject;
/** Tell if the error is promise cancelled error @param error the error object @return is cancellled error. */ + (BOOL)isPromiseCancelled:(NSError *)error;
objection 是一个iOS / MacOS X 平台上的一个基于注释的依赖注入库,能够支持类方式,协议方式,实例对象方式,名称方式绑定,如果接触过Android开发大家可能会比较熟悉Butterknife,两者的功能是一致的,确切地说两者在很多特性上也都存在很多共同点:
我们来看下objection有哪些特性:
* "Annotation"Based Dependency Injection * Seamless support for integrating custom and external dependencies Custom Object Providers Meta Class Bindings Protocol Bindings Instance Bindings Named Bindings * Lazily instantiates dependencies * Eager Singletons * Initializer Support Default and custom arguments
it(@"correctly builds a registered object", ^{ id engine = [[JSObjectiondefaultInjector] getObject:[Engineclass]]; assertThat(engine, isNot(nilValue())); });
it(@"will auto register a class if it is not explicitly registered", ^{ UnregisteredCar *unregisteredCar = [[JSObjectiondefaultInjector] getObject:[UnregisteredCarclass]]; assertThat(unregisteredCar, is(notNilValue())); assertThat(unregisteredCar.engine, is(notNilValue())); });
每次注入的时候都会执行awakeFromObjection
@implementationCar objection_register(Car)
@synthesize engine, brakes, awake;
- (void)awakeFromObjection { awake = YES; }
it(@"calls awakeFromObjection when injecting dependencies into properties of an existing instance", ^{ Car *car = [[Caralloc] init]; [[JSObjectiondefaultInjector] injectDependencies:car]; assertThatBool([carawake], isTrue()); assertThatBool([car.engineawake], isTrue()); });
it(@"can bind a class in singleton scope", ^{ assertThat(injector[[Car class]], is(sameInstance(injector[[Car class]]))); });
it(@"can bind a class in a normal scope", ^{ assertThat(injector[[VisaCCProcessor class]], isNot(sameInstance(injector[[VisaCCProcessor class]]))); });
- (void)initializeEagerSingletons { for (NSString*eagerSingletonKey in _eagerSingletons) { id entry = [_context objectForKey:eagerSingletonKey] ?: [_globalContext objectForKey:eagerSingletonKey]; if ([entry lifeCycle] ==JSObjectionScopeSingleton) { //创建eagerSingletonKey对象 [self getObject:NSClassFromString(eagerSingletonKey)]; } else { @throw [NSException exceptionWithName:@"JSObjectionException" reason:[NSString stringWithFormat:@"Unable to initialize eager singleton for the class '%@' because it was never registered as a singleton", eagerSingletonKey] userInfo:nil]; } } }
// collect items that have changed since the last update NSMutableSet *updatedObjects = [NSMutableSet new];
//遍历从数据源获取到的每个数据,注意这里的object其实是一个数据集,每个数据集针对一个sessionController for (id object in objects) { // 查看是否之前已经创建了sectionController,如果有就直接使用 IGListSectionController *sectionController = [map sectionControllerForObject:object];
//如果再没有直接报错 if (sectionController == nil) { IGLKLog(@"WARNING: Ignoring nil section controller returned by data source %@ for object %@.", dataSource, object); continue; }
// object 与 sessionController之间的关系已经建立,这时候可以认为sessionController已经加载完毕,加载完成后sessionController会收到didUpdateToObject的通知 for (id object in updatedObjects) { [[map sectionControllerForObject:object] didUpdateToObject:object]; }
//计算总数量 NSInteger itemCount = 0; for (IGListSectionController *sectionController in sectionControllers) { itemCount += [sectionController numberOfItems]; }
- (void)_updateBackgroundViewShouldHide:(BOOL)shouldHide { //如果还在更新那么先返回,在update block执行结束之后还会调用 if (self.isInUpdateBlock) { return; } //根据itemCount数量决定是否显示空白页面 UIView *backgroundView = [self.dataSource emptyViewForListAdapter:self]; // don't do anything if the client is using the same view if (backgroundView != _collectionView.backgroundView) { // collection view will just stack the background views underneath each other if we do not remove the previous // one first. also fine if it is nil [_collectionView.backgroundView removeFromSuperview]; _collectionView.backgroundView = backgroundView; } _collectionView.backgroundView.hidden = shouldHide; }
staticBOOLisInterceptedSelector(SEL sel) { return ( // UIScrollViewDelegate sel == @selector(scrollViewDidScroll:) || sel == @selector(scrollViewWillBeginDragging:) || sel == @selector(scrollViewDidEndDragging:willDecelerate:) || sel == @selector(scrollViewDidEndDecelerating:) || // UICollectionViewDelegate sel == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) || sel == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) || sel == @selector(collectionView:didSelectItemAtIndexPath:) || sel == @selector(collectionView:didDeselectItemAtIndexPath:) || sel == @selector(collectionView:didHighlightItemAtIndexPath:) || sel == @selector(collectionView:didUnhighlightItemAtIndexPath:) || // UICollectionViewDelegateFlowLayout sel == @selector(collectionView:layout:sizeForItemAtIndexPath:) || sel == @selector(collectionView:layout:insetForSectionAtIndex:) || sel == @selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:) || sel == @selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:) || sel == @selector(collectionView:layout:referenceSizeForFooterInSection:) || sel == @selector(collectionView:layout:referenceSizeForHeaderInSection:) || // IGListCollectionViewDelegateLayout sel == @selector(collectionView:layout:customizedInitialLayoutAttributes:atIndexPath:) || sel == @selector(collectionView:layout:customizedFinalLayoutAttributes:atIndexPath:) ); }
IGListSectionController*sectionController = [self sectionControllerForView:view]; // if the section controller relationship was destroyed, reconnect it // this happens with iOS 10 UICollectionView display range changes if (sectionController ==nil) { sectionController = [self.sectionMap sectionControllerForSection:indexPath.section]; [self mapView:view toSectionController:sectionController]; }
- (void)_updateWorkingRangesWithListAdapter:(IGListAdapter *)listAdapter { IGAssertMainThread(); // This method is optimized C++ to improve straight-line speed of these operations. Change at your peril. // We use a std::set because it is ordered. std::set<NSInteger> visibleSectionSet = std::set<NSInteger>(); for (const _IGListWorkingRangeHandlerIndexPath &indexPath : _visibleSectionIndices) { visibleSectionSet.insert(indexPath.section); }
//根据workingRangeSize计算workingRange的start 和 end NSInteger start; NSInteger end; if (visibleSectionSet.size() == 0) { // We're now devoid of any visible sections. Bail start = 0; end = 0; } else { start = MAX(*visibleSectionSet.begin() - _workingRangeSize, 0); end = MIN(*visibleSectionSet.rbegin() + 1 + _workingRangeSize, (NSInteger)listAdapter.objects.count); }
// Build the current set of working range section controllers // 构建新的可见sessionController 集合 _IGListWorkingRangeSectionControllerSet workingRangeSectionControllers (visibleSectionSet.size()); for (NSInteger idx = start; idx < end; idx++) { //取出工作区中的sessionController id item = [listAdapter objectAtSection:idx]; IGListSectionController *sectionController = [listAdapter sectionControllerForObject:item]; workingRangeSectionControllers.insert({sectionController}); }
//工作区不能大于1000 IGAssert(workingRangeSectionControllers.size() < 1000, @"This algorithm is way too slow with so many objects:%lu", workingRangeSectionControllers.size());
// Tell any new section controllers that they have entered the working range // 从新的可见sessionController 集合中遍历每个元素看下是否在旧的存在,如果在旧的不存在 说明这个是新进入工作区的通知对应的sessionController for (const _IGListWorkingRangeHandlerSectionControllerWrapper &wrapper : workingRangeSectionControllers) { // Check if the item exists in the old working range item array. auto it = _workingRangeSectionControllers.find(wrapper); if (it == _workingRangeSectionControllers.end()) { // The section controller isn't in the existing list, so it's new. id <IGListWorkingRangeDelegate> workingRangeDelegate = wrapper.sectionController.workingRangeDelegate; [workingRangeDelegate listAdapter:listAdapter sectionControllerWillEnterWorkingRange:wrapper.sectionController]; } }
// 同理处理退出工作区的事件 // Tell any removed section controllers that they have exited the working range for (const _IGListWorkingRangeHandlerSectionControllerWrapper &wrapper : _workingRangeSectionControllers) { // Check if the item exists in the new list of section controllers auto it = workingRangeSectionControllers.find(wrapper); if (it == workingRangeSectionControllers.end()) { // If the item does not exist in the new list, then it's been removed. id <IGListWorkingRangeDelegate> workingRangeDelegate = wrapper.sectionController.workingRangeDelegate; [workingRangeDelegate listAdapter:listAdapter sectionControllerDidExitWorkingRange:wrapper.sectionController]; } }
// item updates must not send mutations to the collection view while we are reloading // 将状态设置为正在加载数据 self.state = IGListBatchUpdateStateExecutingBatchUpdateBlock;
//调用_updateObjects 重建object 和 sessionController之间的关系 if (reloadUpdates) { reloadUpdates(); }
// 执行批量更新中的单个更新block // execute all stored item update blocks even if we are just calling reloadData. the actual collection view // mutations will be discarded, but clients are encouraged to put their actual /data/ mutations inside the // update block as well, so if we don't execute the block the changes will never happen for (IGListItemUpdateBlock itemUpdateBlock in batchUpdates.itemUpdateBlocks) { itemUpdateBlock(); } [completionBlocks addObjectsFromArray:batchUpdates.itemCompletionBlocks]; self.state = IGListBatchUpdateStateExecutedBatchUpdateBlock; //将状态设置为批量更新执行完毕 [self _cleanStateAfterUpdates]; //通知外部当前的UICollectionView即将开始加载数据 [delegate listAdapterUpdater:self willReloadDataWithCollectionView:collectionView]; //加载数据 [collectionView reloadData]; [collectionView.collectionViewLayout invalidateLayout]; [collectionView layoutIfNeeded]; //通知外部当前的UICollectionView已经加载完数据 [delegate listAdapterUpdater:self didReloadDataWithCollectionView:collectionView];
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { IGListSectionController * sectionController = [self sectionControllerForSection:section]; IGAssert(sectionController != nil, @"Nil section controller for section %li for item %@. Check your -diffIdentifier and -isEqual: implementations.", (long)section, [self.sectionMap objectForSection:section]); constNSInteger numberOfItems = [sectionController numberOfItems]; IGAssert(numberOfItems >= 0, @"Cannot return negative number of items %li for section controller %@.", (long)numberOfItems, sectionController); return numberOfItems; }
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { id<IGListAdapterPerformanceDelegate> performanceDelegate = self.performanceDelegate; [performanceDelegate listAdapterWillCallDequeueCell:self]; IGListSectionController *sectionController = [self sectionControllerForSection:indexPath.section]; // flag that a cell is being dequeued in case it tries to access a cell in the process _isDequeuingCell = YES; UICollectionViewCell *cell = [sectionController cellForItemAtIndex:indexPath.item]; _isDequeuingCell = NO; IGAssert(cell != nil, @"Returned a nil cell at indexPath <%@> from section controller: <%@>", indexPath, sectionController); // associate the section controller with the cell so that we know which section controller is using it [self mapView:cell toSectionController:sectionController]; [performanceDelegate listAdapter:self didCallDequeueCell:cell onSectionController:sectionController atIndex:indexPath.item]; return cell; }
if (self.allowsBackgroundReloading && collectionView.window == nil) { [self _beginPerformBatchUpdatesToObjects:toObjects]; reloadDataFallback(); return; } // disables multiple performBatchUpdates: from happening at the same time [self _beginPerformBatchUpdatesToObjects:toObjects];
// block executed in the first param block of -[UICollectionView performBatchUpdates:completion:] void (^batchUpdatesBlock)(IGListIndexSetResult *result) = ^(IGListIndexSetResult *result){ //执行更新 executeUpdateBlocks(); self.applyingUpdateData = [self _flushCollectionView:collectionView withDiffResult:result batchUpdates:self.batchUpdates fromObjects:fromObjects]; //重置状态 [self _cleanStateAfterUpdates]; [self _performBatchUpdatesItemBlockApplied]; };
// block used as the second param of -[UICollectionView performBatchUpdates:completion:] void (^batchUpdatesCompletionBlock)(BOOL) = ^(BOOL finished) { IGListBatchUpdateData *oldApplyingUpdateData = self.applyingUpdateData; executeCompletionBlocks(finished);
// queue another update in case something changed during batch updates. this method will bail next runloop if // there are no changes [self _queueUpdateWithCollectionViewBlock:collectionViewBlock]; };
// block that executes the batch update and exception handling void (^performUpdate)(IGListIndexSetResult *) = ^(IGListIndexSetResult *result){ [collectionView layoutIfNeeded];
/** 与collectionView 交互的上下文 Use this property for accessing the collection size, dequeuing cells, reloading, inserting, deleting, etc. */ @property (nonatomic, weak, nullable, readonly) id <IGListCollectionContext> collectionContext;
微信官方有专门的接入文档: Mars iOS/OS X 接入指南 一种是以framework形式引入的,一般实际开发项目中会以这种方式接入,另一种是调试模式,初期分析Mars Sample代码的时候需要以这种方式引入,这里需要注意的是编译成功后需要将mars.framework拷贝到项目文件夹下再添加到项目中。不然会提示找不到某些头文件。
FIN1bit 表示信息的最后一帧,flag,也就是标记符 RSV1-31bit each 以后备用的 默认都为 0 Opcode4bit 帧类型,稍后细说 Mask1bit 掩码,是否加密数据,只适用于客户端发给服务器的消息,客户端给服务器发送消息,这里一定为 1 Payload7bit 数据的长度 Masking-key 1 or 4 bit 掩码Key Payload data (x + y) bytes 数据 Extension data x bytes 扩展数据 Application data y bytes 程序数据
//如果协议是wss://类型而且_pinnedCertFound 为NO,并且事件类型是有可读数据未读,或者事件类型是还有空余空间可写 if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { //获取到SSLPinnedCertificates NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates]; if (sslCerts) { 将NSStream中的证书与服务端请求中的SSLPinnedCertificates一个一个进行比较,查看是否有找到匹配的。 SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; if (secTrust) { NSInteger numCerts = SecTrustGetCertificateCount(secTrust); for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert));
for (id ref in sslCerts) { SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert));
case NSStreamEventEndEncountered: { [self _pumpScanner]; SRFastLog(@”NSStreamEventEndEncountered %@”, aStream); if (aStream.streamError) { [self _failWithError:aStream.streamError]; } else { dispatch_async(_workQueue, ^{ if (self.readyState != SR_CLOSED) { self.readyState = SR_CLOSED; [self _scheduleCleanup]; }
if (!_sentClose && !_failed) { _sentClose = YES; // If we get closed in this state it’s probably not clean because we should be sending this when we send messages [self _performDelegateBlock:^{ if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { [self.delegate webSocket:self didCloseWithCode:SRStatusCodeGoingAway reason:@”Stream end encountered” wasClean:NO]; } }]; } }); }
break; }
case NSStreamEventHasBytesAvailable: { SRFastLog(@”NSStreamEventHasBytesAvailable %@”, aStream); const int bufferSize = 2048; uint8_t buffer[bufferSize];
while (_inputStream.hasBytesAvailable) { NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize];
if (bytes_read > 0) { [_readBuffer appendBytes:buffer length:bytes_read]; } else if (bytes_read < 0) { [self _failWithError:_inputStream.streamError]; }
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html // According to the docs, you should only trust your provided certs for evaluation. // Pinned certificates are added to the trust. Without pinned certificates, // there is nothing to evaluate against. // // From Apple Docs: // "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors). // Instead, add your own (self-signed) CA certificate to the list of trusted anchors." NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning."); returnNO; }