ellie20
(ellie20)
1
I've noticed that C++ functions with pointer arguments or return values of a SWIFT_SHARED_REFERENCE-bridged type are bridged into Swift with the reference types themselves. For example, this:
SomeReferenceType *myFunction(SomeReferenceType *x);
gets imported as this:
func myFunction(_ x: SomeReferenceType) -> SomeReferenceType
My question is, what ownership convention is used for the C++ function? Which pointers are interpreted by Swift as retained (+1) and which are interpreted as borrowed/unowned (+0)? Is there a way to customize this?
Also, something else I've noticed is that Swift assumes the retain/release functions passed to the SWIFT_SHARED_REFERENCE macro are safe to call on null pointers, so it calls retain and release for optional C++ shared reference types regardless of if they're nil or not. I was wondering if that was documented anywhere, since I shot myself in the foot with it and others might too.
1 Like
zoecarver
(Zoe Carver)
2
We use Swift's conventions for managing foreign reference types. Right now we don't have a good way to customize this. On the Swift side you can still leverage Unmanaged to bridge conventions, though (to return at +0 for example).
Also, something else I've noticed is that Swift assumes the retain/release functions passed to the SWIFT_SHARED_REFERENCE macro are safe to call on null pointers, so it calls retain and releasefor optional C++ shared reference types regardless of if they're nil or not. I was wondering if that was documented anywhere, since I shot myself in the foot with it and others might too.
Swift will respect _Nullable and _Nonnull specifiers, so if you code is annotated with those, I wouldn't expect any problems. By default, pointers will be imported as implicitly unwrapped optionals. Are you sure that you don't have NS_ASSUME_NONNULL_BEGIN though?
1 Like
ellie20
(ellie20)
3
Swift will respect _Nullable and _Nonnull specifiers, so if you code is annotated with those, I wouldn't expect any problems. By default, pointers will be imported as implicitly unwrapped optionals. Are you sure that you don't have NS_ASSUME_NONNULL_BEGIN though?
I don't think I have NS_ASSUME_NONNULL_BEGIN, because the only headers I'm importing in my C++ file are <swift/bridging> and <QuartzCore/CoreAnimation.h>. Even after annotating my retain and release functions with _Nonnull, Swift still calls them for null pointers. The retain and release functions are never explicitly called from Swift, since they're managed by ARC. I wonder if Swift assuming they're null-safe is an artifact of objc_retain and objc_release being null-safe.
We use Swift's conventions for managing foreign reference types. Right now we don't have a good way to customize this. On the Swift side you can still leverage Unmanaged to bridge conventions, though (to return at +0 for example).
One of my functions is a constructor that returns a pointer to my foreign reference type. When I use it from Swift and print each retain and release call, it seems that Swift assumes the function is returning a +0 reference even though I want it to be a +1 reference. If I'm not mistaken, Swift normally assumes that functions always return owned values. I'm currently using the hack Unmanaged.passUnretained(constructor()).takeRetainedValue() to fix this, which makes everything work correctly. Also, these retain and release functions are declarations whose implementations are linked later, within an extern "C" block; does that make a difference?
Alex_L
(Alex Lorenz)
4
That's interesting, I don't think we have anticipated this behavior. We will document it at the very least so it's clear in the docs. Thanks for pointing it out!
1 Like
tera
5
NULL / NONNULL? That's just nullability.
Although I haven't tried them myself, for ownership control you'd need these guys:
CF_RETURNS_RETAINED
NS_RETURNS_RETAINED
CF_RETURNS_NOT_RETAINED
NS_RETURNS_NOT_RETAINED
CF_CONSUMED
NS_RELEASES_ARGUMENT
2 Likes
I think IRGen “knows” that optional class values can be retained and released with the same functions as non-optional values. It should be fairly straightforward to make that a little more conditional.
2 Likes
Alex_L
(Alex Lorenz)
7
Yeah it sounds like we should treat it as a bug, and fix IRGen to avoid such calls. In the meantime we should still document the existing behavior for 5.9 though
1 Like
Alex_L
(Alex Lorenz)
8
zoecarver
(Zoe Carver)
9
I'm surprised that an empty optional would still retain its payload. Are you sure that all your APIs that return nullable pointers are annotated with _Nullable?