<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">In my opinion deinit should be called only for objets that we fully initialized successfully (as it works now).<div class="">How about allowing early return only when no properties were initialized. </div><div class=""><br class=""></div><div class="">The possible problem with this solution is that complex initializers would need to store intermediate results in local variables before assigning them to properties.</div><div class="">Example: <br class=""><div class=""><div class=""><br class="webkit-block-placeholder"></div><div class=""><div class=""><div class="">struct MyArray<T> {<br class=""> var pointer: UnsafeMutablePointer<T><br class=""> var capacity: Int<br class=""><br class=""> init?(capacity: Int) {<br class=""> self.pointer = UnsafeMutablePointer.alloc(capacity)<br class=""> if capacity > 100 {<br class=""> return nil // Error. Can't return nil after partial initialization. <br class=""> }<br class=""> self.capacity = capacity<br class=""> }<br class=""><br class=""> init?(capacity: Int) {<br class=""> if capacity > 100 {<br class=""> return nil // This is ok, early return before initializing any property<br class=""> }<br class=""> self.pointer = UnsafeMutablePointer.alloc(capacity)<br class=""> self.capacity = capacity<br class=""> }</div><div class="">}</div><div class=""><br class=""></div><div class="">Kostiantyn</div></div>
</div>
<br class=""><div><blockquote type="cite" class=""><div class="">On 27 Jan 2016, at 02:40, Joseph Lord via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="content-type" content="text/html; charset=utf-8" class=""><div dir="auto" class=""><div class=""></div><div class=""><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">I think that there is risk here but I don't think it is serious enough one to be with making changes for.</span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">1. For most classes there is no problem because everything is managed by ARC anyway so will clean up without special consideration (few of my classes require a deinit). </span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">2. It doesn't seem surprising that if init did not succeed that deinit will not run.</span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">I suppose the risk is greatest where the nil is a result of a returned value from a call made being passed up as explicit `return nil` or `throw` are more visible. </span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">If something were to be changed I wonder if in cases where a failable init exists in a class with a deinit the init could still be responsible for the clean up but must also call an special empty function to confirm to compiler that the situation has been handled. Not sure on name but maybe `deinited()`. Maybe it would just be a warning if not called. Treatment could maybe be similar to warnings where super calls are not made.</span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">Joseph</span></div></div><div class=""><br class=""></div><div class=""><br class="">On Jan 26, 2016, at 9:39 PM, Chris Eidhof via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><span style="font-size:13px" class="">Absolutely, I agree with you. Of course, this was a simplified example, you can easily construct an example where just reordering isn’t going to cut it.</span><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">It’s not hard to do things right in the current model. We can reorder statements, write wrappers, or think of different solutions altogether. Once you understand how it works, it’s very easy to write correct code.</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">However, my complaint is that it’s now also easy to make a mistake. In the previous model, it wasn’t so easy (the previous model was simpler: an init would always be matched by a deinit).</div></div><div class="gmail_extra"><br class=""><div class="gmail_quote">On Tue, Jan 26, 2016 at 6:39 PM, Douglas Gregor <span dir="ltr" class=""><<a href="mailto:dgregor@apple.com" target="_blank" class="">dgregor@apple.com</a>></span> wrote:<br class=""><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><span class=""><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Jan 26, 2016, at 9:15 AM, Chris Eidhof via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>> wrote:</div><br class=""><div class=""><div style="word-wrap:break-word" class="">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:<div class=""><br class=""></div><div class=""><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""><span style="color:#bb2ca2" class="">class</span> MyArray<T> {</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT';color:rgb(112,61,170)" class=""><span class=""> </span><span style="color:#bb2ca2" class="">var</span><span class=""> pointer: </span>UnsafeMutablePointer<span class=""><</span><span style="color:#4f8187" class="">T</span><span class="">></span></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> <span style="color:#bb2ca2" class="">var</span> capacity: <span style="color:#703daa" class="">Int</span></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT';min-height:15px" class=""> <br class=""></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> <span style="color:#bb2ca2" class="">init</span>?(capacity: <span style="color:#703daa" class="">Int</span>) {</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> <span style="color:#4f8187" class="">pointer</span> = <span style="color:#703daa" class="">UnsafeMutablePointer</span>.<span style="color:#3d1d81" class="">alloc</span>(capacity)</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> <span style="color:#bb2ca2" class="">if</span> capacity > <span style="color:#272ad8" class="">100</span> {</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT';color:rgb(0,132,0)" class=""><span class=""> </span>// Here we should also free the memory. In other words, duplicate the code from deinit.</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> <span style="color:#bb2ca2" class="">return</span> <span style="color:#bb2ca2" class="">nil</span></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> }</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> <span style="color:#bb2ca2" class="">self</span>.<span style="color:#4f8187" class="">capacity</span> = capacity</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT';min-height:15px" class=""> <br class=""></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> }</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT';min-height:15px" class=""> <br class=""></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> <span style="color:#bb2ca2" class="">deinit</span> {</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT';color:rgb(79,129,135)" class=""><span class=""> </span>pointer<span class="">.</span><span style="color:#3d1d81" class="">destroy</span><span class="">(</span>capacity<span class="">)</span></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""> }</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class="">}</div></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""><br class=""></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class="">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.</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""><br class=""></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class="">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).</div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class=""><br class=""></div><div style="margin:0px;line-height:normal;font-family:'Akkurat TT'" class="">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.</div></div></div></blockquote><br class=""></div></span><div class="">Let’s re-order the statements in your example:</div><div class=""><br class=""></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px" class=""><span class=""><div class=""><div class=""><font face="Menlo" class="">class MyArray<T> {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> var pointer: UnsafeMutablePointer<T></font></div></div><div class=""><div class=""><font face="Menlo" class=""> var capacity: Int</font></div></div><div class=""><div class=""><br class=""></div></div><div class=""><div class=""><font face="Menlo" class=""> init?(capacity: Int) {</font></div></div></span><span class=""><div class=""><div class=""><span style="font-family:Menlo" class=""> if capacity > 100 {</span></div></div><div class=""><div class=""><font face="Menlo" class=""> // Here we should also free the memory. In other words, duplicate the code from deinit.</font></div></div><div class=""><div class=""><font face="Menlo" class=""> return nil</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class=""> self.capacity = capacity</font></div></div></span><div class=""><div class=""><div class=""><font face="Menlo" class=""> pointer = UnsafeMutablePointer.alloc(capacity)</font></div></div><div class=""></div><div class=""><span style="font-family:Menlo" class=""> }</span></div></div><div class=""><div class=""><font face="Menlo" class=""> </font></div></div><div class=""><div class=""><font face="Menlo" class=""> deinit {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> pointer.destroy(capacity)</font></div></div><div class=""><div class=""><span style="font-family:Menlo" class=""> }</span></div></div><div class=""><div class=""><font face="Menlo" class="">}</font></div></div></blockquote><div class=""><div class=""><br class=""></div><div class="">If the initializer returns ‘nil’ and we still call deinit, we end up destroying a pointer that was never allocated.</div><div class=""><br class=""></div><div class="">If you come from an Objective-C background, you might expect implicit zeroing of the allocated block to help here. However, Swift doesn’t have that, because many Swift types don’t have a “zero” state that’s safe to destroy. For example, anything with a member of non-optional reference type, e.g.,</div><div class=""><br class=""></div></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px" class=""><div class=""><div class=""><font face="Menlo" class="">class ClassWrapper {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> var array: MyArray<String></font></div></div><div class=""><div class=""><font face="Menlo" class=""><br class=""></font></div></div><div class=""><div class=""><font face="Menlo" class=""> init(array: MyArray<String>) {</font></div></div><div class=""><div class=""><font face="Menlo" class=""> self.array = array</font></div></div><div class=""><div class=""><font face="Menlo" class=""> }</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class=""> deinit {</font></div><div class=""><font face="Menlo" class=""> print(array) // array is a valid instance of MyArray<String></font></div><div class=""><font face="Menlo" class=""> }</font></div></div><div class=""><div class=""><font face="Menlo" class="">}</font></div></div></blockquote><div class=""><div class=""><br class=""></div><div class="">A valid ClassWrapper instance will always have an instance of MyArray<String>, even throughout its deinitializer.</div><div class=""><br class=""></div><div class="">The basic property here is that one cannot run a deinitializer on an instance that hasn’t been fully constructed (all the way up the class hierarchy).</div><div class=""><br class=""></div></div><div class=""><span style="white-space:pre-wrap" class="">        </span>- Doug</div><div class=""><br class=""></div><br class=""></div></blockquote></div><br class=""><br clear="all" class=""><div class=""><br class=""></div>-- <br class=""><div class="gmail_signature">Chris Eidhof</div>
</div>
</div></blockquote><blockquote type="cite" class=""><div class=""><span class="">_______________________________________________</span><br class=""><span class="">swift-evolution mailing list</span><br class=""><span class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a></span><br class=""><span class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br class=""></div></blockquote></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></div></div></body></html>