[swift-evolution] Sketch: Teach init a 'defer'-like ability to deinit

Patrick Smith pgwsmith at gmail.com
Fri Jun 10 23:05:35 CDT 2016


I like the idea, I don’t like the potential to capture anything else though, or for instances to store closure references. It should offer similar performance to a standard `deinit`.

Instead, what about a sibling to `willSet`, `didSet` etc?

class Foo<T> {
  var ptr: UnsafeMutablePointer<T> {
    deinit {
      ptr.destroy(...)
      ptr.dealloc(...)
    }
  }

  init(count: Int = 0, ptr: UnsafeMutablePointer<T> = nil) {
    self.count = count
    self.space = count

    self.ptr = UnsafeMutablePointer<T>.alloc(count)
    self.ptr.initializeFrom(ptr, count: count)
  }
}

That way you can have as many initializers as you like. It would be called when the object deinitializes only, and not when changing the property.

This could also hopefully be possible with property behaviours.

Patrick

> On 11 Jun 2016, at 9:45 AM, Erica Sadun via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Twitter tl;dr: 
>> Brent: So each instance must remember which init was used for it and then run the matching deinit code at deinit time?
>> Me: In my version, the constructive act and destructive act are always paired, even redundantly, using a stack if needed
>> Graham: so all your deferredDeinit blocks would run, no matter which init was invoked?
>> Brent: Closure stack in the worst case. Might be able to optimize to something cheaper if no captures.  Degenerate case: `for i in 0..<10 { deinit { print(i) } 
> 
> So continuing on from Twitter, assuming the compiler cannot optimize in the case of multiple inits, and init-redirections, how about allowing traditional deinit as well, and introduce compile-time optimization into traditional de-init if the compiler finds only one initialization path per class? We can also warn anyone using my version in a complicated degenerate way that it can be costly through education, manual, etc. It would also help if (especially in Cocoa), you could legally use shared initialization setup closures.
> 
> If I create an observer, I want to be able to handle its end-of-life at that point. If I allocate memory, ditto. Etc etc. Surely Swift should be able to support doing this.
> 
> -- E
> 
>> On Jun 8, 2016, at 3:43 PM, Erica Sadun via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> I really like this idea. Spatially moving cleanup next to unsafe operations is good practice.
>> 
>> In normal code, I want my cleanup to follow as closely as possible to my unsafe act:
>> 
>> let buffer: UnsafeMutablePointer<CChar> = UnsafeMutablePointer(allocatingCapacity: chunkSize)
>>     defer { buffer.deallocateCapacity(chunkSize) }
>> 
>> (Sorry for the horrible example, but it's the best I could grep up with on a moment's notice)
>> 
>> I like your idea but what I want to see is not the deinit child closure in init you propose but a new keyword that means defer-on-deinit-cleanup
>> 
>> self.ptr = UnsafeMutablePointer<T>(allocatingCapacity: count)
>>     deferringDeInit { self.ptr.deallocateCapacity(count) }
>> 
>> Or something.
>> 
>> -- E
>> p.s. Normally I put them on the same line with a semicolon but dang these things can be long
>> 
>>> On Jun 8, 2016, at 10:54 AM, Graham Perks via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> Teach init a 'defer'-like ability to deinit
>>> 
>>> 'defer' is a great way to ensure some clean up code is run; it's declaritive locality to the resource acquisition is a boon to clarity.
>>> 
>>> Swift offers no support for resources acquired during 'init'.
>>> 
>>> For an example, from https://www.mikeash.com/pyblog/friday-qa-2015-04-17-lets-build-swiftarray.html <https://www.mikeash.com/pyblog/friday-qa-2015-04-17-lets-build-swiftarray.html>
>>> 
>>> init(count: Int = 0, ptr: UnsafeMutablePointer<T> = nil) {
>>>     self.count = count
>>>     self.space = count
>>> 
>>>     self.ptr = UnsafeMutablePointer<T>.alloc(count)
>>>     self.ptr.initializeFrom(ptr, count: count)
>>> }
>>> 
>>> deinit {
>>>     ptr.destroy(...)
>>>     ptr.dealloc(...)
>>> }
>>> 
>>> Another 'resource' might be adding an NSNotificationCenter observer, and wanting to unobserve in deinit (no need in OS X 10.11, iOS 9, but for earlier releases this is a valid example).
>>> 
>>> Changing the above code to use a 'defer' style deinit block might look like:
>>> 
>>> init(count: Int = 0, ptr: UnsafeMutablePointer<T> = nil) {
>>>     self.count = count
>>>     self.space = count
>>> 
>>>     self.ptr = UnsafeMutablePointer<T>.alloc(count)
>>>     self.ptr.initializeFrom(ptr, count: count)
>>> 
>>>     deinit {
>>>         ptr.destroy(...)
>>>         ptr.dealloc(...)
>>>     }
>>> 
>>>     // NSNotificationCenter example too
>>>     NSNotificationCenter.defaultCenter().addObserver(...)
>>>     deinit { 
>>>         NSNotificationCenter.defaultCenter().removeObserver(...)
>>>     }
>>> }
>>> 
>>> The need to provide a separate implemention of deinit is gone. Reasoning for 'defer' applies here. There is good locality between what was initialized and what needs cleaning up.
>>> 
>>> Considerations:
>>> 1. Should deinit blocks be invoked before or after code in an explicit deinit method?
>>> 2. Should deinit blocks be allowed in other methods; e.g. viewDidLoad()?
>>> 3. How should deinit blocks be prevented from strongly capturing self (thus preventing themselves from ever running!)?
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160611/3209efaf/attachment.html>


More information about the swift-evolution mailing list