Checking whether an object is uniquely referenced, using a weak reference to it


(Tim Vermeulen) #1

class EmptyClass {}

var strongReference = EmptyClass()
weak var weakReference = strongReference

print(isUniquelyReferencedNonObjC(&strongReference)) // true
print(isUniquelyReferencedNonObjC(&weakReference)) // false

I expected both print statements to print true.

I realise that this is probably a known limitation of this function. After all, if it worked with weak references too, there’s no point for the parameter to be inout:

func wrappedIsUniquelyReferencedNonObjC<T: AnyObject>(_ object: T) -> Bool {
    weak var weakObject = object
    return isUniquelyReferencedNonObjC(&weakObject)
}

So the fact that this function takes an inout parameter hints at the fact that it doesn’t work with weak references. If my reasoning is correct, it would be nice if the docs stated that it only works with strong references to objects. :slight_smile:

My question is, is it possible at all to use a weak reference to check whether an object is uniquely referenced or not? Since this approach doesn’t seem to work, I guess it is impossible?


(Jordan Rose) #2

Hi, Tim. The purpose of the isUniquelyReferenced checks is to see whether any changes to the properties of the object can be observed from elsewhere in this program. Therefore, I would expect it to return ‘false’ when there are weak (or unowned) references present, i.e. I think the bug is that we don’t consider weak or unowned references to count for this check.

If my understanding of the intended behavior is correct, then it doesn’t make sense to check for a uniquely-referenced object through a weak (or unowned) reference, because if that were the only reference, then the object would have already been deallocated.

The particular technical reason for the check failing is related to the inout, as you say: in order to pass the reference to the function inout, it has to be loaded into a strong reference first. The reason the inout is there at all is because otherwise the check would fail: there would be a reference from outside the function in addition to the one for the argument.

Hope that clears things up, and if you agree with my interpretation about weak and unowned references, please file a bug at bugs.swift.org <http://bugs.swift.org/>!
Jordan

···

On Jun 27, 2016, at 17:14, Tim Vermeulen via swift-users <swift-users@swift.org> wrote:

class EmptyClass {}

var strongReference = EmptyClass()
weak var weakReference = strongReference

print(isUniquelyReferencedNonObjC(&strongReference)) // true
print(isUniquelyReferencedNonObjC(&weakReference)) // false

I expected both print statements to print true.

I realise that this is probably a known limitation of this function. After all, if it worked with weak references too, there’s no point for the parameter to be inout:

func wrappedIsUniquelyReferencedNonObjC<T: AnyObject>(_ object: T) -> Bool {
    weak var weakObject = object
    return isUniquelyReferencedNonObjC(&weakObject)
}

So the fact that this function takes an inout parameter hints at the fact that it doesn’t work with weak references. If my reasoning is correct, it would be nice if the docs stated that it only works with strong references to objects. :slight_smile:

My question is, is it possible at all to use a weak reference to check whether an object is uniquely referenced or not? Since this approach doesn’t seem to work, I guess it is impossible?
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Tim Vermeulen) #3

Thanks for your reply. It didn’t clear up everything, though. The official documentation says "Weak references do not affect the result of this function.”, which suggests that weak (and unowned) references intentionally aren’t counted. The docs only mention the implementation of copy-on-write behaviour as a use case (which also happens to be what I’m using it for).

Couldn’t there just a be a function that returns the reference count of a given object as an Int? It would make everything a lot easier (i.e. it wouldn’t need inout because it can just create a reference to that object, find the reference count, then subtract 1).

···

Hi, Tim. The purpose of the isUniquelyReferenced checks is to see whether any changes to the properties of the object can be observed from elsewhere in this program. Therefore, I wouldexpectit to return ‘false’ when there are weak (or unowned) references present, i.e. I think the bug is that we don’t consider weak or unowned references to count for this check.

If my understanding of the intended behavior is correct, then it doesn’t make sense to check for a uniquely-referenced object through a weak (or unowned) reference, because if that were the only reference, then the object would have already been deallocated.

The particular technical reason for the check failing is related to the inout, as you say: in order to pass the reference to the function inout, it has to be loaded into a strong reference first. The reason the inout is there at all is because otherwise the check would fail: there would be a reference from outside the function in addition to the one for the argument.

Hope that clears things up, and if you agree with my interpretation about weak and unowned references, please file a bug atbugs.swift.org(http://bugs.swift.org)!
Jordan

> On Jun 27, 2016, at 17:14, Tim Vermeulen via swift-users<swift-users@swift.org(mailto:swift-users@swift.org)>wrote:
> classEmptyClass {}
>
> varstrongReference =EmptyClass()
> weakvarweakReference =strongReference
>
> print(isUniquelyReferencedNonObjC(&strongReference))// true
> print(isUniquelyReferencedNonObjC(&weakReference))// false
>
>
> I expected both print statements to print true.
>
> I realise that this is probably a known limitation of this function. After all, if it worked with weak references too, there’s no point for the parameter to be inout:
>
> funcwrappedIsUniquelyReferencedNonObjC<T:AnyObject>(_object:T) ->Bool{
> weakvarweakObject = object
> returnisUniquelyReferencedNonObjC(&weakObject)
> }
>
>
> So the fact that this function takes an inout parameter hints at the fact that it doesn’t work with weak references. If my reasoning is correct, it would be nice if the docs stated that it only works with strong references to objects. :slight_smile:
>
> My question is, is it possible at all to use a weak reference to check whether an object is uniquely referenced or not? Since this approach doesn’t seem to work, I guess it is impossible?_______________________________________________
> swift-users mailing list
> swift-users@swift.org(mailto:swift-users@swift.org)
> https://lists.swift.org/mailman/listinfo/swift-users


(Jordan Rose) #4

Thanks for your reply. It didn’t clear up everything, though. The official documentation says "Weak references do not affect the result of this function.”, which suggests that weak (and unowned) references intentionally aren’t counted. The docs only mention the implementation of copy-on-write behaviour as a use case (which also happens to be what I’m using it for).

I would expect that weak references are important to count for COW, since you can observe changes through them. Dave?

Couldn’t there just a be a function that returns the reference count of a given object as an Int? It would make everything a lot easier (i.e. it wouldn’t need inout because it can just create a reference to that object, find the reference count, then subtract 1).

As we’ve said for a long time in Objective-C, asking for the reference count of an object is meaningless. isUniquelyReferenced only works because it’s conservative: because it only checks for “exactly 1”, it’s safe from threading issues and autorelease pools. We do not plan to add a -retainCount equivalent to Swift.

Jordan

···

On Jun 27, 2016, at 18:52, Tim Vermeulen <tvermeulen@me.com> wrote:


(Dave Abrahams) #5

I can try to address this thread, but not immediately I'm afraid. I'm
just swamped at the moment.

Sorry,

···

on Tue Jun 28 2016, Jordan Rose <jordan_rose-AT-apple.com> wrote:

On Jun 27, 2016, at 18:52, Tim Vermeulen <tvermeulen@me.com> wrote:

Thanks for your reply. It didn’t clear up everything, though. The
official documentation says "Weak references do not affect the
result of this function.”, which suggests that weak (and unowned)
references intentionally aren’t counted. The docs only mention the
implementation of copy-on-write behaviour as a use case (which also
happens to be what I’m using it for).

I would expect that weak references are important to count for COW,
since you can observe changes through them. Dave?

--
Dave


(Joe Groff) #6

Thanks for your reply. It didn’t clear up everything, though. The official documentation says "Weak references do not affect the result of this function.”, which suggests that weak (and unowned) references intentionally aren’t counted. The docs only mention the implementation of copy-on-write behaviour as a use case (which also happens to be what I’m using it for).

I would expect that weak references are important to count for COW, since you can observe changes through them. Dave?

Passing a weak reference inout goes through a strong optional shadow copy. It wouldn't be safe to directly address the weak reference.

-Joe

···

On Jun 28, 2016, at 9:32 AM, Jordan Rose via swift-users <swift-users@swift.org> wrote:

On Jun 27, 2016, at 18:52, Tim Vermeulen <tvermeulen@me.com> wrote:

Couldn’t there just a be a function that returns the reference count of a given object as an Int? It would make everything a lot easier (i.e. it wouldn’t need inout because it can just create a reference to that object, find the reference count, then subtract 1).

As we’ve said for a long time in Objective-C, asking for the reference count of an object is meaningless. isUniquelyReferenced only works because it’s conservative: because it only checks for “exactly 1”, it’s safe from threading issues and autorelease pools. We do not plan to add a -retainCount equivalent to Swift.

Jordan

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Andrew Trick) #7

Specifically, isUniquelyReferenced guarantees “exactly 1” if it returns ‘true’. It does not guarantee ‘> 1’ by returning ‘false’. In other words, the implementation reserves the right to return ‘false’ whenever it feels like it. The user can’t directly control the number of local references that exist. So, although it’s critical for optimization, you shouldn’t build any visible program behavior around isUniquelyReferenced.

I don’t have a good answer on the weak reference semantics, but I *suspect* that considering weak references in the count is neither feasible for objc-style weak references, nor useful since we don’t expose weak references to CoW storage.

-Andy

···

On Jun 28, 2016, at 9:32 AM, Jordan Rose via swift-users <swift-users@swift.org> wrote:

On Jun 27, 2016, at 18:52, Tim Vermeulen <tvermeulen@me.com> wrote:

Thanks for your reply. It didn’t clear up everything, though. The official documentation says "Weak references do not affect the result of this function.”, which suggests that weak (and unowned) references intentionally aren’t counted. The docs only mention the implementation of copy-on-write behaviour as a use case (which also happens to be what I’m using it for).

I would expect that weak references are important to count for COW, since you can observe changes through them. Dave?

Couldn’t there just a be a function that returns the reference count of a given object as an Int? It would make everything a lot easier (i.e. it wouldn’t need inout because it can just create a reference to that object, find the reference count, then subtract 1).

As we’ve said for a long time in Objective-C, asking for the reference count of an object is meaningless. isUniquelyReferenced only works because it’s conservative: because it only checks for “exactly 1”, it’s safe from threading issues and autorelease pools. We do not plan to add a -retainCount equivalent to Swift.

Jordan


(Dave Abrahams) #8

This sort of depends on what you expect the semantics of your weak
reference to be. It's not possible to take a weak reference to an
Array; you can only take a weak reference to the NSArray it uses as a
backing store. That's an Objective-C-only idea.

Given an arbitrary NSArray on the Objective-C side, it might turn out to
be an NSMutableArray, so if you want to avoid observing changes to it,
in principle you need to copy it. So I think it's possible to argue
that the weak reference count is not an issue here.

···

on Tue Jun 28 2016, Jordan Rose <jordan_rose-AT-apple.com> wrote:

On Jun 27, 2016, at 18:52, Tim Vermeulen <tvermeulen@me.com> wrote:

Thanks for your reply. It didn’t clear up everything, though. The
official documentation says "Weak references do not affect the
result of this function.”, which suggests that weak (and unowned)
references intentionally aren’t counted. The docs only mention the
implementation of copy-on-write behaviour as a use case (which also
happens to be what I’m using it for).

I would expect that weak references are important to count for COW,
since you can observe changes through them. Dave?

--
-Dave


(Dave Abrahams) #9

We have a race in this case:

  class X {}
  var a = [X()]
  weak var b = a as NSArray
  async {
    // Reads the buffer through the weak reference in another thread.
    print(b!.object(at: 0))
  }
  // Sees that a's buffer is uniquely referenced and begins to modify it
  // in place.
  a[0] = X()

So yes, isUniquelyReferenced should always be false if the weak
reference count is nonzero. We can read the weak and strong reference
counts together with a single instruction so this should be easily
implementable. We just haven't done it. <rdar://problem/27070378>

···

on Tue Jun 28 2016, Andrew Trick <atrick-AT-apple.com> wrote:

On Jun 28, 2016, at 9:32 AM, Jordan Rose via swift-users <swift-users@swift.org> wrote:

On Jun 27, 2016, at 18:52, Tim Vermeulen <tvermeulen@me.com> wrote:

Thanks for your reply. It didn’t clear up everything, though. The
official documentation says "Weak references do not affect the
result of this function.”, which suggests that weak (and unowned)
references intentionally aren’t counted. The docs only mention the
implementation of copy-on-write behaviour as a use case (which also
happens to be what I’m using it for).

I would expect that weak references are important to count for COW,
since you can observe changes through them. Dave?

Couldn’t there just a be a function that returns the reference
count of a given object as an Int? It would make everything a lot
easier (i.e. it wouldn’t need inout because it can just create a
reference to that object, find the reference count, then subtract
1).

As we’ve said for a long time in Objective-C, asking for the
reference count of an object is meaningless. isUniquelyReferenced
only works because it’s conservative: because it only checks for
“exactly 1”, it’s safe from threading issues and autorelease
pools. We do not plan to add a -retainCount equivalent to Swift.

Jordan

Specifically, isUniquelyReferenced guarantees “exactly 1” if it
returns ‘true’. It does not guarantee ‘> 1’ by returning ‘false’. In
other words, the implementation reserves the right to return ‘false’
whenever it feels like it. The user can’t directly control the number
of local references that exist. So, although it’s critical for
optimization, you shouldn’t build any visible program behavior around
isUniquelyReferenced.

I don’t have a good answer on the weak reference semantics, but I
*suspect* that considering weak references in the count is neither
feasible for objc-style weak references, nor useful since we don’t
expose weak references to CoW storage.

--
Dave