mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-03-26 07:04:05 +08:00
Refactor RCTUIManager
Summary: Moved the view creation & property binding logic out of RCTUIManager into a separate RCTComponentData class - this follows the pattern used with the bridge. I've also updated the property binding to use pre-allocated blocks for setting the values, which is more efficient than the previous system that re-contructed the selectors each time it was called. This should improve view update performance significantly.
This commit is contained in:
@@ -54,13 +54,6 @@ RCT_EXTERN NSString *const RCTDidCreateNativeModules;
|
||||
*/
|
||||
typedef NSArray *(^RCTBridgeModuleProviderBlock)(void);
|
||||
|
||||
/**
|
||||
* Register the given class as a bridge module. All modules must be registered
|
||||
* prior to the first bridge initialization.
|
||||
*
|
||||
*/
|
||||
RCT_EXTERN void RCTRegisterModule(Class);
|
||||
|
||||
/**
|
||||
* This function returns the module name for a given class.
|
||||
*/
|
||||
@@ -71,7 +64,6 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
||||
*/
|
||||
@interface RCTBridge : NSObject <RCTInvalidating>
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new bridge with a custom RCTBridgeDelegate.
|
||||
*
|
||||
|
||||
@@ -48,6 +48,11 @@ NSArray *RCTGetModuleClasses(void)
|
||||
return RCTModuleClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given class as a bridge module. All modules must be registered
|
||||
* prior to the first bridge initialization.
|
||||
*/
|
||||
void RCTRegisterModule(Class);
|
||||
void RCTRegisterModule(Class moduleClass)
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
@@ -57,7 +62,7 @@ void RCTRegisterModule(Class moduleClass)
|
||||
|
||||
RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
||||
@"%@ does not conform to the RCTBridgeModule protocol",
|
||||
NSStringFromClass(moduleClass));
|
||||
moduleClass);
|
||||
|
||||
// Register module
|
||||
[RCTModuleClasses addObject:moduleClass];
|
||||
|
||||
@@ -94,7 +94,7 @@ extern dispatch_queue_t RCTJSThread;
|
||||
#define RCT_EXPORT_MODULE(js_name) \
|
||||
RCT_EXTERN void RCTRegisterModule(Class); \
|
||||
+ (NSString *)moduleName { return @#js_name; } \
|
||||
+ (void)load { RCTRegisterModule([self class]); }
|
||||
+ (void)load { RCTRegisterModule(self); }
|
||||
|
||||
/**
|
||||
* Wrap the parameter line of your method implementation with this macro to
|
||||
|
||||
@@ -136,21 +136,6 @@ typedef BOOL css_clip_t, css_backface_visibility_t;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This function will attempt to set a property using a json value by first
|
||||
* inferring the correct type from all available information, and then
|
||||
* applying an appropriate conversion method. If the property does not
|
||||
* exist, or the type cannot be inferred, the function will return NO.
|
||||
*/
|
||||
RCT_EXTERN BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json);
|
||||
|
||||
/**
|
||||
* This function attempts to copy a property from the source object to the
|
||||
* destination object using KVC. If the property does not exist, or cannot
|
||||
* be set, it will do nothing and return NO.
|
||||
*/
|
||||
RCT_EXTERN BOOL RCTCopyProperty(id target, id source, NSString *keyPath);
|
||||
|
||||
/**
|
||||
* Underlying implementations of RCT_XXX_CONVERTER macros. Ignore these.
|
||||
*/
|
||||
|
||||
@@ -1056,177 +1056,3 @@ RCT_ENUM_CONVERTER(RCTAnimationType, (@{
|
||||
}), RCTAnimationTypeEaseInEaseOut, integerValue)
|
||||
|
||||
@end
|
||||
|
||||
BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keyPath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Get property setter
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
// Fail early
|
||||
if (![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@try {
|
||||
|
||||
NSMethodSignature *signature = [RCTConvert methodSignatureForSelector:type];
|
||||
switch (signature.methodReturnType[0]) {
|
||||
|
||||
#define RCT_SET_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \
|
||||
set(target, setter, convert([RCTConvert class], type, json)); \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_SET_CASE(':', SEL)
|
||||
RCT_SET_CASE('*', const char *)
|
||||
RCT_SET_CASE('c', char)
|
||||
RCT_SET_CASE('C', unsigned char)
|
||||
RCT_SET_CASE('s', short)
|
||||
RCT_SET_CASE('S', unsigned short)
|
||||
RCT_SET_CASE('i', int)
|
||||
RCT_SET_CASE('I', unsigned int)
|
||||
RCT_SET_CASE('l', long)
|
||||
RCT_SET_CASE('L', unsigned long)
|
||||
RCT_SET_CASE('q', long long)
|
||||
RCT_SET_CASE('Q', unsigned long long)
|
||||
RCT_SET_CASE('f', float)
|
||||
RCT_SET_CASE('d', double)
|
||||
RCT_SET_CASE('B', BOOL)
|
||||
RCT_SET_CASE('^', void *)
|
||||
|
||||
case '@': {
|
||||
id (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend;
|
||||
void (*set)(id, SEL, id) = (typeof(set))objc_msgSend;
|
||||
set(target, setter, convert([RCTConvert class], type, json));
|
||||
break;
|
||||
}
|
||||
case '{':
|
||||
default: {
|
||||
|
||||
// Get converted value
|
||||
void *value = malloc(signature.methodReturnLength);
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setTarget:[RCTConvert class]];
|
||||
[invocation setSelector:type];
|
||||
[invocation setArgument:&json atIndex:2];
|
||||
[invocation invoke];
|
||||
[invocation getReturnValue:value];
|
||||
|
||||
// Set converted value
|
||||
signature = [target methodSignatureForSelector:setter];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&setter atIndex:1];
|
||||
[invocation setArgument:value atIndex:2];
|
||||
[invocation invokeWithTarget:target];
|
||||
free(value);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogError(@"Exception thrown while attempting to set property '%@' of \
|
||||
'%@' with value '%@': %@", key, [target class], json, exception);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL RCTCopyProperty(id target, id source, NSString *keyPath)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keyPath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
source = [source valueForKey:parts[i]];
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!source || !target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Get property getter
|
||||
SEL getter = NSSelectorFromString(key);
|
||||
|
||||
// Get property setter
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
// Fail early
|
||||
if (![source respondsToSelector:getter] || ![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMethodSignature *signature = [source methodSignatureForSelector:getter];
|
||||
switch (signature.methodReturnType[0]) {
|
||||
|
||||
#define RCT_COPY_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
_type (*get)(id, SEL) = (typeof(get))objc_msgSend; \
|
||||
void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \
|
||||
set(target, setter, get(source, getter)); \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_COPY_CASE(':', SEL)
|
||||
RCT_COPY_CASE('*', const char *)
|
||||
RCT_COPY_CASE('c', char)
|
||||
RCT_COPY_CASE('C', unsigned char)
|
||||
RCT_COPY_CASE('s', short)
|
||||
RCT_COPY_CASE('S', unsigned short)
|
||||
RCT_COPY_CASE('i', int)
|
||||
RCT_COPY_CASE('I', unsigned int)
|
||||
RCT_COPY_CASE('l', long)
|
||||
RCT_COPY_CASE('L', unsigned long)
|
||||
RCT_COPY_CASE('q', long long)
|
||||
RCT_COPY_CASE('Q', unsigned long long)
|
||||
RCT_COPY_CASE('f', float)
|
||||
RCT_COPY_CASE('d', double)
|
||||
RCT_COPY_CASE('B', BOOL)
|
||||
RCT_COPY_CASE('^', void *)
|
||||
|
||||
case '@': {
|
||||
id (*get)(id, SEL) = (typeof(get))objc_msgSend;
|
||||
void (*set)(id, SEL, id) = (typeof(set))objc_msgSend;
|
||||
set(target, setter, get(source, getter));
|
||||
break;
|
||||
}
|
||||
case '{':
|
||||
default: {
|
||||
|
||||
// Get value
|
||||
void *value = malloc(signature.methodReturnLength);
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&getter atIndex:1];
|
||||
[invocation invokeWithTarget:source];
|
||||
[invocation getReturnValue:value];
|
||||
|
||||
// Set value
|
||||
signature = [target methodSignatureForSelector:setter];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&setter atIndex:1];
|
||||
[invocation setArgument:value atIndex:2];
|
||||
[invocation invokeWithTarget:target];
|
||||
free(value);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
typedef void (^RCTArgumentBlock)(RCTBridge *, NSInvocation *, NSUInteger, id);
|
||||
typedef void (^RCTArgumentBlock)(RCTBridge *, NSUInteger, id);
|
||||
|
||||
@implementation RCTMethodArgument
|
||||
|
||||
@@ -47,7 +47,7 @@ typedef void (^RCTArgumentBlock)(RCTBridge *, NSInvocation *, NSUInteger, id);
|
||||
{
|
||||
Class _moduleClass;
|
||||
SEL _selector;
|
||||
NSMethodSignature *_methodSignature;
|
||||
NSInvocation *_invocation;
|
||||
NSArray *_argumentBlocks;
|
||||
}
|
||||
|
||||
@@ -135,16 +135,20 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||
methodName;
|
||||
});
|
||||
|
||||
// Get method signature
|
||||
_methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
||||
RCTAssert(_methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName);
|
||||
// Create method invocation
|
||||
NSMethodSignature *methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
||||
RCTAssert(methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName);
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[invocation setSelector:_selector];
|
||||
[invocation retainArguments];
|
||||
_invocation = invocation;
|
||||
|
||||
// Process arguments
|
||||
NSUInteger numberOfArguments = _methodSignature.numberOfArguments;
|
||||
NSUInteger numberOfArguments = methodSignature.numberOfArguments;
|
||||
NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
|
||||
|
||||
#define RCT_ARG_BLOCK(_logic) \
|
||||
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { \
|
||||
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \
|
||||
_logic \
|
||||
[invocation setArgument:&value atIndex:(index) + 2]; \
|
||||
}];
|
||||
@@ -168,7 +172,7 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||
};
|
||||
|
||||
for (NSUInteger i = 2; i < numberOfArguments; i++) {
|
||||
const char *objcType = [_methodSignature getArgumentTypeAtIndex:i];
|
||||
const char *objcType = [methodSignature getArgumentTypeAtIndex:i];
|
||||
BOOL isNullableType = NO;
|
||||
RCTMethodArgument *argument = arguments[i - 2];
|
||||
NSString *typeName = argument.type;
|
||||
@@ -176,45 +180,54 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||
if ([RCTConvert respondsToSelector:selector]) {
|
||||
switch (objcType[0]) {
|
||||
|
||||
#define RCT_CONVERT_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
if (RCT_DEBUG && ([@#_type hasSuffix:@"*"] || [@#_type hasSuffix:@"Ref"] || [@#_type isEqualToString:@"id"])) { \
|
||||
isNullableType = YES; \
|
||||
} \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||
break; \
|
||||
}
|
||||
#define RCT_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_CONVERT_CASE(':', SEL)
|
||||
RCT_CONVERT_CASE('*', const char *)
|
||||
RCT_CONVERT_CASE('c', char)
|
||||
RCT_CONVERT_CASE('C', unsigned char)
|
||||
RCT_CONVERT_CASE('s', short)
|
||||
RCT_CONVERT_CASE('S', unsigned short)
|
||||
RCT_CONVERT_CASE('i', int)
|
||||
RCT_CONVERT_CASE('I', unsigned int)
|
||||
RCT_CONVERT_CASE('l', long)
|
||||
RCT_CONVERT_CASE('L', unsigned long)
|
||||
RCT_CONVERT_CASE('q', long long)
|
||||
RCT_CONVERT_CASE('Q', unsigned long long)
|
||||
RCT_CONVERT_CASE('f', float)
|
||||
RCT_CONVERT_CASE('d', double)
|
||||
RCT_CONVERT_CASE('B', BOOL)
|
||||
RCT_CONVERT_CASE('@', id)
|
||||
RCT_CONVERT_CASE('^', void *)
|
||||
RCT_CASE(_C_CHR, char)
|
||||
RCT_CASE(_C_UCHR, unsigned char)
|
||||
RCT_CASE(_C_SHT, short)
|
||||
RCT_CASE(_C_USHT, unsigned short)
|
||||
RCT_CASE(_C_INT, int)
|
||||
RCT_CASE(_C_UINT, unsigned int)
|
||||
RCT_CASE(_C_LNG, long)
|
||||
RCT_CASE(_C_ULNG, unsigned long)
|
||||
RCT_CASE(_C_LNG_LNG, long long)
|
||||
RCT_CASE(_C_ULNG_LNG, unsigned long long)
|
||||
RCT_CASE(_C_FLT, float)
|
||||
RCT_CASE(_C_DBL, double)
|
||||
RCT_CASE(_C_BOOL, BOOL)
|
||||
|
||||
case '{': {
|
||||
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) {
|
||||
#define RCT_NULLABLE_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
isNullableType = YES; \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||
break; \
|
||||
}
|
||||
|
||||
NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector];
|
||||
void *returnValue = malloc(methodSignature.methodReturnLength);
|
||||
NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[_invocation setTarget:[RCTConvert class]];
|
||||
[_invocation setSelector:selector];
|
||||
[_invocation setArgument:&json atIndex:2];
|
||||
[_invocation invoke];
|
||||
[_invocation getReturnValue:returnValue];
|
||||
RCT_NULLABLE_CASE(_C_SEL, SEL)
|
||||
RCT_NULLABLE_CASE(_C_CHARPTR, const char *)
|
||||
RCT_NULLABLE_CASE(_C_PTR, void *)
|
||||
RCT_NULLABLE_CASE(_C_ID, id)
|
||||
|
||||
case _C_STRUCT_B: {
|
||||
|
||||
NSMethodSignature *typeSignature = [RCTConvert methodSignatureForSelector:selector];
|
||||
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
|
||||
[typeInvocation setSelector:selector];
|
||||
[typeInvocation setTarget:[RCTConvert class]];
|
||||
|
||||
[argumentBlocks addObject:
|
||||
^(__unused RCTBridge *bridge, NSUInteger index, id json) {
|
||||
|
||||
void *returnValue = malloc(typeSignature.methodReturnLength);
|
||||
[typeInvocation setArgument:&json atIndex:2];
|
||||
[typeInvocation invoke];
|
||||
[typeInvocation getReturnValue:returnValue];
|
||||
|
||||
[invocation setArgument:returnValue atIndex:index + 2];
|
||||
|
||||
@@ -323,13 +336,13 @@ case _value: { \
|
||||
|
||||
if (nullability == RCTNonnullable) {
|
||||
RCTArgumentBlock oldBlock = argumentBlocks[i - 2];
|
||||
argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) {
|
||||
argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSUInteger index, id json) {
|
||||
if (json == nil || json == (id)kCFNull) {
|
||||
RCTLogArgumentError(weakSelf, index, typeName, "must not be null");
|
||||
id null = nil;
|
||||
[invocation setArgument:&null atIndex:index + 2];
|
||||
} else {
|
||||
oldBlock(bridge, invocation, index, json);
|
||||
oldBlock(bridge, index, json);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -370,22 +383,17 @@ case _value: { \
|
||||
}
|
||||
}
|
||||
|
||||
// Create invocation (we can't re-use this as it wouldn't be thread-safe)
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_methodSignature];
|
||||
[invocation setArgument:&_selector atIndex:1];
|
||||
[invocation retainArguments];
|
||||
|
||||
// Set arguments
|
||||
NSUInteger index = 0;
|
||||
for (id json in arguments) {
|
||||
id arg = RCTNilIfNull(json);
|
||||
RCTArgumentBlock block = _argumentBlocks[index];
|
||||
block(bridge, invocation, index, arg);
|
||||
block(bridge, index, arg);
|
||||
index++;
|
||||
}
|
||||
|
||||
// Invoke method
|
||||
[invocation invokeWithTarget:module];
|
||||
[_invocation invokeWithTarget:module];
|
||||
}
|
||||
|
||||
- (NSString *)methodName
|
||||
|
||||
@@ -244,7 +244,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(id<RCTViewNodeProtocol>)subview atIndex:(NSInteger)atIndex
|
||||
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
[super insertReactSubview:subview atIndex:atIndex];
|
||||
RCTPerformanceLoggerEnd(RCTPLTTI);
|
||||
|
||||
Reference in New Issue
Block a user