Re-Visit Proposal: Weak Native Swift Containers (12 2015)


(Dominik Pich) #1

Hello,
I'd like to re-visit a proposal from Riley Testut about weak containers. Since no conclusion/outcome was achieved (AFAICS from looking at the archives and the repository)
and since I just would have needed this again too... I found it a good time to re-propose this :smiley:

···

---

"
In multiple places in my projects, I essentially recreate the “multiple observer” pattern used by NSNotificationCenter. Originally this was implemented by simply maintaining an array of observers, and adding to/removing from it as necessary. However, this had the unintended side effect of maintaining a strong reference to the observers, which in many cases is undesirable (for the same reasons it’s common to mark delegate properties as weak).

Now, I’m using a private NSHashTable instance, and expose the observers as public API by creating a public computed property which essentially returns an array derived from the NSHashTable like so:

public var receivers: [GameControllerReceiverType] {
     // self.privateReceivers.allObjects as! [GameControllerReceiverType] crashes Swift :frowning:
     return self.privateReceivers.allObjects.map({ $0 as! GameControllerReceiverType })
}

This workaround works, but is undesirable for a number of reasons. Most notably:

• NSHashTable is not a native Swift collection, and is also not in the Foundation Swift port, so it is not portable to other systems.
• It also has not yet been annotated with generics, so it loses the nice type safety of other Swift collections. Because of this, I have to map the objects to the appropriate type before returning the allObjects array, which runs in O(n) time instead of O(1).
• It’s repetitive. For every type that wants to implement this pattern, they must maintain both a public computed method and a private NSHashTable instance. This gets worse when this should be part of a protocol; there’s no way to enforce that each type conforming to it has a NSHashTable, while also keeping that information private from the consumer of the API.

I think native swift collections with support for weak references for their contents would be very useful, and in more places than just listed above. I don’t think Array could be easily extended to support it (what happens if a value is released? does everything shift down? do they keep their indices?), but Set and Dictionary (where the keys and/or values could be weak, akin to NSMapTable) would be good candidates IMO.

Thoughts?"

--reference to last message thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001579.html
(last messages were 'arguing' how to implement it)


(Haravikk) #2

I’ve used the following in some similar cases:

  struct Weak<T> {
    weak var value:T?
    init(_ value:T) { self.value = value }
  }
  let myCache = Array<Weak<Foo>>()

When inserting new values I just look for the first one that’s nil or append if it’s taking too long, but you’re right, something more reactive would be better, for example if the above were observable I could do something about it as soon as it became nil.

···

On 11 May 2016, at 14:00, Dominik Pich via swift-evolution <swift-evolution@swift.org> wrote:

Hello,
I'd like to re-visit a proposal from Riley Testut about weak containers. Since no conclusion/outcome was achieved (AFAICS from looking at the archives and the repository)
and since I just would have needed this again too... I found it a good time to re-propose this :smiley:

---

"
In multiple places in my projects, I essentially recreate the “multiple observer” pattern used by NSNotificationCenter. Originally this was implemented by simply maintaining an array of observers, and adding to/removing from it as necessary. However, this had the unintended side effect of maintaining a strong reference to the observers, which in many cases is undesirable (for the same reasons it’s common to mark delegate properties as weak).

Now, I’m using a private NSHashTable instance, and expose the observers as public API by creating a public computed property which essentially returns an array derived from the NSHashTable like so:

public var receivers: [GameControllerReceiverType] {
   // self.privateReceivers.allObjects as! [GameControllerReceiverType] crashes Swift :frowning:
   return self.privateReceivers.allObjects.map({ $0 as! GameControllerReceiverType })
}

This workaround works, but is undesirable for a number of reasons. Most notably:

• NSHashTable is not a native Swift collection, and is also not in the Foundation Swift port, so it is not portable to other systems.
• It also has not yet been annotated with generics, so it loses the nice type safety of other Swift collections. Because of this, I have to map the objects to the appropriate type before returning the allObjects array, which runs in O(n) time instead of O(1).
• It’s repetitive. For every type that wants to implement this pattern, they must maintain both a public computed method and a private NSHashTable instance. This gets worse when this should be part of a protocol; there’s no way to enforce that each type conforming to it has a NSHashTable, while also keeping that information private from the consumer of the API.

I think native swift collections with support for weak references for their contents would be very useful, and in more places than just listed above. I don’t think Array could be easily extended to support it (what happens if a value is released? does everything shift down? do they keep their indices?), but Set and Dictionary (where the keys and/or values could be weak, akin to NSMapTable) would be good candidates IMO.

Thoughts?"

--reference to last message thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001579.html
(last messages were 'arguing' how to implement it)

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


(Patrick Smith) #3

I remember reading that Swift’s weak references are ‘lazy’, and only clear (and release if needed) when they are next accessed. So you couldn’t observe them AFAIK.

···

On 11 May 2016, at 11:23 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

I’ve used the following in some similar cases:

  struct Weak<T> {
    weak var value:T?
    init(_ value:T) { self.value = value }
  }
  let myCache = Array<Weak<Foo>>()

When inserting new values I just look for the first one that’s nil or append if it’s taking too long, but you’re right, something more reactive would be better, for example if the above were observable I could do something about it as soon as it became nil.

On 11 May 2016, at 14:00, Dominik Pich via swift-evolution <swift-evolution@swift.org> wrote:

Hello,
I'd like to re-visit a proposal from Riley Testut about weak containers. Since no conclusion/outcome was achieved (AFAICS from looking at the archives and the repository)
and since I just would have needed this again too... I found it a good time to re-propose this :smiley:

---

"
In multiple places in my projects, I essentially recreate the “multiple observer” pattern used by NSNotificationCenter. Originally this was implemented by simply maintaining an array of observers, and adding to/removing from it as necessary. However, this had the unintended side effect of maintaining a strong reference to the observers, which in many cases is undesirable (for the same reasons it’s common to mark delegate properties as weak).

Now, I’m using a private NSHashTable instance, and expose the observers as public API by creating a public computed property which essentially returns an array derived from the NSHashTable like so:

public var receivers: [GameControllerReceiverType] {
  // self.privateReceivers.allObjects as! [GameControllerReceiverType] crashes Swift :frowning:
  return self.privateReceivers.allObjects.map({ $0 as! GameControllerReceiverType })
}

This workaround works, but is undesirable for a number of reasons. Most notably:

• NSHashTable is not a native Swift collection, and is also not in the Foundation Swift port, so it is not portable to other systems.
• It also has not yet been annotated with generics, so it loses the nice type safety of other Swift collections. Because of this, I have to map the objects to the appropriate type before returning the allObjects array, which runs in O(n) time instead of O(1).
• It’s repetitive. For every type that wants to implement this pattern, they must maintain both a public computed method and a private NSHashTable instance. This gets worse when this should be part of a protocol; there’s no way to enforce that each type conforming to it has a NSHashTable, while also keeping that information private from the consumer of the API.

I think native swift collections with support for weak references for their contents would be very useful, and in more places than just listed above. I don’t think Array could be easily extended to support it (what happens if a value is released? does everything shift down? do they keep their indices?), but Set and Dictionary (where the keys and/or values could be weak, akin to NSMapTable) would be good candidates IMO.

Thoughts?"

--reference to last message thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001579.html
(last messages were 'arguing' how to implement it)

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

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


(Goffredo Marocchi) #4

Associated object based tricks may be used in the meantime, KVO on objects with an uncertain duration was always problematic.

···

Sent from my iPhone

On 11 May 2016, at 14:30, Patrick Smith via swift-evolution <swift-evolution@swift.org> wrote:

I remember reading that Swift’s weak references are ‘lazy’, and only clear (and release if needed) when they are next accessed. So you couldn’t observe them AFAIK.

On 11 May 2016, at 11:23 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

I’ve used the following in some similar cases:

   struct Weak<T> {
       weak var value:T?
       init(_ value:T) { self.value = value }
   }
   let myCache = Array<Weak<Foo>>()

When inserting new values I just look for the first one that’s nil or append if it’s taking too long, but you’re right, something more reactive would be better, for example if the above were observable I could do something about it as soon as it became nil.

On 11 May 2016, at 14:00, Dominik Pich via swift-evolution <swift-evolution@swift.org> wrote:

Hello,
I'd like to re-visit a proposal from Riley Testut about weak containers. Since no conclusion/outcome was achieved (AFAICS from looking at the archives and the repository)
and since I just would have needed this again too... I found it a good time to re-propose this :smiley:

---

"
In multiple places in my projects, I essentially recreate the “multiple observer” pattern used by NSNotificationCenter. Originally this was implemented by simply maintaining an array of observers, and adding to/removing from it as necessary. However, this had the unintended side effect of maintaining a strong reference to the observers, which in many cases is undesirable (for the same reasons it’s common to mark delegate properties as weak).

Now, I’m using a private NSHashTable instance, and expose the observers as public API by creating a public computed property which essentially returns an array derived from the NSHashTable like so:

public var receivers: [GameControllerReceiverType] {
// self.privateReceivers.allObjects as! [GameControllerReceiverType] crashes Swift :frowning:
return self.privateReceivers.allObjects.map({ $0 as! GameControllerReceiverType })
}

This workaround works, but is undesirable for a number of reasons. Most notably:

• NSHashTable is not a native Swift collection, and is also not in the Foundation Swift port, so it is not portable to other systems.
• It also has not yet been annotated with generics, so it loses the nice type safety of other Swift collections. Because of this, I have to map the objects to the appropriate type before returning the allObjects array, which runs in O(n) time instead of O(1).
• It’s repetitive. For every type that wants to implement this pattern, they must maintain both a public computed method and a private NSHashTable instance. This gets worse when this should be part of a protocol; there’s no way to enforce that each type conforming to it has a NSHashTable, while also keeping that information private from the consumer of the API.

I think native swift collections with support for weak references for their contents would be very useful, and in more places than just listed above. I don’t think Array could be easily extended to support it (what happens if a value is released? does everything shift down? do they keep their indices?), but Set and Dictionary (where the keys and/or values could be weak, akin to NSMapTable) would be good candidates IMO.

Thoughts?"

--reference to last message thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001579.html
(last messages were 'arguing' how to implement it)

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

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

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


(Riley Testut) #5

Thanks for reviving this Dominik! I left it for a while because I wasn't sure if there was a big enough demand for it (or at least for now while focusing on Swift 3), but if others still think this would be useful I'm very interested in pushing this forward.

Unfortunately because (AFAICT) NSHashTable/NSHashMap don't have CoreFoundation equivalents, any progress on this would have to be more or less from scratch (or so I'm assuming), and not part of the corelibs-foundation project. Maybe some input from the Core Team on how feasible this would be would be helpful.

···

On May 11, 2016, at 8:48 AM, Goffredo Marocchi via swift-evolution <swift-evolution@swift.org> wrote:

Associated object based tricks may be used in the meantime, KVO on objects with an uncertain duration was always problematic.

Sent from my iPhone

On 11 May 2016, at 14:30, Patrick Smith via swift-evolution <swift-evolution@swift.org> wrote:

I remember reading that Swift’s weak references are ‘lazy’, and only clear (and release if needed) when they are next accessed. So you couldn’t observe them AFAIK.

On 11 May 2016, at 11:23 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

I’ve used the following in some similar cases:

  struct Weak<T> {
      weak var value:T?
      init(_ value:T) { self.value = value }
  }
  let myCache = Array<Weak<Foo>>()

When inserting new values I just look for the first one that’s nil or append if it’s taking too long, but you’re right, something more reactive would be better, for example if the above were observable I could do something about it as soon as it became nil.

On 11 May 2016, at 14:00, Dominik Pich via swift-evolution <swift-evolution@swift.org> wrote:

Hello,
I'd like to re-visit a proposal from Riley Testut about weak containers. Since no conclusion/outcome was achieved (AFAICS from looking at the archives and the repository)
and since I just would have needed this again too... I found it a good time to re-propose this :D

---

"
In multiple places in my projects, I essentially recreate the “multiple observer” pattern used by NSNotificationCenter. Originally this was implemented by simply maintaining an array of observers, and adding to/removing from it as necessary. However, this had the unintended side effect of maintaining a strong reference to the observers, which in many cases is undesirable (for the same reasons it’s common to mark delegate properties as weak).

Now, I’m using a private NSHashTable instance, and expose the observers as public API by creating a public computed property which essentially returns an array derived from the NSHashTable like so:

public var receivers: [GameControllerReceiverType] {
// self.privateReceivers.allObjects as! [GameControllerReceiverType] crashes Swift :(
return self.privateReceivers.allObjects.map({ $0 as! GameControllerReceiverType })
}

This workaround works, but is undesirable for a number of reasons. Most notably:

• NSHashTable is not a native Swift collection, and is also not in the Foundation Swift port, so it is not portable to other systems.
• It also has not yet been annotated with generics, so it loses the nice type safety of other Swift collections. Because of this, I have to map the objects to the appropriate type before returning the allObjects array, which runs in O(n) time instead of O(1).
• It’s repetitive. For every type that wants to implement this pattern, they must maintain both a public computed method and a private NSHashTable instance. This gets worse when this should be part of a protocol; there’s no way to enforce that each type conforming to it has a NSHashTable, while also keeping that information private from the consumer of the API.

I think native swift collections with support for weak references for their contents would be very useful, and in more places than just listed above. I don’t think Array could be easily extended to support it (what happens if a value is released? does everything shift down? do they keep their indices?), but Set and Dictionary (where the keys and/or values could be weak, akin to NSMapTable) would be good candidates IMO.

Thoughts?"

--reference to last message thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001579.html
(last messages were 'arguing' how to implement it)

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

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

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

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