From 71da2917e577b7ec659083408cff7f9981d6600f Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 22 Oct 2015 04:02:51 -0700 Subject: [PATCH] Remove retainArguments from RCTModuleMethod's invocation Summary: public Fixes #2527 We were re-using the same invocation for every module's method, but calling `[NSInvocation retainArguments]`, so the arguments would never be released. Reviewed By: nicklockwood Differential Revision: D2559997 fb-gh-sync-id: eafa3b3517c7cab3539954e26e250f7f668eee50 --- React/Base/RCTModuleMethod.m | 54 ++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/React/Base/RCTModuleMethod.m b/React/Base/RCTModuleMethod.m index f01e60cfd..e684f79a8 100644 --- a/React/Base/RCTModuleMethod.m +++ b/React/Base/RCTModuleMethod.m @@ -158,7 +158,6 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) RCTAssert(methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName); NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; invocation.selector = _selector; - [invocation retainArguments]; _invocation = invocation; // Process arguments @@ -172,6 +171,13 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) return YES; \ }]; +/** + * Explicitly copy the block and retain it, since NSInvocation doesn't retain them. + */ +#define RCT_BLOCK_ARGUMENT(block...) \ + id value = json ? [block copy] : (id)^(__unused NSArray *_){}; \ + CFBridgingRetain(value) + __weak RCTModuleMethod *weakSelf = self; void (^addBlockArgument)(void) = ^{ RCT_ARG_BLOCK( @@ -181,12 +187,11 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) return NO; } - // Marked as autoreleasing, because NSInvocation doesn't retain arguments - __autoreleasing id value = (json ? ^(NSArray *args) { + RCT_BLOCK_ARGUMENT(^(NSArray *args) { [bridge _invokeAndProcessModule:@"BatchedBridge" method:@"invokeCallbackAndReturnFlushedQueue" arguments:@[json, args]]; - } : ^(__unused NSArray *unused) {}); + }); ) }; @@ -231,7 +236,16 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) 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_ID: { + isNullableType = YES; + id (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; + RCT_ARG_BLOCK( + id value = convert([RCTConvert class], selector, json); + CFBridgingRetain(value); + ) + break; + } case _C_STRUCT_B: { @@ -272,12 +286,11 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) return NO; } - // Marked as autoreleasing, because NSInvocation doesn't retain arguments - __autoreleasing id value = (json ? ^(NSError *error) { + RCT_BLOCK_ARGUMENT(^(NSError *error) { [bridge _invokeAndProcessModule:@"BatchedBridge" method:@"invokeCallbackAndReturnFlushedQueue" arguments:@[json, @[RCTJSErrorFromNSError(error)]]]; - } : ^(__unused NSError *error) {}); + }); ) } else if ([typeName isEqualToString:@"RCTPromiseResolveBlock"]) { RCTAssert(i == numberOfArguments - 2, @@ -289,8 +302,7 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) return NO; } - // Marked as autoreleasing, because NSInvocation doesn't retain arguments - __autoreleasing RCTPromiseResolveBlock value = (^(id result) { + RCT_BLOCK_ARGUMENT(^(id result) { [bridge _invokeAndProcessModule:@"BatchedBridge" method:@"invokeCallbackAndReturnFlushedQueue" arguments:@[json, result ? @[result] : @[]]]; @@ -306,8 +318,7 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) return NO; } - // Marked as autoreleasing, because NSInvocation doesn't retain arguments - __autoreleasing RCTPromiseRejectBlock value = (^(NSError *error) { + RCT_BLOCK_ARGUMENT(^(NSError *error) { NSDictionary *errorJSON = RCTJSErrorFromNSError(error); [bridge _invokeAndProcessModule:@"BatchedBridge" method:@"invokeCallbackAndReturnFlushedQueue" @@ -433,6 +444,25 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments) // Invoke method [_invocation invokeWithTarget:module]; + + RCTAssert( + @encode(RCTArgumentBlock)[0] == _C_ID, + @"Block type encoding has changed, it won't be released. A check for the block" + "type encoding (%s) has to be added below.", + @encode(RCTArgumentBlock) + ); + + index = 2; + for (NSUInteger length = _invocation.methodSignature.numberOfArguments; index < length; index++) { + if ([_invocation.methodSignature getArgumentTypeAtIndex:index][0] == _C_ID) { + __unsafe_unretained id value; + [_invocation getArgument:&value atIndex:index]; + + if (value) { + CFRelease((__bridge CFTypeRef)value); + } + } + } } - (NSString *)methodName