http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/5b99cc6b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m index c53d5f1..db26c93 100644 --- a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m +++ b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.m @@ -20,6 +20,7 @@ #import "WXComponentManager.h" #import "WXComponent.h" #import "WXComponent_internal.h" +#import "WXComponent+DataBinding.h" #import "WXComponentFactory.h" #import "WXDefine.h" #import "NSArray+Weex.h" @@ -135,6 +136,18 @@ static NSThread *WXComponentThread; } } ++ (void)_performBlockSyncOnComponentThread:(void (^)())block +{ + if([NSThread currentThread] == [self componentThread]){ + block(); + } else { + [self performSelector:@selector(_performBlockOnComponentThread:) + onThread:WXComponentThread + withObject:[block copy] + waitUntilDone:YES]; + } +} + - (void)startComponentTasks { [self _awakeDisplayLink]; @@ -204,6 +217,15 @@ static NSThread *WXComponentThread; _rootComponent = [self _buildComponentForData:data supercomponent:nil]; [self _initRootCSSNode]; + + NSArray *subcomponentsData = [data valueForKey:@"children"]; + if (subcomponentsData) { + BOOL appendTree = [_rootComponent.attributes[@"append"] isEqualToString:@"tree"]; + for(NSDictionary *subcomponentData in subcomponentsData){ + [self _recursivelyAddComponent:subcomponentData toSupercomponent:_rootComponent atIndex:-1 appendingInTree:appendTree]; + } + } + __weak typeof(self) weakSelf = self; WX_MONITOR_INSTANCE_PERF_END(WXFirstScreenJSFExecuteTime, self.weexInstance); [self _addUITask:^{ @@ -213,6 +235,8 @@ static NSThread *WXComponentThread; [strongSelf.weexInstance.rootView addSubview:strongSelf->_rootComponent.view]; [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:data[@"ref"] className:nil name:data[@"type"] phase:WXTracingEnd functionName:@"createBody" options:@{@"threadName":WXTUIThread}]; }]; + + } static bool rootNodeIsDirty(void *context) @@ -259,14 +283,16 @@ static css_node_t * rootNodeGetChild(void *context, int i) if(supercomponent && component && supercomponent->_lazyCreateView) { component->_lazyCreateView = YES; } - - __weak typeof(self) weakSelf = self; - [self _addUITask:^{ - [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}]; - [supercomponent insertSubview:component atIndex:index]; - [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}]; - }]; - + + if (!component->_isTemplate) { + __weak typeof(self) weakSelf = self; + [self _addUITask:^{ + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingBegin functionName:@"addElement" options:@{@"threadName":WXTUIThread}]; + [supercomponent insertSubview:component atIndex:index]; + [WXTracingManager startTracingWithInstanceId:weakSelf.weexInstance.instanceId ref:componentData[@"ref"] className:nil name:componentData[@"type"] phase:WXTracingEnd functionName:@"addElement" options:@{@"threadName":WXTUIThread}]; + }]; + } + NSArray *subcomponentsData = [componentData valueForKey:@"children"]; BOOL appendTree = !appendingInTree && [component.attributes[@"append"] isEqualToString:@"tree"]; @@ -274,6 +300,9 @@ static css_node_t * rootNodeGetChild(void *context, int i) for(NSDictionary *subcomponentData in subcomponentsData){ [self _recursivelyAddComponent:subcomponentData toSupercomponent:component atIndex:-1 appendingInTree:appendTree || appendingInTree]; } + + [component _didInserted]; + if (appendTree) { // If appending treeï¼force layout in case of too much tasks piling up in syncQueue [self _layoutAndSyncUI]; @@ -384,9 +413,28 @@ static css_node_t * rootNodeGetChild(void *context, int i) } } } - - Class clazz = [WXComponentFactory classWithComponentName:type]; + + WXComponentConfig *config = [WXComponentFactory configWithComponentName:type]; + BOOL isTemplate = [config.properties[@"isTemplate"] boolValue] || (supercomponent && supercomponent->_isTemplate); + NSDictionary *bindingStyles; + NSDictionary *bindingAttibutes; + NSDictionary *bindingEvents; + NSDictionary *bindingProps; + if (isTemplate) { + bindingProps = [self _extractBindingProps:&attributes]; + bindingStyles = [self _extractBindings:&styles]; + bindingAttibutes = [self _extractBindings:&attributes]; + bindingEvents = [self _extractBindingEvents:&events]; + } + + Class clazz = NSClassFromString(config.clazz);; WXComponent *component = [[clazz alloc] initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:self.weexInstance]; + + if (isTemplate) { + component->_isTemplate = YES; + [component _storeBindingsWithProps:bindingProps styles:bindingStyles attributes:bindingAttibutes events:bindingEvents]; + } + WXAssert(component, @"Component build failed for data:%@", data); [_indexDict setObject:component forKey:component.ref]; @@ -394,6 +442,80 @@ static css_node_t * rootNodeGetChild(void *context, int i) return component; } +- (void)addComponent:(WXComponent *)component toIndexDictForRef:(NSString *)ref +{ + [_indexDict setObject:component forKey:ref]; +} + +- (NSDictionary *)_extractBindings:(NSDictionary **)attributesOrStylesPoint +{ + NSDictionary *attributesOrStyles = *attributesOrStylesPoint; + if (!attributesOrStyles) { + return nil; + } + + NSMutableDictionary *newAttributesOrStyles = [attributesOrStyles mutableCopy]; + NSMutableDictionary *bindingAttributesOrStyles = [NSMutableDictionary dictionary]; + + [attributesOrStyles enumerateKeysAndObjectsUsingBlock:^(id _Nonnull attributeOrStyleName, id _Nonnull attributeOrStyle, BOOL * _Nonnull stop) { + if ([WXBindingMatchIdentify isEqualToString:attributeOrStyleName] // match + || [WXBindingRepeatIdentify isEqualToString:attributeOrStyleName] // repeat + || ([attributeOrStyle isKindOfClass:[NSDictionary class]] && attributeOrStyle[WXBindingIdentify])) { // {"attributeOrStyleName": {"@binding":"bindingExpression"} + bindingAttributesOrStyles[attributeOrStyleName] = attributeOrStyle; + [newAttributesOrStyles removeObjectForKey:attributeOrStyleName]; + } else if ([attributeOrStyle isKindOfClass:[NSArray class]]) { + // {"attributeOrStyleName":[..., "string", {"@binding":"bindingExpression"}, "string", {"@binding":"bindingExpression"}, ...] + __block BOOL isBinding = NO; + [attributeOrStyle enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([obj isKindOfClass:[NSDictionary class]] && obj[WXBindingIdentify]) { + isBinding = YES; + *stop = YES; + } + }]; + + if (isBinding) { + bindingAttributesOrStyles[attributeOrStyleName] = attributeOrStyle; + [newAttributesOrStyles removeObjectForKey:attributeOrStyleName]; + } + } + }]; + + *attributesOrStylesPoint = newAttributesOrStyles; + + return bindingAttributesOrStyles; +} + +- (NSDictionary *)_extractBindingEvents:(NSArray **)eventsPoint +{ + NSArray *events = *eventsPoint; + NSMutableArray *newEvents = [events mutableCopy]; + NSMutableDictionary *bindingEvents = [NSMutableDictionary dictionary]; + [events enumerateObjectsUsingBlock:^(id _Nonnull event, NSUInteger idx, BOOL * _Nonnull stop) { + if ([event isKindOfClass:[NSDictionary class]] && event[@"type"] && event[@"params"]) { + NSString *eventName = event[@"type"]; + NSString *bindingParams = event[@"params"]; + bindingEvents[eventName] = bindingParams; + newEvents[idx] = eventName; + } + }]; + + *eventsPoint = newEvents; + return bindingEvents; +} + +- (NSDictionary *)_extractBindingProps:(NSDictionary **)attributesPoint +{ + NSDictionary *attributes = *attributesPoint; + if (attributes[@"@componentProps"]) { + NSMutableDictionary *newAttributes = [attributes mutableCopy]; + [newAttributes removeObjectForKey:@"@componentProps"]; + *attributesPoint = newAttributes; + return attributes[@"@componentProps"]; + } + + return nil; +} + #pragma mark Reset -(BOOL)isShouldReset:(id )value { @@ -783,3 +905,8 @@ void WXPerformBlockOnComponentThread(void (^block)()) { [WXComponentManager _performBlockOnComponentThread:block]; } + +void WXPerformBlockSyncOnComponentThread(void (^block)()) +{ + [WXComponentManager _performBlockSyncOnComponentThread:block]; +}
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/5b99cc6b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h index 39385ef..ac1b13a 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.h +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.h @@ -23,6 +23,11 @@ @class WXSDKInstance; +typedef enum : NSUInteger { + WXDisplayTypeNone, + WXDisplayTypeBlock +} WXDisplayType; + /** * @abstract the component callback , result can be string or dictionary. * @discussion callback data to js, the id of callback function will be removed to save memory. @@ -37,7 +42,7 @@ typedef void (^WXKeepAliveCallback)(_Nonnull id result, BOOL keepAlive); NS_ASSUME_NONNULL_BEGIN -@interface WXComponent : NSObject +@interface WXComponent : NSObject <NSCopying> ///-------------------------------------- /// @name Component Hierarchy Management @@ -346,6 +351,8 @@ NS_ASSUME_NONNULL_BEGIN /// @name Display ///-------------------------------------- +@property (nonatomic, assign) WXDisplayType displayType; + /** * @abstract Marks the view as needing display. The method should be called on the main thread. * @discussion You can use this method to notify the system that your component's contents need to be redrawn. This method makes a note of the request and returns immediately. The component is not actually redrawn until the next drawing cycle, at which point all invalidated components are updated. @@ -399,6 +406,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (UIImage *)endDrawContext:(CGContextRef)context; +///-------------------------------------- +/// @name Data Binding +///-------------------------------------- + +/** + * @abstract Update binding data for the component + * @parameter binding data to update + */ +- (void)updateBindingData:(NSDictionary *)data; + @end @interface WXComponent (Deprecated) http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/5b99cc6b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m index 0992b49..2bad1c2 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m +++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m @@ -88,6 +88,7 @@ _absolutePosition = CGPointMake(NAN, NAN); + _displayType = WXDisplayTypeBlock; _isNeedJoinLayoutSystem = YES; _isLayoutDirty = YES; _isViewFrameSyncWithCalculated = YES; @@ -140,6 +141,41 @@ return self; } +- (id)copyWithZone:(NSZone *)zone +{ + NSInteger copyId = 0; + @synchronized(self){ + static NSInteger __copy = 0; + copyId = __copy % (1024*1024); + __copy++; + } + NSString *copyRef = [NSString stringWithFormat:@"%ldcopy_of%@", copyId, _isTemplate ? self.ref : self->_templateComponent.ref]; + WXComponent *component = [[[self class] allocWithZone:zone] initWithRef:copyRef type:self.type styles:self.styles attributes:self.attributes events:self.events weexInstance:self.weexInstance]; + if (_isTemplate) { + component->_templateComponent = self; + } else { + component->_templateComponent = self->_templateComponent; + } + memcpy(component->_cssNode, self.cssNode, sizeof(css_node_t)); + component->_cssNode->context = (__bridge void *)component; + component->_calculatedFrame = self.calculatedFrame; + + NSMutableArray *subcomponentsCopy = [NSMutableArray array]; + for (WXComponent *subcomponent in self.subcomponents) { + WXComponent *subcomponentCopy = [subcomponent copy]; + subcomponentCopy->_supercomponent = component; + [subcomponentsCopy addObject:subcomponentCopy]; + } + + component->_subcomponents = subcomponentsCopy; + + WXPerformBlockOnComponentThread(^{ + [self.weexInstance.componentManager addComponent:component toIndexDictForRef:copyRef]; + }); + + return component; +} + - (UIAccessibilityTraits)_parseAccessibilityTraitsWithTraits:(UIAccessibilityTraits)trait roles:(NSString*)roleStr { UIAccessibilityTraits newTrait = trait; @@ -208,6 +244,28 @@ return events; } +- (void)setDisplayType:(WXDisplayType)displayType +{ + if (_displayType != displayType) { + _displayType = displayType; + if (displayType == WXDisplayTypeNone) { + _isNeedJoinLayoutSystem = NO; + [self.supercomponent _recomputeCSSNodeChildren]; + WXPerformBlockOnMainThread(^{ + [self removeFromSuperview]; + }); + } else { + _isNeedJoinLayoutSystem = YES; + [self.supercomponent _recomputeCSSNodeChildren]; + WXPerformBlockOnMainThread(^{ + [self _buildViewHierarchyLazily]; + // TODO: insert into the correct index + [self.supercomponent insertSubview:self atIndex:0]; + }); + } + } +} + - (WXSDKInstance *)weexInstance { return _weexInstance; @@ -371,6 +429,26 @@ return _cssNode; } +- (void)_addEventParams:(NSDictionary *)params +{ + pthread_mutex_lock(&_propertyMutex); + if (!_eventParameters) { + _eventParameters = [NSMutableDictionary dictionary]; + } + [_eventParameters addEntriesFromDictionary:params]; + pthread_mutex_unlock(&_propertyMutex); +} + +- (NSArray *)_paramsForEvent:(NSString *)eventName +{ + NSArray *params; + pthread_mutex_lock(&_propertyMutex); + params = _eventParameters[eventName]; + pthread_mutex_unlock(&_propertyMutex); + + return params; +} + #pragma mark Component Hierarchy - (NSArray<WXComponent *> *)subcomponents @@ -440,6 +518,11 @@ [newSupercomponent _insertSubcomponent:self atIndex:index]; } +- (void)_didInserted +{ + +} + - (id<WXScrollerProtocol>)ancestorScroller { if(!_ancestorScroller) { http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/5b99cc6b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h index f494247..8c7aee3 100644 --- a/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h +++ b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.h @@ -21,7 +21,7 @@ @protocol WXDiffable <NSObject> -- (BOOL)isEqualToWXObject:(id<WXDiffable>)object; +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object; @end @@ -40,6 +40,10 @@ - (BOOL)hasChanges; +- (instancetype)initWithInserts:(NSIndexSet *)inserts + deletes:(NSIndexSet *)deletes + updates:(NSArray<WXDiffUpdateIndex *> *)updates; + @end @interface WXDiffUtil : NSObject @@ -47,3 +51,20 @@ + (WXDiffResult *)diffWithMinimumDistance:(NSArray<id<WXDiffable>> *)newArray oldArray:(NSArray<id<WXDiffable>> *)oldArray; @end + +@interface NSNumber (WXDiffable) <WXDiffable> + +@end + +@interface NSString (WXDiffable) <WXDiffable> + +@end + +@interface NSArray (WXDiffable) <WXDiffable> + +@end + +@interface NSDictionary (WXDiffable) <WXDiffable> + +@end + http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/5b99cc6b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m index 9396243..fabdbd1 100644 --- a/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m +++ b/ios/sdk/WeexSDK/Sources/Utility/WXDiffUtil.m @@ -96,7 +96,7 @@ typedef enum : NSUInteger { for (int oldIndex = 1; oldIndex < oldSize; oldIndex ++) { for (int newIndex = 1; newIndex < newSize; newIndex ++) { - if ([oldArray[oldIndex - 1] isEqualToWXObject:newArray[newIndex - 1]]) { + if ([oldArray[oldIndex - 1] weex_isEqualTo:newArray[newIndex - 1]]) { matrix[oldIndex][newIndex] = matrix[oldIndex - 1][newIndex - 1]; } else { int updateCost = matrix[oldIndex - 1][newIndex - 1] + 1; @@ -107,7 +107,9 @@ typedef enum : NSUInteger { } } +#if DEBUG [self _printMatrix:matrix rowSize:oldSize columnSize:newSize]; +#endif NSMutableArray *updates = [NSMutableArray array]; NSMutableIndexSet *inserts = [NSMutableIndexSet indexSet]; @@ -195,3 +197,85 @@ typedef enum : NSUInteger { } @end + +@implementation NSNumber (WXDiffable) + +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object +{ + return [self isEqual:object]; +} + +@end + +@implementation NSString (WXDiffable) + +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object +{ + return [self isEqual:object]; +} + +@end + +@implementation NSArray (WXDiffable) + +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object +{ + if (![object isKindOfClass:[NSArray class]]) { + return NO; + } + + NSArray *array = (NSArray *)object; + if (self.count != array.count) { + return NO; + } + + __block BOOL isEqual = YES; + [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + id obj2 = [self objectAtIndex:idx]; + if ([obj conformsToProtocol:@protocol(WXDiffable)] && [obj2 conformsToProtocol:@protocol(WXDiffable)]) { + if (![obj weex_isEqualTo:obj2]) { + isEqual = NO; + *stop = YES; + } + } else { + isEqual = NO; + *stop = YES; + } + }]; + + return isEqual; +} + +@end + +@implementation NSDictionary (WXDiffable) + +- (BOOL)weex_isEqualTo:(id<WXDiffable>)object +{ + if (![object isKindOfClass:[NSDictionary class]]) { + return NO; + } + + NSDictionary *dictionary = (NSDictionary *)object; + if (self.count != dictionary.count) { + return NO; + } + + __block BOOL isEqual = YES; + [dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + id obj2 = [self objectForKey:key]; + if (obj2 && [obj2 conformsToProtocol:@protocol(WXDiffable)] && [obj conformsToProtocol:@protocol(WXDiffable)]) { + if (![obj weex_isEqualTo:obj2]) { + isEqual = NO; + *stop = YES; + } + } else { + isEqual = NO; + *stop = YES; + } + }]; + + return isEqual; +} + +@end http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/5b99cc6b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h index 5d96757..538dc77 100644 --- a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h +++ b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h @@ -81,33 +81,41 @@ do {\ }\ }while(0) +#ifdef __cplusplus +extern "C" { +#endif + /** * @abstract execute asynchronous action block on the main thread. * */ -extern void WXPerformBlockOnMainThread( void (^ _Nonnull block)()); +void WXPerformBlockOnMainThread( void (^ _Nonnull block)()); /** * @abstract execute synchronous action block on the main thread. * */ -extern void WXPerformBlockSyncOnMainThread( void (^ _Nonnull block)()); +void WXPerformBlockSyncOnMainThread( void (^ _Nonnull block)()); /** * @abstract execute action block on the specific thread. * */ -extern void WXPerformBlockOnThread(void (^ _Nonnull block)(), NSThread *_Nonnull thread); +void WXPerformBlockOnThread(void (^ _Nonnull block)(), NSThread *_Nonnull thread); /** * @abstract swizzling methods. * */ -extern void WXSwizzleInstanceMethod(_Nonnull Class className, _Nonnull SEL original, _Nonnull SEL replaced); +void WXSwizzleInstanceMethod(_Nonnull Class className, _Nonnull SEL original, _Nonnull SEL replaced); -extern void WXSwizzleInstanceMethodWithBlock(_Nonnull Class className, _Nonnull SEL original, _Nonnull id block, _Nonnull SEL replaced); +void WXSwizzleInstanceMethodWithBlock(_Nonnull Class className, _Nonnull SEL original, _Nonnull id block, _Nonnull SEL replaced); -extern _Nonnull SEL WXSwizzledSelectorForSelector(_Nonnull SEL selector); +_Nonnull SEL WXSwizzledSelectorForSelector(_Nonnull SEL selector); + +#ifdef __cplusplus +} +#endif @interface WXUtility : NSObject http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/5b99cc6b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m index ef00f9a..be27ae7 100644 --- a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m +++ b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.m @@ -94,6 +94,10 @@ do {\ { WXAssertMainThread(); + if (subcomponent.displayType == WXDisplayTypeNone) { + return; + } + WX_CHECK_COMPONENT_TYPE(self.componentType) if (subcomponent->_positionType == WXPositionTypeFixed) { [self.weexInstance.rootView addSubview:subcomponent.view];