[ffigen] Add the ability to control reference counting behavior in castFromPointer (#388)

diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
index 4c77067..04eb6e5 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
@@ -103,8 +103,9 @@
   }
 
   /// Returns a [$name] that wraps the given raw object pointer.
-  static $name castFromPointer($natLib lib, ffi.Pointer<ObjCObject> other) {
-    return $name._(other, lib, retain: true, release: true);
+  static $name castFromPointer($natLib lib, ffi.Pointer<ObjCObject> other,
+      {bool retain = false, bool release = false}) {
+    return $name._(other, lib, retain: retain, release: release);
   }
 
   /// Returns whether [obj] is an instance of [$name].
diff --git a/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart b/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart
index 2c3ab47..74c1270 100644
--- a/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart
+++ b/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart
@@ -97,6 +97,38 @@
       calloc.free(counter);
     });
 
+    castFromPointerInnerReleaseAndRetain(int address) {
+      final fromCast = RefCounted.castFromPointer(
+          lib, Pointer<ObjCObject>.fromAddress(address),
+          release: true, retain: true);
+      expect(fromCast.refCount, 2);
+    }
+
+    test('castFromPointer - release and retain', () {
+      final obj1 = RefCounted.new1(lib);
+      expect(obj1.refCount, 1);
+
+      castFromPointerInnerReleaseAndRetain(obj1.meAsInt());
+      doGC();
+      expect(obj1.refCount, 1);
+    });
+
+    castFromPointerInnerNoReleaseAndRetain(int address) {
+      final fromCast = RefCounted.castFromPointer(
+          lib, Pointer<ObjCObject>.fromAddress(address),
+          release: false, retain: false);
+      expect(fromCast.refCount, 1);
+    }
+
+    test('castFromPointer - no release and retain', () {
+      final obj1 = RefCounted.new1(lib);
+      expect(obj1.refCount, 1);
+
+      castFromPointerInnerNoReleaseAndRetain(obj1.meAsInt());
+      doGC();
+      expect(obj1.refCount, 1);
+    });
+
     test('Manual release', () {
       final counter = calloc<Int32>();
       final obj1 = ArcTestObject.newWithCounter_(lib, counter);
diff --git a/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.m b/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.m
index 4fa6c10..78e5a6b 100644
--- a/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.m
+++ b/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.m
@@ -20,6 +20,14 @@
 
 @end
 
+@interface RefCounted : NSObject
+
+@property(readonly) uint64_t refCount;
+
+- (int64_t) meAsInt;
+
+@end
+
 @implementation ArcTestObject
 
 + (instancetype)allocTheThing {
@@ -63,3 +71,30 @@
 }
 
 @end
+
+@implementation RefCounted
+
+- (instancetype)init {
+    if (self = [super init]) {
+      self->_refCount = 1;
+    }
+    return self;
+}
+
+- (instancetype)retain {
+  ++self->_refCount;
+  return self;
+}
+
+- (oneway void)release {
+  --self->_refCount;
+  if (self->_refCount == 0) {
+    [self dealloc];
+  }
+}
+
+- (int64_t) meAsInt {
+  return (int64_t) self;
+}
+
+@end
\ No newline at end of file