[swift-evolution] deinit and failable initializers

Félix Cloutier felixcca at yahoo.ca
Tue Jan 26 13:14:50 CST 2016


I thought it was a separate magic method. This pattern is fine.

Félix

> Le 26 janv. 2016 à 13:03:47, Joe Groff <jgroff at apple.com> a écrit :
> 
> 
>> On Jan 26, 2016, at 9:58 AM, Félix Cloutier via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> A simple deinitOnNil/deinitOnError will be insufficient as soon as you have to allocate more than one resources with a failable point in between.
> 
> How so? In general, you want to clean up these resources unless you've already committed them to a fully-initialized object. This should do the right thing:
> 
> init() throws {
>   self.foo = alloc(); deferOnError { dealloc(self.foo) }
>   try something()
>   self.bar = alloc(); deferOnError { dealloc(self.bar) }
>   try something()
>   super.init()
> }
> 
> -Joe
> 
>> I think that it could best be solved with move semantics but that's far off Swift 3.
> 
> You don't need move semantics; you could also factor out your resources into dedicated owner classes with non-failable initializers, which would allow for automatic memory management to do the right thing. The problem only arises with unsafe resources or other invariants that require manual management, and it's a general issue with implicit early exits.
> 
> -Joe
> 
>> 
>> Félix
>> 
>>> Le 26 janv. 2016 à 12:15:46, Chris Eidhof via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> a écrit :
>>> 
>>> Now that we can return nil from a failable initializer without having initialized all the properties, it’s easier to make a mistake. For example, consider the following (artificial) code:
>>> 
>>> class MyArray<T> {
>>>     var pointer: UnsafeMutablePointer<T>
>>>     var capacity: Int
>>>     
>>>     init?(capacity: Int) {
>>>         pointer = UnsafeMutablePointer.alloc(capacity)
>>>         if capacity > 100 {
>>>             // Here we should also free the memory. In other words, duplicate the code from deinit.
>>>             return nil
>>>         }
>>>         self.capacity = capacity
>>>         
>>>     }
>>>     
>>>     deinit {
>>>         pointer.destroy(capacity)
>>>     }
>>> }
>>> 
>>> In the `return nil` case, we should really free the memory allocated by the pointer. Or in other words, we need to duplicate the behavior from the deinit.
>>> 
>>> Before Swift 2.2, this mistake wasn’t possible, because we knew that we could count on deinit being called, *always*. With the current behavior, return `nil` is easier, but it does come at the cost of accidentally introducing bugs. As Joe Groff pointed out, a solution would be to have something like “deferOnError” (or in this case, “deferOnNil”), but that feels a bit heavy-weight to me (and you still have to duplicate code).
>>> 
>>> In any case, I think it’s nice that we can now return nil earlier. I don’t like that it goes at the cost of safety, but I realize it’s probably only making things less safe in a small amount of edge cases.
>>> 
>>> Chris
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto: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/20160126/56d8ebd6/attachment.html>


More information about the swift-evolution mailing list