From ae4e6d16085cb3d1af39854e8fb82b44a102064f Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 19 Feb 2025 14:32:21 +1100 Subject: [PATCH] [ffigen] Allow blocks to keep their isolates alive --- pkgs/ffigen/CHANGELOG.md | 2 + .../lib/src/code_generator/objc_block.dart | 34 +- pkgs/ffigen/pubspec.yaml | 2 +- .../test/native_objc_test/block_test.dart | 188 ++++++++ pkgs/objective_c/CHANGELOG.md | 4 + pkgs/objective_c/lib/src/internal.dart | 32 +- .../src/objective_c_bindings_generated.dart | 435 +++++++++++------- pkgs/objective_c/pubspec.yaml | 4 +- 8 files changed, 516 insertions(+), 185 deletions(-) diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 92c4a40d8d..4d90b47e4b 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -4,6 +4,8 @@ locate Apple APIs: `$XCODE`, `$IOS_SDK`, and `$MACOS_SDK`. - __Breaking change__: Change the `usrTypeMappings` field of `Config`'s factory constructor from a `List` to a `Map`. +- Add a `keepIsolateAlive` parameter to the block constructors that allows a + block to keep its owner isolate alive. ## 17.0.0 diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart index d46c3d9130..1cb279c4fb 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart @@ -240,8 +240,12 @@ abstract final class $name { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static $blockType fromFunction(${func.dartType} fn) => - $blockType($newClosureBlock($closureCallable, $convFn), + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static $blockType fromFunction(${func.dartType} fn, + {bool keepIsolateAlive = false}) => + $blockType($newClosureBlock($closureCallable, $convFn, keepIsolateAlive), retain: false, release: true); '''); @@ -274,11 +278,12 @@ abstract final class $name { /// but only supports void functions, and is not run synchronously. See /// NativeCallable.listener for more details. /// - /// Note that unlike the default behavior of NativeCallable.listener, listener - /// blocks do not keep the isolate alive. - static $blockType listener(${func.dartType} fn) { - final raw = $newClosureBlock( - $listenerCallable.nativeFunction.cast(), $listenerConvFn); + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static $blockType listener(${func.dartType} fn, + {bool keepIsolateAlive = false}) { + final raw = $newClosureBlock($listenerCallable.nativeFunction.cast(), + $listenerConvFn, keepIsolateAlive); final wrapper = $wrapListenerFn(raw); $releaseFn(raw.cast()); return $blockType(wrapper, retain: false, release: true); @@ -290,14 +295,17 @@ abstract final class $name { /// caller until the callback is handled by the Dart isolate that created /// the block. Async functions are not supported. /// - /// This block does not keep the owner isolate alive. If the owner isolate has - /// shut down, and the block is invoked by native code, it may block + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. If the owner isolate + /// has shut down, and the block is invoked by native code, it may block /// indefinitely, or have other undefined behavior. - static $blockType blocking(${func.dartType} fn) { - final raw = $newClosureBlock( - $blockingCallable.nativeFunction.cast(), $listenerConvFn); + static $blockType blocking(${func.dartType} fn, + {bool keepIsolateAlive = false}) { + final raw = $newClosureBlock($blockingCallable.nativeFunction.cast(), + $listenerConvFn, keepIsolateAlive); final rawListener = $newClosureBlock( - $blockingListenerCallable.nativeFunction.cast(), $listenerConvFn); + $blockingListenerCallable.nativeFunction.cast(), + $listenerConvFn, keepIsolateAlive); final wrapper = $wrapBlockingBlockFn($wrapBlockingFn, raw, rawListener); $releaseFn(raw.cast()); $releaseFn(rawListener.cast()); diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml index e40aa13ff4..4012351d26 100644 --- a/pkgs/ffigen/pubspec.yaml +++ b/pkgs/ffigen/pubspec.yaml @@ -41,7 +41,7 @@ dev_dependencies: dart_flutter_team_lints: ^2.0.0 json_schema: ^5.1.1 leak_tracker: ^10.0.7 - objective_c: ^6.0.0 + objective_c: ^7.0.0 test: ^1.16.2 dependency_overrides: diff --git a/pkgs/ffigen/test/native_objc_test/block_test.dart b/pkgs/ffigen/test/native_objc_test/block_test.dart index 8a4b7136ed..2dc2d0d18d 100644 --- a/pkgs/ffigen/test/native_objc_test/block_test.dart +++ b/pkgs/ffigen/test/native_objc_test/block_test.dart @@ -10,6 +10,7 @@ import 'dart:async'; import 'dart:ffi'; import 'dart:io'; +import 'dart:isolate'; import 'package:ffi/ffi.dart'; import 'package:objective_c/objective_c.dart'; @@ -863,6 +864,193 @@ void main() { expect(objectRetainCount(objectPtr), 0); } }); + + test('Block.fromFunction, keepIsolateAlive', () async { + final isolateSendPort = Completer(); + final blocksCreated = Completer(); + final blkKeepAliveDestroyed = Completer(); + final receivePort = RawReceivePort((msg) { + if (msg is SendPort) { + isolateSendPort.complete(msg); + } else if (msg == 'Blocks created') { + blocksCreated.complete(); + } else if (msg == 'blkKeepAlive destroyed') { + blkKeepAliveDestroyed.complete(); + } + }); + + var isExited = false; + late final RawReceivePort exitPort; + exitPort = RawReceivePort((_) { + isExited = true; + exitPort.close(); + }); + + final isolate = Isolate.spawn((sendPort) { + final blkKeepAlive = + VoidBlock.fromFunction(() {}, keepIsolateAlive: true); + final blkDontKeepAlive = + VoidBlock.fromFunction(() {}, keepIsolateAlive: false); + sendPort.send('Blocks created'); + + final isolatePort = RawReceivePort((msg) { + if (msg == 'Destroy blkKeepAlive') { + blkKeepAlive.ref.release(); + sendPort.send('blkKeepAlive destroyed'); + } + }) + ..keepIsolateAlive = false; + + sendPort.send(isolatePort.sendPort); + }, receivePort.sendPort, onExit: exitPort.sendPort); + + await blocksCreated.future; + + doGC(); + await Future.delayed(Duration.zero); // Let dispose message arrive. + doGC(); + await Future.delayed(Duration.zero); // Let exit message arrive. + + // Both blocks are still alive. + expect(isExited, isFalse); + + (await isolateSendPort.future).send('Destroy blkKeepAlive'); + await blkKeepAliveDestroyed.future; + + doGC(); + await Future.delayed(Duration.zero); // Let dispose message arrive. + doGC(); + await Future.delayed(Duration.zero); // Let exit message arrive. + + // Only blkDontKeepAlive is alive. + expect(isExited, isTrue); + + receivePort.close(); + }, skip: !canDoGC); + + test('Block.listener, keepIsolateAlive', () async { + final isolateSendPort = Completer(); + final blocksCreated = Completer(); + final blkKeepAliveDestroyed = Completer(); + final receivePort = RawReceivePort((msg) { + if (msg is SendPort) { + isolateSendPort.complete(msg); + } else if (msg == 'Blocks created') { + blocksCreated.complete(); + } else if (msg == 'blkKeepAlive destroyed') { + blkKeepAliveDestroyed.complete(); + } + }); + + var isExited = false; + late final RawReceivePort exitPort; + exitPort = RawReceivePort((_) { + isExited = true; + exitPort.close(); + }); + + final isolate = Isolate.spawn((sendPort) { + final blkKeepAlive = VoidBlock.listener(() {}, keepIsolateAlive: true); + final blkDontKeepAlive = + VoidBlock.listener(() {}, keepIsolateAlive: false); + sendPort.send('Blocks created'); + + final isolatePort = RawReceivePort((msg) { + if (msg == 'Destroy blkKeepAlive') { + blkKeepAlive.ref.release(); + sendPort.send('blkKeepAlive destroyed'); + } + }) + ..keepIsolateAlive = false; + + sendPort.send(isolatePort.sendPort); + }, receivePort.sendPort, onExit: exitPort.sendPort); + + await blocksCreated.future; + + doGC(); + await Future.delayed(Duration.zero); // Let dispose message arrive. + doGC(); + await Future.delayed(Duration.zero); // Let exit message arrive. + + // Both blocks are still alive. + expect(isExited, isFalse); + + (await isolateSendPort.future).send('Destroy blkKeepAlive'); + await blkKeepAliveDestroyed.future; + + doGC(); + await Future.delayed(Duration.zero); // Let dispose message arrive. + doGC(); + await Future.delayed(Duration.zero); // Let exit message arrive. + + // Only blkDontKeepAlive is alive. + expect(isExited, isTrue); + + receivePort.close(); + }, skip: !canDoGC); + + test('Block.blocking, keepIsolateAlive', () async { + final isolateSendPort = Completer(); + final blocksCreated = Completer(); + final blkKeepAliveDestroyed = Completer(); + final receivePort = RawReceivePort((msg) { + if (msg is SendPort) { + isolateSendPort.complete(msg); + } else if (msg == 'Blocks created') { + blocksCreated.complete(); + } else if (msg == 'blkKeepAlive destroyed') { + blkKeepAliveDestroyed.complete(); + } + }); + + var isExited = false; + late final RawReceivePort exitPort; + exitPort = RawReceivePort((_) { + isExited = true; + exitPort.close(); + }); + + final isolate = Isolate.spawn((sendPort) { + final blkKeepAlive = VoidBlock.blocking(() {}, keepIsolateAlive: true); + final blkDontKeepAlive = + VoidBlock.blocking(() {}, keepIsolateAlive: false); + sendPort.send('Blocks created'); + + final isolatePort = RawReceivePort((msg) { + if (msg == 'Destroy blkKeepAlive') { + blkKeepAlive.ref.release(); + sendPort.send('blkKeepAlive destroyed'); + } + }) + ..keepIsolateAlive = false; + + sendPort.send(isolatePort.sendPort); + }, receivePort.sendPort, onExit: exitPort.sendPort); + + await blocksCreated.future; + + doGC(); + await Future.delayed(Duration.zero); // Let dispose message arrive. + doGC(); + await Future.delayed(Duration.zero); // Let exit message arrive. + + // Both blocks are still alive. + expect(isExited, isFalse); + + (await isolateSendPort.future).send('Destroy blkKeepAlive'); + await blkKeepAliveDestroyed.future; + + doGC(); + await Future.delayed(Duration.zero); // Let dispose message arrive. + doGC(); + await Future.delayed(Duration.zero); // Let exit message arrive. + + // Only blkDontKeepAlive is alive. + expect(isExited, isTrue); + + receivePort.close(); + }, skip: !canDoGC); }); } diff --git a/pkgs/objective_c/CHANGELOG.md b/pkgs/objective_c/CHANGELOG.md index 7fee7a54ea..841c1c73c6 100644 --- a/pkgs/objective_c/CHANGELOG.md +++ b/pkgs/objective_c/CHANGELOG.md @@ -1,3 +1,7 @@ +## 7.0.0 + +- Use ffigen 18.0.0 + ## 6.0.0 - Use ffigen 17.0.0 diff --git a/pkgs/objective_c/lib/src/internal.dart b/pkgs/objective_c/lib/src/internal.dart index 01a96f328b..1a1d4f97d6 100644 --- a/pkgs/objective_c/lib/src/internal.dart +++ b/pkgs/objective_c/lib/src/internal.dart @@ -389,18 +389,24 @@ BlockPtr _newBlock(VoidPtr invoke, VoidPtr target, const int _blockHasCopyDispose = 1 << 25; /// Only for use by ffigen bindings. -BlockPtr newClosureBlock(VoidPtr invoke, Function fn) => _newBlock( - invoke, - _registerBlockClosure(fn), - _closureBlockDesc, - _blockClosureDisposer.sendPort.nativePort, - _blockHasCopyDispose); +BlockPtr newClosureBlock(VoidPtr invoke, Function fn, bool keepIsolateAlive) => + _newBlock( + invoke, + _registerBlockClosure(fn, keepIsolateAlive), + _closureBlockDesc, + _blockClosureDisposer.sendPort.nativePort, + _blockHasCopyDispose); /// Only for use by ffigen bindings. BlockPtr newPointerBlock(VoidPtr invoke, VoidPtr target) => _newBlock(invoke, target, _pointerBlockDesc, 0, 0); -final _blockClosureRegistry = {}; +typedef _RegEntry = ({ + Function closure, + RawReceivePort? keepAlivePort, +}); + +final _blockClosureRegistry = {}; int _blockClosureRegistryLastId = 0; @@ -409,15 +415,19 @@ final _blockClosureDisposer = () { return RawReceivePort((dynamic msg) { final id = msg as int; assert(_blockClosureRegistry.containsKey(id)); - _blockClosureRegistry.remove(id); + final entry = _blockClosureRegistry.remove(id)!; + entry.keepAlivePort?.close(); }, 'ObjCBlockClosureDisposer') ..keepIsolateAlive = false; }(); -VoidPtr _registerBlockClosure(Function closure) { +VoidPtr _registerBlockClosure(Function closure, bool keepIsolateAlive) { ++_blockClosureRegistryLastId; assert(!_blockClosureRegistry.containsKey(_blockClosureRegistryLastId)); - _blockClosureRegistry[_blockClosureRegistryLastId] = closure; + _blockClosureRegistry[_blockClosureRegistryLastId] = ( + closure: closure, + keepAlivePort: keepIsolateAlive ? RawReceivePort() : null, + ); return VoidPtr.fromAddress(_blockClosureRegistryLastId); } @@ -425,7 +435,7 @@ VoidPtr _registerBlockClosure(Function closure) { Function getBlockClosure(BlockPtr block) { var id = block.ref.target.address; assert(_blockClosureRegistry.containsKey(id)); - return _blockClosureRegistry[id]!; + return _blockClosureRegistry[id]!.closure; } typedef NewWaiterFn = NativeFunction; diff --git a/pkgs/objective_c/lib/src/objective_c_bindings_generated.dart b/pkgs/objective_c/lib/src/objective_c_bindings_generated.dart index 560c1e0ef6..419d12d6d0 100644 --- a/pkgs/objective_c/lib/src/objective_c_bindings_generated.dart +++ b/pkgs/objective_c/lib/src/objective_c_bindings_generated.dart @@ -11717,13 +11717,18 @@ abstract final class ObjCBlock_NSArray_ffiVoid { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock)> fromFunction( - NSArray Function(ffi.Pointer) fn) => + NSArray Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock)>( objc.newClosureBlock( _ObjCBlock_NSArray_ffiVoid_closureCallable, (ffi.Pointer arg0) => - fn(arg0).ref.retainAndAutorelease()), + fn(arg0).ref.retainAndAutorelease(), + keepIsolateAlive), retain: false, release: true); } @@ -11814,16 +11819,20 @@ abstract final class ObjCBlock_NSItemProviderRepresentationVisibility_ffiVoid_NS /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock, NSString)> fromFunction( NSItemProviderRepresentationVisibility Function( ffi.Pointer, NSString) - fn) => + fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock, NSString)>( objc.newClosureBlock( _ObjCBlock_NSItemProviderRepresentationVisibility_ffiVoid_NSString_closureCallable, (ffi.Pointer arg0, ffi.Pointer arg1) => - fn(arg0, NSString.castFromPointer(arg1, retain: true, release: true)) - .value), + fn(arg0, NSString.castFromPointer(arg1, retain: true, release: true)).value, + keepIsolateAlive), retain: false, release: true); } @@ -11904,13 +11913,18 @@ abstract final class ObjCBlock_NSString_ffiVoid { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock)> fromFunction( - NSString Function(ffi.Pointer) fn) => + NSString Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock)>( objc.newClosureBlock( _ObjCBlock_NSString_ffiVoid_closureCallable, (ffi.Pointer arg0) => - fn(arg0).ref.retainAndAutorelease()), + fn(arg0).ref.retainAndAutorelease(), + keepIsolateAlive), retain: false, release: true); } @@ -11989,12 +12003,17 @@ abstract final class ObjCBlock_NSUInteger_ffiVoid { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock)> - fromFunction(int Function(ffi.Pointer) fn) => + fromFunction(int Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock)>( objc.newClosureBlock( _ObjCBlock_NSUInteger_ffiVoid_closureCallable, - (ffi.Pointer arg0) => fn(arg0)), + (ffi.Pointer arg0) => fn(arg0), + keepIsolateAlive), retain: false, release: true); } @@ -12113,19 +12132,24 @@ abstract final class ObjCBlock_NSUInteger_ffiVoid_NSFastEnumerationState_objcObj /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc.ObjCBlock< - ffi.UnsignedLong Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer>, - ffi.UnsignedLong)> fromFunction(int Function(ffi.Pointer, ffi.Pointer, ffi.Pointer>, int) fn) => - objc.ObjCBlock, ffi.Pointer, ffi.Pointer>, ffi.UnsignedLong)>( - objc.newClosureBlock( - _ObjCBlock_NSUInteger_ffiVoid_NSFastEnumerationState_objcObjCObject_NSUInteger_closureCallable, - (ffi.Pointer arg0, ffi.Pointer arg1, ffi.Pointer> arg2, int arg3) => - fn(arg0, arg1, arg2, arg3)), - retain: false, - release: true); + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc + .ObjCBlock, ffi.Pointer, ffi.Pointer>, ffi.UnsignedLong)> + fromFunction(int Function(ffi.Pointer, ffi.Pointer, ffi.Pointer>, int) fn, + {bool keepIsolateAlive = false}) => + objc.ObjCBlock, ffi.Pointer, ffi.Pointer>, ffi.UnsignedLong)>( + objc.newClosureBlock( + _ObjCBlock_NSUInteger_ffiVoid_NSFastEnumerationState_objcObjCObject_NSUInteger_closureCallable, + (ffi.Pointer arg0, + ffi.Pointer arg1, + ffi.Pointer> arg2, + int arg3) => + fn(arg0, arg1, arg2, arg3), + keepIsolateAlive), + retain: false, + release: true); } /// Call operator for `objc.ObjCBlock, ffi.Pointer, ffi.Pointer>, ffi.UnsignedLong)>`. @@ -12214,11 +12238,15 @@ abstract final class ObjCBlock_NSZone_ffiVoid { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock Function(ffi.Pointer)> - fromFunction(ffi.Pointer Function(ffi.Pointer) fn) => + fromFunction(ffi.Pointer Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock Function(ffi.Pointer)>( objc.newClosureBlock(_ObjCBlock_NSZone_ffiVoid_closureCallable, - (ffi.Pointer arg0) => fn(arg0)), + (ffi.Pointer arg0) => fn(arg0), keepIsolateAlive), retain: false, release: true); } @@ -12289,11 +12317,15 @@ abstract final class ObjCBlock_bool_ffiVoid { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock)> fromFunction( - bool Function(ffi.Pointer) fn) => + bool Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock)>( objc.newClosureBlock(_ObjCBlock_bool_ffiVoid_closureCallable, - (ffi.Pointer arg0) => fn(arg0)), + (ffi.Pointer arg0) => fn(arg0), keepIsolateAlive), retain: false, release: true); } @@ -12375,18 +12407,21 @@ abstract final class ObjCBlock_bool_ffiVoid_Protocol { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc.ObjCBlock, Protocol)> - fromFunction(bool Function(ffi.Pointer, Protocol) fn) => - objc.ObjCBlock, Protocol)>( - objc.newClosureBlock( - _ObjCBlock_bool_ffiVoid_Protocol_closureCallable, - (ffi.Pointer arg0, ffi.Pointer arg1) => - fn( - arg0, - Protocol.castFromPointer(arg1, - retain: true, release: true))), - retain: false, - release: true); + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock< + ffi.Bool Function(ffi.Pointer, Protocol)> fromFunction( + bool Function(ffi.Pointer, Protocol) fn, + {bool keepIsolateAlive = false}) => + objc.ObjCBlock, Protocol)>( + objc.newClosureBlock( + _ObjCBlock_bool_ffiVoid_Protocol_closureCallable, + (ffi.Pointer arg0, ffi.Pointer arg1) => + fn(arg0, Protocol.castFromPointer(arg1, retain: true, release: true)), + keepIsolateAlive), + retain: false, + release: true); } /// Call operator for `objc.ObjCBlock, Protocol)>`. @@ -12472,18 +12507,21 @@ abstract final class ObjCBlock_bool_ffiVoid_objcObjCObject { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc.ObjCBlock, ffi.Pointer)> - fromFunction( - bool Function(ffi.Pointer, objc.ObjCObjectBase) fn) => - objc.ObjCBlock< - ffi.Bool Function( - ffi.Pointer, ffi.Pointer)>( - objc.newClosureBlock( - _ObjCBlock_bool_ffiVoid_objcObjCObject_closureCallable, - (ffi.Pointer arg0, ffi.Pointer arg1) => - fn(arg0, objc.ObjCObjectBase(arg1, retain: true, release: true))), - retain: false, - release: true); + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock< + ffi.Bool Function(ffi.Pointer, ffi.Pointer)> fromFunction( + bool Function(ffi.Pointer, objc.ObjCObjectBase) fn, + {bool keepIsolateAlive = false}) => + objc.ObjCBlock, ffi.Pointer)>( + objc.newClosureBlock( + _ObjCBlock_bool_ffiVoid_objcObjCObject_closureCallable, + (ffi.Pointer arg0, ffi.Pointer arg1) => + fn(arg0, objc.ObjCObjectBase(arg1, retain: true, release: true)), + keepIsolateAlive), + retain: false, + release: true); } /// Call operator for `objc.ObjCBlock, ffi.Pointer)>`. @@ -12570,16 +12608,19 @@ abstract final class ObjCBlock_bool_ffiVoid_objcObjCSelector { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc - .ObjCBlock, ffi.Pointer)> - fromFunction(bool Function(ffi.Pointer, ffi.Pointer) fn) => - objc.ObjCBlock< - ffi.Bool Function( - ffi.Pointer, ffi.Pointer)>( + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock< + ffi.Bool Function(ffi.Pointer, ffi.Pointer)> + fromFunction(bool Function(ffi.Pointer, ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => + objc.ObjCBlock, ffi.Pointer)>( objc.newClosureBlock( _ObjCBlock_bool_ffiVoid_objcObjCSelector_closureCallable, (ffi.Pointer arg0, ffi.Pointer arg1) => - fn(arg0, arg1)), + fn(arg0, arg1), + keepIsolateAlive), retain: false, release: true); } @@ -12773,18 +12814,20 @@ abstract final class ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCO /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock?, NSError)>, ffi.Pointer, NSDictionary)> - fromFunction(void Function(objc.ObjCBlock?, NSError)>, objc.ObjCObjectBase, NSDictionary) fn) => + fromFunction(void Function(objc.ObjCBlock?, NSError)>, objc.ObjCObjectBase, NSDictionary) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock?, NSError)>, ffi.Pointer, NSDictionary)>( objc.newClosureBlock( _ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCObject_NSDictionary_closureCallable, - (ffi.Pointer arg0, - ffi.Pointer arg1, - ffi.Pointer arg2) => - fn( - ObjCBlock_ffiVoid_idNSSecureCoding_NSError.castFromPointer(arg0, retain: true, release: true), - objc.ObjCObjectBase(arg1, retain: true, release: true), - NSDictionary.castFromPointer(arg2, retain: true, release: true))), + (ffi.Pointer arg0, ffi.Pointer arg1, ffi.Pointer arg2) => fn( + ObjCBlock_ffiVoid_idNSSecureCoding_NSError.castFromPointer(arg0, retain: true, release: true), + objc.ObjCObjectBase(arg1, retain: true, release: true), + NSDictionary.castFromPointer(arg2, retain: true, release: true)), + keepIsolateAlive), retain: false, release: true); @@ -12795,8 +12838,8 @@ abstract final class ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCO /// but only supports void functions, and is not run synchronously. See /// NativeCallable.listener for more details. /// - /// Note that unlike the default behavior of NativeCallable.listener, listener - /// blocks do not keep the isolate alive. + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock< ffi.Void Function( objc.ObjCBlock< @@ -12808,7 +12851,8 @@ abstract final class ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCO ffi.Void Function(ffi.Pointer?, NSError)>, objc.ObjCObjectBase, NSDictionary) - fn) { + fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCObject_NSDictionary_listenerCallable .nativeFunction @@ -12821,7 +12865,8 @@ abstract final class ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCO retain: false, release: true), objc.ObjCObjectBase(arg1, retain: false, release: true), NSDictionary.castFromPointer(arg2, - retain: false, release: true))); + retain: false, release: true)), + keepIsolateAlive); final wrapper = _ObjectiveCBindings_wrapListenerBlock_1b3bb6a(raw); objc.objectRelease(raw.cast()); return objc.ObjCBlock< @@ -12838,8 +12883,9 @@ abstract final class ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCO /// caller until the callback is handled by the Dart isolate that created /// the block. Async functions are not supported. /// - /// This block does not keep the owner isolate alive. If the owner isolate has - /// shut down, and the block is invoked by native code, it may block + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. If the owner isolate + /// has shut down, and the block is invoked by native code, it may block /// indefinitely, or have other undefined behavior. static objc.ObjCBlock< ffi.Void Function( @@ -12852,7 +12898,8 @@ abstract final class ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCO ffi.Void Function(ffi.Pointer?, NSError)>, objc.ObjCObjectBase, NSDictionary) - fn) { + fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCObject_NSDictionary_blockingCallable .nativeFunction @@ -12865,7 +12912,8 @@ abstract final class ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCO retain: false, release: true), objc.ObjCObjectBase(arg1, retain: false, release: true), NSDictionary.castFromPointer(arg2, - retain: false, release: true))); + retain: false, release: true)), + keepIsolateAlive); final rawListener = objc.newClosureBlock( _ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCObject_NSDictionary_blockingListenerCallable .nativeFunction @@ -12878,7 +12926,8 @@ abstract final class ObjCBlock_ffiVoid_NSItemProviderCompletionHandler_objcObjCO retain: false, release: true), objc.ObjCObjectBase(arg1, retain: false, release: true), NSDictionary.castFromPointer(arg2, - retain: false, release: true))); + retain: false, release: true)), + keepIsolateAlive); final wrapper = objc.wrapBlockingBlock( _ObjectiveCBindings_wrapBlockingBlock_1b3bb6a, raw, rawListener); objc.objectRelease(raw.cast()); @@ -13017,11 +13066,15 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock)> fromFunction( - void Function(ffi.Pointer) fn) => + void Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock)>( objc.newClosureBlock(_ObjCBlock_ffiVoid_ffiVoid_closureCallable, - (ffi.Pointer arg0) => fn(arg0)), + (ffi.Pointer arg0) => fn(arg0), keepIsolateAlive), retain: false, release: true); @@ -13032,13 +13085,15 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid { /// but only supports void functions, and is not run synchronously. See /// NativeCallable.listener for more details. /// - /// Note that unlike the default behavior of NativeCallable.listener, listener - /// blocks do not keep the isolate alive. + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock)> listener( - void Function(ffi.Pointer) fn) { + void Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_listenerCallable.nativeFunction.cast(), - (ffi.Pointer arg0) => fn(arg0)); + (ffi.Pointer arg0) => fn(arg0), + keepIsolateAlive); final wrapper = _ObjectiveCBindings_wrapListenerBlock_ovsamd(raw); objc.objectRelease(raw.cast()); return objc.ObjCBlock)>(wrapper, @@ -13051,18 +13106,22 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid { /// caller until the callback is handled by the Dart isolate that created /// the block. Async functions are not supported. /// - /// This block does not keep the owner isolate alive. If the owner isolate has - /// shut down, and the block is invoked by native code, it may block + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. If the owner isolate + /// has shut down, and the block is invoked by native code, it may block /// indefinitely, or have other undefined behavior. static objc.ObjCBlock)> blocking( - void Function(ffi.Pointer) fn) { + void Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_blockingCallable.nativeFunction.cast(), - (ffi.Pointer arg0) => fn(arg0)); + (ffi.Pointer arg0) => fn(arg0), + keepIsolateAlive); final rawListener = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_blockingListenerCallable.nativeFunction .cast(), - (ffi.Pointer arg0) => fn(arg0)); + (ffi.Pointer arg0) => fn(arg0), + keepIsolateAlive); final wrapper = objc.wrapBlockingBlock( _ObjectiveCBindings_wrapBlockingBlock_ovsamd, raw, rawListener); objc.objectRelease(raw.cast()); @@ -13210,15 +13269,19 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSCoder { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock< ffi.Void Function(ffi.Pointer, NSCoder)> fromFunction( - void Function(ffi.Pointer, NSCoder) fn) => + void Function(ffi.Pointer, NSCoder) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock, NSCoder)>( objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_NSCoder_closureCallable, - (ffi.Pointer arg0, ffi.Pointer arg1) => fn( - arg0, - NSCoder.castFromPointer(arg1, retain: true, release: true))), + (ffi.Pointer arg0, ffi.Pointer arg1) => + fn(arg0, NSCoder.castFromPointer(arg1, retain: true, release: true)), + keepIsolateAlive), retain: false, release: true); @@ -13229,15 +13292,17 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSCoder { /// but only supports void functions, and is not run synchronously. See /// NativeCallable.listener for more details. /// - /// Note that unlike the default behavior of NativeCallable.listener, listener - /// blocks do not keep the isolate alive. + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock, NSCoder)> - listener(void Function(ffi.Pointer, NSCoder) fn) { + listener(void Function(ffi.Pointer, NSCoder) fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_NSCoder_listenerCallable.nativeFunction .cast(), (ffi.Pointer arg0, ffi.Pointer arg1) => fn( - arg0, NSCoder.castFromPointer(arg1, retain: false, release: true))); + arg0, NSCoder.castFromPointer(arg1, retain: false, release: true)), + keepIsolateAlive); final wrapper = _ObjectiveCBindings_wrapListenerBlock_18v1jvf(raw); objc.objectRelease(raw.cast()); return objc.ObjCBlock, NSCoder)>( @@ -13252,22 +13317,26 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSCoder { /// caller until the callback is handled by the Dart isolate that created /// the block. Async functions are not supported. /// - /// This block does not keep the owner isolate alive. If the owner isolate has - /// shut down, and the block is invoked by native code, it may block + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. If the owner isolate + /// has shut down, and the block is invoked by native code, it may block /// indefinitely, or have other undefined behavior. static objc.ObjCBlock, NSCoder)> - blocking(void Function(ffi.Pointer, NSCoder) fn) { + blocking(void Function(ffi.Pointer, NSCoder) fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_NSCoder_blockingCallable.nativeFunction .cast(), (ffi.Pointer arg0, ffi.Pointer arg1) => fn( - arg0, NSCoder.castFromPointer(arg1, retain: false, release: true))); + arg0, NSCoder.castFromPointer(arg1, retain: false, release: true)), + keepIsolateAlive); final rawListener = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_NSCoder_blockingListenerCallable .nativeFunction .cast(), (ffi.Pointer arg0, ffi.Pointer arg1) => fn( - arg0, NSCoder.castFromPointer(arg1, retain: false, release: true))); + arg0, NSCoder.castFromPointer(arg1, retain: false, release: true)), + keepIsolateAlive); final wrapper = objc.wrapBlockingBlock( _ObjectiveCBindings_wrapBlockingBlock_18v1jvf, raw, rawListener); objc.objectRelease(raw.cast()); @@ -13446,17 +13515,21 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock, NSStream, ffi.UnsignedLong)> fromFunction( - void Function(ffi.Pointer, NSStream, NSStreamEvent) fn) => + void Function(ffi.Pointer, NSStream, NSStreamEvent) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock, NSStream, ffi.UnsignedLong)>( objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent_closureCallable, - (ffi.Pointer arg0, ffi.Pointer arg1, - int arg2) => + (ffi.Pointer arg0, ffi.Pointer arg1, int arg2) => fn( arg0, NSStream.castFromPointer(arg1, retain: true, release: true), - NSStreamEvent.fromValue(arg2))), + NSStreamEvent.fromValue(arg2)), + keepIsolateAlive), retain: false, release: true); @@ -13467,12 +13540,12 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent { /// but only supports void functions, and is not run synchronously. See /// NativeCallable.listener for more details. /// - /// Note that unlike the default behavior of NativeCallable.listener, listener - /// blocks do not keep the isolate alive. + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock< ffi.Void Function(ffi.Pointer, NSStream, ffi.UnsignedLong)> - listener( - void Function(ffi.Pointer, NSStream, NSStreamEvent) fn) { + listener(void Function(ffi.Pointer, NSStream, NSStreamEvent) fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent_listenerCallable .nativeFunction @@ -13482,7 +13555,8 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent { fn( arg0, NSStream.castFromPointer(arg1, retain: false, release: true), - NSStreamEvent.fromValue(arg2))); + NSStreamEvent.fromValue(arg2)), + keepIsolateAlive); final wrapper = _ObjectiveCBindings_wrapListenerBlock_hoampi(raw); objc.objectRelease(raw.cast()); return objc.ObjCBlock< @@ -13496,13 +13570,14 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent { /// caller until the callback is handled by the Dart isolate that created /// the block. Async functions are not supported. /// - /// This block does not keep the owner isolate alive. If the owner isolate has - /// shut down, and the block is invoked by native code, it may block + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. If the owner isolate + /// has shut down, and the block is invoked by native code, it may block /// indefinitely, or have other undefined behavior. static objc.ObjCBlock< ffi.Void Function(ffi.Pointer, NSStream, ffi.UnsignedLong)> - blocking( - void Function(ffi.Pointer, NSStream, NSStreamEvent) fn) { + blocking(void Function(ffi.Pointer, NSStream, NSStreamEvent) fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent_blockingCallable .nativeFunction @@ -13512,7 +13587,8 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent { fn( arg0, NSStream.castFromPointer(arg1, retain: false, release: true), - NSStreamEvent.fromValue(arg2))); + NSStreamEvent.fromValue(arg2)), + keepIsolateAlive); final rawListener = objc.newClosureBlock( _ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent_blockingListenerCallable .nativeFunction @@ -13522,7 +13598,8 @@ abstract final class ObjCBlock_ffiVoid_ffiVoid_NSStream_NSStreamEvent { fn( arg0, NSStream.castFromPointer(arg1, retain: false, release: true), - NSStreamEvent.fromValue(arg2))); + NSStreamEvent.fromValue(arg2)), + keepIsolateAlive); final wrapper = objc.wrapBlockingBlock( _ObjectiveCBindings_wrapBlockingBlock_hoampi, raw, rawListener); objc.objectRelease(raw.cast()); @@ -13688,17 +13765,21 @@ abstract final class ObjCBlock_ffiVoid_idNSSecureCoding_NSError { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock?, NSError)> fromFunction( - void Function(NSSecureCoding?, NSError) fn) => + void Function(NSSecureCoding?, NSError) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock?, NSError)>( objc.newClosureBlock( _ObjCBlock_ffiVoid_idNSSecureCoding_NSError_closureCallable, (ffi.Pointer arg0, ffi.Pointer arg1) => fn( arg0.address == 0 ? null - : NSSecureCoding.castFromPointer(arg0, - retain: true, release: true), - NSError.castFromPointer(arg1, retain: true, release: true))), + : NSSecureCoding.castFromPointer(arg0, retain: true, release: true), + NSError.castFromPointer(arg1, retain: true, release: true)), + keepIsolateAlive), retain: false, release: true); @@ -13709,11 +13790,12 @@ abstract final class ObjCBlock_ffiVoid_idNSSecureCoding_NSError { /// but only supports void functions, and is not run synchronously. See /// NativeCallable.listener for more details. /// - /// Note that unlike the default behavior of NativeCallable.listener, listener - /// blocks do not keep the isolate alive. + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc .ObjCBlock?, NSError)> - listener(void Function(NSSecureCoding?, NSError) fn) { + listener(void Function(NSSecureCoding?, NSError) fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_idNSSecureCoding_NSError_listenerCallable .nativeFunction @@ -13725,7 +13807,8 @@ abstract final class ObjCBlock_ffiVoid_idNSSecureCoding_NSError { ? null : NSSecureCoding.castFromPointer(arg0, retain: false, release: true), - NSError.castFromPointer(arg1, retain: false, release: true))); + NSError.castFromPointer(arg1, retain: false, release: true)), + keepIsolateAlive); final wrapper = _ObjectiveCBindings_wrapListenerBlock_pfv6jd(raw); objc.objectRelease(raw.cast()); return objc.ObjCBlock< @@ -13739,12 +13822,14 @@ abstract final class ObjCBlock_ffiVoid_idNSSecureCoding_NSError { /// caller until the callback is handled by the Dart isolate that created /// the block. Async functions are not supported. /// - /// This block does not keep the owner isolate alive. If the owner isolate has - /// shut down, and the block is invoked by native code, it may block + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. If the owner isolate + /// has shut down, and the block is invoked by native code, it may block /// indefinitely, or have other undefined behavior. static objc .ObjCBlock?, NSError)> - blocking(void Function(NSSecureCoding?, NSError) fn) { + blocking(void Function(NSSecureCoding?, NSError) fn, + {bool keepIsolateAlive = false}) { final raw = objc.newClosureBlock( _ObjCBlock_ffiVoid_idNSSecureCoding_NSError_blockingCallable .nativeFunction @@ -13756,7 +13841,8 @@ abstract final class ObjCBlock_ffiVoid_idNSSecureCoding_NSError { ? null : NSSecureCoding.castFromPointer(arg0, retain: false, release: true), - NSError.castFromPointer(arg1, retain: false, release: true))); + NSError.castFromPointer(arg1, retain: false, release: true)), + keepIsolateAlive); final rawListener = objc.newClosureBlock( _ObjCBlock_ffiVoid_idNSSecureCoding_NSError_blockingListenerCallable .nativeFunction @@ -13768,7 +13854,8 @@ abstract final class ObjCBlock_ffiVoid_idNSSecureCoding_NSError { ? null : NSSecureCoding.castFromPointer(arg0, retain: false, release: true), - NSError.castFromPointer(arg1, retain: false, release: true))); + NSError.castFromPointer(arg1, retain: false, release: true)), + keepIsolateAlive); final wrapper = objc.wrapBlockingBlock( _ObjectiveCBindings_wrapBlockingBlock_pfv6jd, raw, rawListener); objc.objectRelease(raw.cast()); @@ -13863,10 +13950,13 @@ abstract final class ObjCBlock_instancetype_ffiVoid_NSCoder { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc - .ObjCBlock?> Function(ffi.Pointer, NSCoder)> + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock?> Function(ffi.Pointer, NSCoder)> fromFunction( - Dartinstancetype? Function(ffi.Pointer, NSCoder) fn) => + Dartinstancetype? Function(ffi.Pointer, NSCoder) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock?> Function(ffi.Pointer, NSCoder)>( objc.newClosureBlock( _ObjCBlock_instancetype_ffiVoid_NSCoder_closureCallable, @@ -13874,7 +13964,8 @@ abstract final class ObjCBlock_instancetype_ffiVoid_NSCoder { fn(arg0, NSCoder.castFromPointer(arg1, retain: true, release: true)) ?.ref .retainAndReturnPointer() ?? - ffi.nullptr), + ffi.nullptr, + keepIsolateAlive), retain: false, release: true); } @@ -14002,8 +14093,13 @@ abstract final class ObjCBlock_instancetype_ffiVoid_NSData_NSString_NSError { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc.ObjCBlock? Function(ffi.Pointer, NSData, NSString, ffi.Pointer>)> - fromFunction(Dartinstancetype? Function(ffi.Pointer, NSData, NSString, ffi.Pointer>) fn) => + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc + .ObjCBlock? Function(ffi.Pointer, NSData, NSString, ffi.Pointer>)> + fromFunction(Dartinstancetype? Function(ffi.Pointer, NSData, NSString, ffi.Pointer>) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock? Function(ffi.Pointer, NSData, NSString, ffi.Pointer>)>( objc.newClosureBlock( _ObjCBlock_instancetype_ffiVoid_NSData_NSString_NSError_closureCallable, @@ -14011,8 +14107,8 @@ abstract final class ObjCBlock_instancetype_ffiVoid_NSData_NSString_NSError { ffi.Pointer arg1, ffi.Pointer arg2, ffi.Pointer> arg3) => - fn(arg0, NSData.castFromPointer(arg1, retain: true, release: true), NSString.castFromPointer(arg2, retain: true, release: true), arg3)?.ref.retainAndAutorelease() ?? - ffi.nullptr), + fn(arg0, NSData.castFromPointer(arg1, retain: true, release: true), NSString.castFromPointer(arg2, retain: true, release: true), arg3)?.ref.retainAndAutorelease() ?? ffi.nullptr, + keepIsolateAlive), retain: false, release: true); } @@ -14113,15 +14209,20 @@ abstract final class ObjCBlock_objcObjCObject_ffiVoid { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc .ObjCBlock Function(ffi.Pointer)> - fromFunction(objc.ObjCObjectBase Function(ffi.Pointer) fn) => + fromFunction(objc.ObjCObjectBase Function(ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock< ffi.Pointer Function(ffi.Pointer)>( objc.newClosureBlock( _ObjCBlock_objcObjCObject_ffiVoid_closureCallable, (ffi.Pointer arg0) => - fn(arg0).ref.retainAndAutorelease()), + fn(arg0).ref.retainAndAutorelease(), + keepIsolateAlive), retain: false, release: true); } @@ -14216,16 +14317,20 @@ abstract final class ObjCBlock_objcObjCObject_ffiVoid_NSZone { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc - .ObjCBlock> Function(ffi.Pointer, ffi.Pointer)> - fromFunction(objc.ObjCObjectBase Function(ffi.Pointer, ffi.Pointer) fn) => + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock> Function(ffi.Pointer, ffi.Pointer)> + fromFunction(objc.ObjCObjectBase Function(ffi.Pointer, ffi.Pointer) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock< objc.Retained> Function( ffi.Pointer, ffi.Pointer)>( objc.newClosureBlock( _ObjCBlock_objcObjCObject_ffiVoid_NSZone_closureCallable, (ffi.Pointer arg0, ffi.Pointer arg1) => - fn(arg0, arg1).ref.retainAndReturnPointer()), + fn(arg0, arg1).ref.retainAndReturnPointer(), + keepIsolateAlive), retain: false, release: true); } @@ -14330,17 +14435,22 @@ abstract final class ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc.ObjCBlock Function(ffi.Pointer, ffi.Pointer)> - fromFunction(objc.ObjCObjectBase Function(ffi.Pointer, ffi.Pointer) fn) => - objc.ObjCBlock< - ffi.Pointer Function( - ffi.Pointer, ffi.Pointer)>( - objc.newClosureBlock( - _ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector_closureCallable, - (ffi.Pointer arg0, ffi.Pointer arg1) => - fn(arg0, arg1).ref.retainAndAutorelease()), - retain: false, - release: true); + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock Function(ffi.Pointer, ffi.Pointer)> fromFunction( + objc.ObjCObjectBase Function( + ffi.Pointer, ffi.Pointer) + fn, + {bool keepIsolateAlive = false}) => + objc.ObjCBlock Function(ffi.Pointer, ffi.Pointer)>( + objc.newClosureBlock( + _ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector_closureCallable, + (ffi.Pointer arg0, ffi.Pointer arg1) => + fn(arg0, arg1).ref.retainAndAutorelease(), + keepIsolateAlive), + retain: false, + release: true); } /// Call operator for `objc.ObjCBlock Function(ffi.Pointer, ffi.Pointer)>`. @@ -14455,8 +14565,12 @@ abstract final class ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector_objcObjCO /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. static objc.ObjCBlock Function(ffi.Pointer, ffi.Pointer, ffi.Pointer)> - fromFunction(objc.ObjCObjectBase Function(ffi.Pointer, ffi.Pointer, objc.ObjCObjectBase) fn) => + fromFunction(objc.ObjCObjectBase Function(ffi.Pointer, ffi.Pointer, objc.ObjCObjectBase) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock Function(ffi.Pointer, ffi.Pointer, ffi.Pointer)>( objc.newClosureBlock( _ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector_objcObjCObject_closureCallable, @@ -14465,7 +14579,8 @@ abstract final class ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector_objcObjCO ffi.Pointer arg2) => fn(arg0, arg1, objc.ObjCObjectBase(arg2, retain: true, release: true)) .ref - .retainAndAutorelease()), + .retainAndAutorelease(), + keepIsolateAlive), retain: false, release: true); } @@ -14598,8 +14713,13 @@ abstract final class ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector_objcObjCO /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - static objc.ObjCBlock Function(ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Pointer)> - fromFunction(objc.ObjCObjectBase Function(ffi.Pointer, ffi.Pointer, objc.ObjCObjectBase, objc.ObjCObjectBase) fn) => + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc + .ObjCBlock Function(ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Pointer)> + fromFunction(objc.ObjCObjectBase Function(ffi.Pointer, ffi.Pointer, objc.ObjCObjectBase, objc.ObjCObjectBase) fn, + {bool keepIsolateAlive = false}) => objc.ObjCBlock Function(ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Pointer)>( objc.newClosureBlock( _ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector_objcObjCObject_objcObjCObject_closureCallable, @@ -14607,9 +14727,8 @@ abstract final class ObjCBlock_objcObjCObject_ffiVoid_objcObjCSelector_objcObjCO ffi.Pointer arg1, ffi.Pointer arg2, ffi.Pointer arg3) => - fn(arg0, arg1, objc.ObjCObjectBase(arg2, retain: true, release: true), objc.ObjCObjectBase(arg3, retain: true, release: true)) - .ref - .retainAndAutorelease()), + fn(arg0, arg1, objc.ObjCObjectBase(arg2, retain: true, release: true), objc.ObjCObjectBase(arg3, retain: true, release: true)).ref.retainAndAutorelease(), + keepIsolateAlive), retain: false, release: true); } diff --git a/pkgs/objective_c/pubspec.yaml b/pkgs/objective_c/pubspec.yaml index f93b9f5178..17f02b86be 100644 --- a/pkgs/objective_c/pubspec.yaml +++ b/pkgs/objective_c/pubspec.yaml @@ -4,7 +4,7 @@ name: objective_c description: 'A library to access Objective C from Flutter that acts as a support library for package:ffigen.' -version: 6.0.0 +version: 7.0.0-dev repository: https://github.com/dart-lang/native/tree/main/pkgs/objective_c issue_tracker: https://github.com/dart-lang/native/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Aobjective_c @@ -28,7 +28,7 @@ dev_dependencies: args: ^2.6.0 coverage: ^1.11.0 dart_flutter_team_lints: ^2.0.0 - ffigen: ^17.0.0 + ffigen: ^18.0.0 flutter_lints: ^3.0.0 flutter_test: sdk: flutter