diff --git a/shell/platform/darwin/BUILD.gn b/shell/platform/darwin/BUILD.gn index 23c8e4055acf7..f11a8b7aa1cb1 100644 --- a/shell/platform/darwin/BUILD.gn +++ b/shell/platform/darwin/BUILD.gn @@ -26,6 +26,20 @@ source_set("flutter_channels") { sources = [ "common/buffer_conversions.h", "common/buffer_conversions.mm", + ] + + deps = [ "//flutter/fml" ] + + public_deps = [ ":flutter_channels_arc" ] + + public_configs = [ "//flutter:config" ] +} + +source_set("flutter_channels_arc") { + cflags_objc = flutter_cflags_objc_arc + cflags_objcc = flutter_cflags_objcc_arc + + sources = [ "common/framework/Headers/FlutterBinaryMessenger.h", "common/framework/Headers/FlutterChannels.h", "common/framework/Headers/FlutterCodecs.h", @@ -36,13 +50,12 @@ source_set("flutter_channels") { "common/framework/Source/FlutterStandardCodec_Internal.h", ] - deps = [ - "//flutter/common", - "//flutter/flow", - "//flutter/fml", - "//flutter/runtime", - "//flutter/shell/common", - "//third_party/skia", + public = [ + "common/framework/Headers/FlutterBinaryMessenger.h", + "common/framework/Headers/FlutterChannels.h", + "common/framework/Headers/FlutterCodecs.h", + "common/framework/Headers/FlutterMacros.h", + "common/framework/Source/FlutterStandardCodec_Internal.h", ] public_configs = [ "//flutter:config" ] diff --git a/shell/platform/darwin/common/BUILD.gn b/shell/platform/darwin/common/BUILD.gn index 45a9e3092b2bc..72e3a807f1d85 100644 --- a/shell/platform/darwin/common/BUILD.gn +++ b/shell/platform/darwin/common/BUILD.gn @@ -16,15 +16,7 @@ source_set("common") { "command_line.mm", ] - deps = [ - "//flutter/common", - "//flutter/flow", - "//flutter/fml", - "//flutter/runtime", - "//flutter/shell/common", - "//third_party/dart/runtime:dart_api", - "//third_party/skia", - ] + deps = [ "//flutter/fml" ] public_configs = [ "//flutter:config" ] } @@ -38,8 +30,8 @@ config("framework_relative_headers") { # Framework code shared between iOS and macOS. source_set("framework_shared") { - cflags_objc = flutter_cflags_objc - cflags_objcc = flutter_cflags_objcc + cflags_objc = flutter_cflags_objc_arc + cflags_objcc = flutter_cflags_objcc_arc sources = [ "framework/Source/FlutterChannels.mm", diff --git a/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h b/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h index 3ba5d4e9c2580..f7117f01ffb7a 100644 --- a/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h +++ b/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h @@ -344,17 +344,17 @@ FLUTTER_DARWIN_EXPORT /** * The type of the encoded values. */ -@property(readonly, nonatomic) FlutterStandardDataType type; +@property(readonly, nonatomic, assign) FlutterStandardDataType type; /** * The number of value items encoded. */ -@property(readonly, nonatomic) UInt32 elementCount; +@property(readonly, nonatomic, assign) UInt32 elementCount; /** * The number of bytes used by the encoding of a single value item. */ -@property(readonly, nonatomic) UInt8 elementSize; +@property(readonly, nonatomic, assign) UInt8 elementSize; @end /** diff --git a/shell/platform/darwin/common/framework/Source/FlutterChannels.mm b/shell/platform/darwin/common/framework/Source/FlutterChannels.mm index 30b306a301f39..f360144d3748f 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterChannels.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterChannels.mm @@ -4,6 +4,8 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" +FLUTTER_ASSERT_ARC + #pragma mark - Basic message channel static NSString* const kFlutterChannelBuffersChannel = @"dev.flutter/channel-buffers"; @@ -51,9 +53,9 @@ + (instancetype)messageChannelWithName:(NSString*)name + (instancetype)messageChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec { - return [[[FlutterBasicMessageChannel alloc] initWithName:name - binaryMessenger:messenger - codec:codec] autorelease]; + return [[FlutterBasicMessageChannel alloc] initWithName:name + binaryMessenger:messenger + codec:codec]; } - (instancetype)initWithName:(NSString*)name @@ -69,21 +71,13 @@ - (instancetype)initWithName:(NSString*)name taskQueue:(NSObject*)taskQueue { self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _name = [name retain]; - _messenger = [messenger retain]; - _codec = [codec retain]; - _taskQueue = [taskQueue retain]; + _name = [name copy]; + _messenger = messenger; + _codec = codec; + _taskQueue = taskQueue; return self; } -- (void)dealloc { - [_name release]; - [_messenger release]; - [_codec release]; - [_taskQueue release]; - [super dealloc]; -} - - (void)sendMessage:(id)message { [_messenger sendOnChannel:_name message:[_codec encode:message]]; } @@ -107,7 +101,10 @@ - (void)setMessageHandler:(FlutterMessageHandler)handler { } return; } + // Grab reference to avoid retain on self. + // `self` might be released before the block, so the block needs to retain the codec to + // make sure it is not released with `self` NSObject* codec = _codec; FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) { handler([codec decode:message], ^(id reply) { @@ -128,26 +125,19 @@ - (void)resizeChannelBuffer:(NSInteger)newSize { //////////////////////////////////////////////////////////////////////////////// @implementation FlutterError + (instancetype)errorWithCode:(NSString*)code message:(NSString*)message details:(id)details { - return [[[FlutterError alloc] initWithCode:code message:message details:details] autorelease]; + return [[FlutterError alloc] initWithCode:code message:message details:details]; } - (instancetype)initWithCode:(NSString*)code message:(NSString*)message details:(id)details { NSAssert(code, @"Code cannot be nil"); self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _code = [code retain]; - _message = [message retain]; - _details = [details retain]; + _code = [code copy]; + _message = [message copy]; + _details = details; return self; } -- (void)dealloc { - [_code release]; - [_message release]; - [_details release]; - [super dealloc]; -} - - (BOOL)isEqual:(id)object { if (self == object) { return YES; @@ -169,24 +159,18 @@ - (NSUInteger)hash { //////////////////////////////////////////////////////////////////////////////// @implementation FlutterMethodCall + (instancetype)methodCallWithMethodName:(NSString*)method arguments:(id)arguments { - return [[[FlutterMethodCall alloc] initWithMethodName:method arguments:arguments] autorelease]; + return [[FlutterMethodCall alloc] initWithMethodName:method arguments:arguments]; } - (instancetype)initWithMethodName:(NSString*)method arguments:(id)arguments { NSAssert(method, @"Method name cannot be nil"); self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _method = [method retain]; - _arguments = [arguments retain]; + _method = [method copy]; + _arguments = arguments; return self; } -- (void)dealloc { - [_method release]; - [_arguments release]; - [super dealloc]; -} - - (BOOL)isEqual:(id)object { if (self == object) { return YES; @@ -224,8 +208,7 @@ + (instancetype)methodChannelWithName:(NSString*)name + (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec { - return [[[FlutterMethodChannel alloc] initWithName:name binaryMessenger:messenger - codec:codec] autorelease]; + return [[FlutterMethodChannel alloc] initWithName:name binaryMessenger:messenger codec:codec]; } - (instancetype)initWithName:(NSString*)name @@ -240,21 +223,13 @@ - (instancetype)initWithName:(NSString*)name taskQueue:(NSObject*)taskQueue { self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _name = [name retain]; - _messenger = [messenger retain]; - _codec = [codec retain]; - _taskQueue = [taskQueue retain]; + _name = [name copy]; + _messenger = messenger; + _codec = codec; + _taskQueue = taskQueue; return self; } -- (void)dealloc { - [_name release]; - [_messenger release]; - [_codec release]; - [_taskQueue release]; - [super dealloc]; -} - - (void)invokeMethod:(NSString*)method arguments:(id)arguments { FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:method arguments:arguments]; @@ -285,6 +260,8 @@ - (void)setMethodCallHandler:(FlutterMethodCallHandler)handler { return; } // Make sure the block captures the codec, not self. + // `self` might be released before the block, so the block needs to retain the codec to + // make sure it is not released with `self` NSObject* codec = _codec; FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) { FlutterMethodCall* call = [codec decodeMethodCall:message]; @@ -328,8 +305,7 @@ + (instancetype)eventChannelWithName:(NSString*)name + (instancetype)eventChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec { - return [[[FlutterEventChannel alloc] initWithName:name binaryMessenger:messenger - codec:codec] autorelease]; + return [[FlutterEventChannel alloc] initWithName:name binaryMessenger:messenger codec:codec]; } - (instancetype)initWithName:(NSString*)name @@ -344,21 +320,13 @@ - (instancetype)initWithName:(NSString*)name taskQueue:(NSObject* _Nullable)taskQueue { self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _name = [name retain]; - _messenger = [messenger retain]; - _codec = [codec retain]; - _taskQueue = [taskQueue retain]; + _name = [name copy]; + _messenger = messenger; + _codec = codec; + _taskQueue = taskQueue; return self; } -- (void)dealloc { - [_name release]; - [_codec release]; - [_messenger release]; - [_taskQueue release]; - [super dealloc]; -} - static FlutterBinaryMessengerConnection SetStreamHandlerMessageHandlerOnChannel( NSObject* handler, NSString* name, diff --git a/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m b/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m index a499fbea8f34d..e2593f535b7e1 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m +++ b/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m @@ -234,6 +234,78 @@ - (void)testBasicMessageChannelTaskQueue { OCMVerify([binaryMessenger cleanUpConnection:connection]); } +- (void)testBasicMessageChannelInvokeHandlerAfterChannelReleased { + NSString* channelName = @"foo"; + __block NSString* handlerMessage; + __block FlutterBinaryMessageHandler messageHandler; + @autoreleasepool { + FlutterBinaryMessengerConnection connection = 123; + id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + id codec = OCMProtocolMock(@protocol(FlutterMessageCodec)); + id taskQueue = OCMClassMock([NSObject class]); + FlutterBasicMessageChannel* channel = + [[FlutterBasicMessageChannel alloc] initWithName:channelName + binaryMessenger:binaryMessenger + codec:codec + taskQueue:taskQueue]; + + FlutterMessageHandler handler = ^(id _Nullable message, FlutterReply callback) { + handlerMessage = message; + }; + OCMStub([binaryMessenger + setMessageHandlerOnChannel:channelName + binaryMessageHandler:[OCMArg checkWithBlock:^BOOL( + FlutterBinaryMessageHandler handler) { + messageHandler = handler; + return YES; + }] + taskQueue:taskQueue]) + .andReturn(connection); + OCMStub([codec decode:[OCMArg any]]).andReturn(@"decoded message"); + [channel setMessageHandler:handler]; + } + // Channel is released, messageHandler should still retain the codec. The codec + // internally makes a `decode` call and updates the `handlerMessage` to "decoded message". + messageHandler([NSData data], ^(NSData* data){ + }); + XCTAssertEqualObjects(handlerMessage, @"decoded message"); +} + +- (void)testMethodChannelInvokeHandlerAfterChannelReleased { + NSString* channelName = @"foo"; + FlutterBinaryMessengerConnection connection = 123; + __block FlutterMethodCall* decodedMethodCall; + __block FlutterBinaryMessageHandler messageHandler; + @autoreleasepool { + id binaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + id codec = OCMProtocolMock(@protocol(FlutterMethodCodec)); + id taskQueue = OCMClassMock([NSObject class]); + FlutterMethodChannel* channel = [[FlutterMethodChannel alloc] initWithName:channelName + binaryMessenger:binaryMessenger + codec:codec + taskQueue:taskQueue]; + FlutterMethodCallHandler handler = ^(FlutterMethodCall* call, FlutterResult result) { + decodedMethodCall = call; + }; + OCMStub([binaryMessenger + setMessageHandlerOnChannel:channelName + binaryMessageHandler:[OCMArg checkWithBlock:^BOOL( + FlutterBinaryMessageHandler handler) { + messageHandler = handler; + return YES; + }] + taskQueue:taskQueue]) + .andReturn(connection); + OCMStub([codec decodeMethodCall:[OCMArg any]]) + .andReturn([FlutterMethodCall methodCallWithMethodName:@"decoded method call" + arguments:nil]); + [channel setMethodCallHandler:handler]; + } + messageHandler([NSData data], ^(NSData* data){ + }); + XCTAssertEqualObjects(decodedMethodCall.method, @"decoded method call"); +} + - (void)testMethodChannelTaskQueue { NSString* channelName = @"foo"; FlutterBinaryMessengerConnection connection = 123; diff --git a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm index 8ea26cc258063..57ef1eb752a4a 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm @@ -6,6 +6,8 @@ #include +FLUTTER_ASSERT_ARC + @implementation FlutterBinaryCodec + (instancetype)sharedInstance { static id _sharedInstance = nil; @@ -48,7 +50,7 @@ - (NSString*)decode:(NSData*)message { if (message == nil) { return nil; } - return [[[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding] autorelease]; + return [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding]; } @end diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm index eeb3ef918b0c3..9a8b17c6dd689 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import + #import "flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h" +FLUTTER_ASSERT_ARC + #pragma mark - Codec for basic message channel @implementation FlutterStandardMessageCodec { @@ -12,29 +16,23 @@ @implementation FlutterStandardMessageCodec { + (instancetype)sharedInstance { static id _sharedInstance = nil; if (!_sharedInstance) { - FlutterStandardReaderWriter* readerWriter = - [[[FlutterStandardReaderWriter alloc] init] autorelease]; + FlutterStandardReaderWriter* readerWriter = [[FlutterStandardReaderWriter alloc] init]; _sharedInstance = [[FlutterStandardMessageCodec alloc] initWithReaderWriter:readerWriter]; } return _sharedInstance; } + (instancetype)codecWithReaderWriter:(FlutterStandardReaderWriter*)readerWriter { - return [[[FlutterStandardMessageCodec alloc] initWithReaderWriter:readerWriter] autorelease]; + return [[FlutterStandardMessageCodec alloc] initWithReaderWriter:readerWriter]; } - (instancetype)initWithReaderWriter:(FlutterStandardReaderWriter*)readerWriter { self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _readerWriter = [readerWriter retain]; + _readerWriter = readerWriter; return self; } -- (void)dealloc { - [_readerWriter release]; - [super dealloc]; -} - - (NSData*)encode:(id)message { if (message == nil) { return nil; @@ -64,29 +62,23 @@ @implementation FlutterStandardMethodCodec { + (instancetype)sharedInstance { static id _sharedInstance = nil; if (!_sharedInstance) { - FlutterStandardReaderWriter* readerWriter = - [[[FlutterStandardReaderWriter alloc] init] autorelease]; + FlutterStandardReaderWriter* readerWriter = [[FlutterStandardReaderWriter alloc] init]; _sharedInstance = [[FlutterStandardMethodCodec alloc] initWithReaderWriter:readerWriter]; } return _sharedInstance; } + (instancetype)codecWithReaderWriter:(FlutterStandardReaderWriter*)readerWriter { - return [[[FlutterStandardMethodCodec alloc] initWithReaderWriter:readerWriter] autorelease]; + return [[FlutterStandardMethodCodec alloc] initWithReaderWriter:readerWriter]; } - (instancetype)initWithReaderWriter:(FlutterStandardReaderWriter*)readerWriter { self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _readerWriter = [readerWriter retain]; + _readerWriter = readerWriter; return self; } -- (void)dealloc { - [_readerWriter release]; - [super dealloc]; -} - - (NSData*)encodeMethodCall:(FlutterMethodCall*)call { NSMutableData* data = [NSMutableData dataWithCapacity:32]; FlutterStandardWriter* writer = [_readerWriter writerWithData:data]; @@ -173,7 +165,7 @@ + (instancetype)typedDataWithFloat64:(NSData*)data { } + (instancetype)typedDataWithData:(NSData*)data type:(FlutterStandardDataType)type { - return [[[FlutterStandardTypedData alloc] initWithData:data type:type] autorelease]; + return [[FlutterStandardTypedData alloc] initWithData:data type:type]; } - (instancetype)initWithData:(NSData*)data type:(FlutterStandardDataType)type { @@ -182,18 +174,13 @@ - (instancetype)initWithData:(NSData*)data type:(FlutterStandardDataType)type { NSAssert(data.length % elementSize == 0, @"Data must contain integral number of elements"); self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _data = [data retain]; + _data = [data copy]; _type = type; _elementSize = elementSize; _elementCount = data.length / elementSize; return self; } -- (void)dealloc { - [_data release]; - [super dealloc]; -} - - (BOOL)isEqual:(id)object { if (self == object) { return YES; @@ -220,15 +207,10 @@ @implementation FlutterStandardWriter { - (instancetype)initWithData:(NSMutableData*)data { self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _data = [data retain]; + _data = data; return self; } -- (void)dealloc { - [_data release]; - [super dealloc]; -} - - (void)writeByte:(UInt8)value { [_data appendBytes:&value length:1]; } @@ -269,75 +251,84 @@ - (void)writeUTF8:(NSString*)value { [self writeBytes:value.UTF8String length:length]; } -- (void)writeValue:(id)value { - if (value == nil || value == [NSNull null]) { - [self writeByte:FlutterStandardFieldNil]; - } else if ([value isKindOfClass:[NSNumber class]]) { +static void WriteKeyValuePair(const void* key, const void* value, void* context) { + [(__bridge FlutterStandardWriter*)context writeValue:(__bridge id)key]; + [(__bridge FlutterStandardWriter*)context writeValue:(__bridge id)value]; +} + +static void WriteArrayValue(const void* value, void* context) { + [(__bridge FlutterStandardWriter*)context writeValue:(__bridge id)value]; +} + +static void WriteValue(FlutterStandardWriter* writer, CFTypeRef value) { + if (value == nullptr || CFGetTypeID(value) == CFNullGetTypeID()) { + [writer writeByte:FlutterStandardFieldNil]; + } else if (CFGetTypeID(value) == CFBooleanGetTypeID()) { + [writer writeByte:(CFBooleanGetValue((CFBooleanRef)value) ? FlutterStandardFieldTrue + : FlutterStandardFieldFalse)]; + } else if (CFGetTypeID(value) == CFNumberGetTypeID()) { CFNumberRef number = (CFNumberRef)value; BOOL success = NO; - if (CFGetTypeID(number) == CFBooleanGetTypeID()) { - BOOL b = CFBooleanGetValue((CFBooleanRef)number); - [self writeByte:(b ? FlutterStandardFieldTrue : FlutterStandardFieldFalse)]; - success = YES; - } else if (CFNumberIsFloatType(number)) { + if (CFNumberIsFloatType(number)) { Float64 f; success = CFNumberGetValue(number, kCFNumberFloat64Type, &f); if (success) { - [self writeByte:FlutterStandardFieldFloat64]; - [self writeAlignment:8]; - [self writeBytes:(UInt8*)&f length:8]; + [writer writeByte:FlutterStandardFieldFloat64]; + [writer writeAlignment:8]; + [writer writeBytes:(UInt8*)&f length:8]; } } else if (CFNumberGetByteSize(number) <= 4) { SInt32 n; success = CFNumberGetValue(number, kCFNumberSInt32Type, &n); if (success) { - [self writeByte:FlutterStandardFieldInt32]; - [self writeBytes:(UInt8*)&n length:4]; + [writer writeByte:FlutterStandardFieldInt32]; + [writer writeBytes:(UInt8*)&n length:4]; } } else if (CFNumberGetByteSize(number) <= 8) { SInt64 n; success = CFNumberGetValue(number, kCFNumberSInt64Type, &n); if (success) { - [self writeByte:FlutterStandardFieldInt64]; - [self writeBytes:(UInt8*)&n length:8]; + [writer writeByte:FlutterStandardFieldInt64]; + [writer writeBytes:(UInt8*)&n length:8]; } } if (!success) { - NSLog(@"Unsupported value: %@ of number type %ld", value, CFNumberGetType(number)); - NSAssert(NO, @"Unsupported value for standard codec"); - } - } else if ([value isKindOfClass:[NSString class]]) { - NSString* string = value; - [self writeByte:FlutterStandardFieldString]; - [self writeUTF8:string]; - } else if ([value isKindOfClass:[FlutterStandardTypedData class]]) { - FlutterStandardTypedData* typedData = value; - [self writeByte:FlutterStandardFieldForDataType(typedData.type)]; - [self writeSize:typedData.elementCount]; - [self writeAlignment:typedData.elementSize]; - [self writeData:typedData.data]; - } else if ([value isKindOfClass:[NSData class]]) { - [self writeValue:[FlutterStandardTypedData typedDataWithBytes:value]]; - } else if ([value isKindOfClass:[NSArray class]]) { - NSArray* array = value; - [self writeByte:FlutterStandardFieldList]; - [self writeSize:array.count]; - for (id object in array) { - [self writeValue:object]; - } - } else if ([value isKindOfClass:[NSDictionary class]]) { - NSDictionary* dict = value; - [self writeByte:FlutterStandardFieldMap]; - [self writeSize:dict.count]; - for (id key in dict) { - [self writeValue:key]; - [self writeValue:[dict objectForKey:key]]; + NSLog(@"Unsupported value: %@ of number type %ld", (__bridge id)value, + CFNumberGetType(number)); + NSCAssert(NO, @"Unsupported value for standard codec"); } + } else if (CFGetTypeID(value) == CFStringGetTypeID()) { + [writer writeByte:FlutterStandardFieldString]; + [writer writeUTF8:(__bridge NSString*)value]; + } else if ([(__bridge id)value isKindOfClass:[FlutterStandardTypedData class]]) { + FlutterStandardTypedData* typedData = (__bridge id)value; + [writer writeByte:FlutterStandardFieldForDataType(typedData.type)]; + [writer writeSize:typedData.elementCount]; + [writer writeAlignment:typedData.elementSize]; + [writer writeData:typedData.data]; + } else if (CFGetTypeID(value) == CFDataGetTypeID()) { + [writer writeValue:[FlutterStandardTypedData typedDataWithBytes:(__bridge NSData*)value]]; + } else if (CFGetTypeID(value) == CFArrayGetTypeID()) { + CFArrayRef array = (CFArrayRef)value; + long count = CFArrayGetCount(array); + [writer writeByte:FlutterStandardFieldList]; + [writer writeSize:count]; + CFArrayApplyFunction(array, CFRangeMake(0, count), WriteArrayValue, (void*)writer); + } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) { + CFDictionaryRef dict = (CFDictionaryRef)value; + [writer writeByte:FlutterStandardFieldMap]; + [writer writeSize:CFDictionaryGetCount(dict)]; + CFDictionaryApplyFunction(dict, WriteKeyValuePair, (void*)writer); } else { - NSLog(@"Unsupported value: %@ of type %@", value, [value class]); - NSAssert(NO, @"Unsupported value for standard codec"); + NSLog(@"Unsupported value: %@ of type %@", (__bridge id)value, [(__bridge id)value class]); + NSCAssert(NO, @"Unsupported value for standard codec"); } } + +- (void)writeValue:(id)value { + WriteValue(self, (__bridge CFTypeRef)value); +} + @end @implementation FlutterStandardReader { @@ -348,16 +339,11 @@ @implementation FlutterStandardReader { - (instancetype)initWithData:(NSData*)data { self = [super init]; NSAssert(self, @"Super init cannot be nil"); - _data = [data retain]; + _data = [data copy]; _range = NSMakeRange(0, 0); return self; } -- (void)dealloc { - [_data release]; - [super dealloc]; -} - - (BOOL)hasMore { return _range.location < _data.length; } @@ -389,16 +375,27 @@ - (UInt32)readSize { } } -- (NSData*)readData:(NSUInteger)length { +- (CFDataRef)readDataRef:(NSUInteger)length { _range.length = length; - NSData* data = [_data subdataWithRange:_range]; + CFDataRef bytes = (__bridge CFDataRef)[_data subdataWithRange:_range]; _range.location += _range.length; - return data; + return bytes; +} + +- (NSData*)readData:(NSUInteger)length { + return (__bridge NSData*)[self readDataRef:length]; +} + +static CFTypeRef ReadUTF8(FlutterStandardReader* reader) { + CFDataRef bytes = [reader readDataRef:[reader readSize]]; + CFStringRef string = + CFStringCreateWithBytes(kCFAllocatorDefault, CFDataGetBytePtr(bytes), CFDataGetLength(bytes), + kCFStringEncodingUTF8, false); + return CFAutorelease(string); } - (NSString*)readUTF8 { - NSData* bytes = [self readData:[self readSize]]; - return [[[NSString alloc] initWithData:bytes encoding:NSUTF8StringEncoding] autorelease]; + return (__bridge NSString*)ReadUTF8(self); } - (void)readAlignment:(UInt8)alignment { @@ -412,80 +409,91 @@ - (FlutterStandardTypedData*)readTypedDataOfType:(FlutterStandardDataType)type { UInt32 elementCount = [self readSize]; UInt8 elementSize = elementSizeForFlutterStandardDataType(type); [self readAlignment:elementSize]; - NSData* data = [self readData:elementCount * elementSize]; - return [FlutterStandardTypedData typedDataWithData:data type:type]; + return [FlutterStandardTypedData + typedDataWithData:(__bridge NSData*)[self readDataRef:elementCount * elementSize] + type:type]; } - (nullable id)readValue { - return [self readValueOfType:[self readByte]]; + return (__bridge id)ReadValue(self); +} + +static CFTypeRef ReadValue(FlutterStandardReader* codec) { + return ReadValueOfType(codec, [codec readByte]); } - (nullable id)readValueOfType:(UInt8)type { + return (__bridge id)ReadValueOfType(self, type); +} + +static CFTypeRef ReadValueOfType(FlutterStandardReader* codec, UInt8 type) { FlutterStandardField field = (FlutterStandardField)type; switch (field) { case FlutterStandardFieldNil: return nil; case FlutterStandardFieldTrue: - return @YES; + return (__bridge CFBooleanRef) @YES; case FlutterStandardFieldFalse: - return @NO; + return (__bridge CFBooleanRef) @NO; case FlutterStandardFieldInt32: { SInt32 value; - [self readBytes:&value length:4]; - return @(value); + [codec readBytes:&value length:4]; + return (__bridge CFNumberRef) @(value); } case FlutterStandardFieldInt64: { SInt64 value; - [self readBytes:&value length:8]; - return @(value); + [codec readBytes:&value length:8]; + return (__bridge CFNumberRef) @(value); } case FlutterStandardFieldFloat64: { Float64 value; - [self readAlignment:8]; - [self readBytes:&value length:8]; - return [NSNumber numberWithDouble:value]; + [codec readAlignment:8]; + [codec readBytes:&value length:8]; + return (__bridge CFNumberRef)[NSNumber numberWithDouble:value]; } case FlutterStandardFieldIntHex: case FlutterStandardFieldString: - return [self readUTF8]; + return ReadUTF8(codec); case FlutterStandardFieldUInt8Data: case FlutterStandardFieldInt32Data: case FlutterStandardFieldInt64Data: case FlutterStandardFieldFloat32Data: case FlutterStandardFieldFloat64Data: - return [self readTypedDataOfType:FlutterStandardDataTypeForField(field)]; + return (__bridge CFTypeRef)[codec readTypedDataOfType:FlutterStandardDataTypeForField(field)]; case FlutterStandardFieldList: { - UInt32 length = [self readSize]; - NSMutableArray* array = [NSMutableArray arrayWithCapacity:length]; + UInt32 length = [codec readSize]; + CFMutableArrayRef array = + CFArrayCreateMutable(kCFAllocatorDefault, length, &kCFTypeArrayCallBacks); for (UInt32 i = 0; i < length; i++) { - id value = [self readValue]; - [array addObject:(value == nil ? [NSNull null] : value)]; + CFTypeRef value = ReadValue(codec); + CFArrayAppendValue(array, (value == nil ? kCFNull : value)); } - return array; + return CFAutorelease(array); } case FlutterStandardFieldMap: { - UInt32 size = [self readSize]; - NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:size]; + UInt32 size = [codec readSize]; + CFMutableDictionaryRef dict = + CFDictionaryCreateMutable(kCFAllocatorDefault, size, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); for (UInt32 i = 0; i < size; i++) { - id key = [self readValue]; - id val = [self readValue]; - [dict setObject:(val == nil ? [NSNull null] : val) - forKey:(key == nil ? [NSNull null] : key)]; + CFTypeRef key = ReadValue(codec); + CFTypeRef val = ReadValue(codec); + CFDictionaryAddValue(dict, (key == nil ? kCFNull : key), (val == nil ? kCFNull : val)); } - return dict; + return CFAutorelease(dict); } default: - NSAssert(NO, @"Corrupted standard message"); + NSCAssert(NO, @"Corrupted standard message"); } } @end @implementation FlutterStandardReaderWriter - (FlutterStandardWriter*)writerWithData:(NSMutableData*)data { - return [[[FlutterStandardWriter alloc] initWithData:data] autorelease]; + return [[FlutterStandardWriter alloc] initWithData:data]; } - (FlutterStandardReader*)readerWithData:(NSData*)data { - return [[[FlutterStandardReader alloc] initWithData:data] autorelease]; + return [[FlutterStandardReader alloc] initWithData:data]; } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterRestorationPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterRestorationPluginTest.mm index 36de96d451a8d..d61a29e0f31fd 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterRestorationPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterRestorationPluginTest.mm @@ -80,7 +80,7 @@ - (void)testRestorationEnabledWaitsForData { [restorationPlugin setRestorationData:data]; XCTAssertEqual([capturedResult count], 2u); XCTAssertEqual([capturedResult objectForKey:@"enabled"], @YES); - XCTAssertEqual([[capturedResult objectForKey:@"data"] data], data); + XCTAssertEqualObjects([[capturedResult objectForKey:@"data"] data], data); } - (void)testRestorationDisabledRespondsRightAway { @@ -112,7 +112,7 @@ - (void)testRespondsRightAwayWhenDataIsSet { }]; XCTAssertEqual([capturedResult count], 2u); XCTAssertEqual([capturedResult objectForKey:@"enabled"], @YES); - XCTAssertEqual([[capturedResult objectForKey:@"data"] data], data); + XCTAssertEqualObjects([[capturedResult objectForKey:@"data"] data], data); } - (void)testRespondsWithNoDataWhenRestorationIsCompletedWithoutData { @@ -161,7 +161,7 @@ - (void)testReturnsDataSetByFramework { result:^(id _Nullable result) { XCTAssertNil(result); }]; - XCTAssertEqual([restorationPlugin restorationData], data); + XCTAssertEqualObjects([restorationPlugin restorationData], data); } - (void)testRespondsWithDataSetByFramework { @@ -177,7 +177,7 @@ - (void)testRespondsWithDataSetByFramework { result:^(id _Nullable result) { XCTAssertNil(result); }]; - XCTAssertEqual([restorationPlugin restorationData], data); + XCTAssertEqualObjects([restorationPlugin restorationData], data); __block id capturedResult; methodCall = [FlutterMethodCall methodCallWithMethodName:@"get" arguments:nil]; @@ -187,7 +187,7 @@ - (void)testRespondsWithDataSetByFramework { }]; XCTAssertEqual([capturedResult count], 2u); XCTAssertEqual([capturedResult objectForKey:@"enabled"], @YES); - XCTAssertEqual([[capturedResult objectForKey:@"data"] data], data); + XCTAssertEqualObjects([[capturedResult objectForKey:@"data"] data], data); } - (void)testResetClearsData { @@ -203,7 +203,7 @@ - (void)testResetClearsData { result:^(id _Nullable result) { XCTAssertNil(result); }]; - XCTAssertEqual([restorationPlugin restorationData], data); + XCTAssertEqualObjects([restorationPlugin restorationData], data); [restorationPlugin reset]; XCTAssertNil([restorationPlugin restorationData]);