Extending init checks for property initialization


(Shannon Potter) #1

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {
    
    // MARK: Properties

    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer

    // MARK: - Object Lifecycle

    override init(nibName: String?, bundle nibBundle: NSBundle?) {
        super.init(nibName: nibName, bundle: nibBundle)
        
        commonInitialization()
    }

    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)
        
        commonInitialization()
    }

    private func commonInitialization() {
        videoPlayer = AVPlayer(...)
        videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
    }
    
}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

}


(Matthew Johnson) #2

You may wish to have a look at my proposal for partial initializers. It went dormant as I think everyone was burned out on talking about initializers at the time.

https://github.com/anandabits/swift-evolution/blob/partial-initializers/proposals/NNNN-partial-initializers.md

···

Sent from my iPad

On Apr 27, 2016, at 4:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

  // MARK: Properties

  private var videoPlayer: AVPlayer
  private var videoPlayerLayer: AVPlayerLayer

  // MARK: - Object Lifecycle

  override init(nibName: String?, bundle nibBundle: NSBundle?) {
      super.init(nibName: nibName, bundle: nibBundle)

      commonInitialization()
  }

  required init?(coder decoder: NSCoder) {
      super.init(coder: decoder)

      commonInitialization()
  }

  private func commonInitialization() {
      videoPlayer = AVPlayer(...)
      videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
  }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

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


(Basem Emara) #3

I see what you’re saying and the forced optionals is pretty inconvenient.

As far as syntax, how about more of a “deferred” init that gets triggered regardless like this:

defer init() {
    // Always gets called no matter what designated init triggers
}

···

On Apr 27, 2016, at 5:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

   // MARK: Properties

   private var videoPlayer: AVPlayer
   private var videoPlayerLayer: AVPlayerLayer

   // MARK: - Object Lifecycle

   override init(nibName: String?, bundle nibBundle: NSBundle?) {
       super.init(nibName: nibName, bundle: nibBundle)

       commonInitialization()
   }

   required init?(coder decoder: NSCoder) {
       super.init(coder: decoder)

       commonInitialization()
   }

   private func commonInitialization() {
       videoPlayer = AVPlayer(...)
       videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
   }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

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


(Wallacy) #4

Why not use the Designated Initializers and Convenience Initializers
<https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html>
?

    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer

    required override init(){ // <-- commonInitialization()
        videoPlayer = AVPlayer()
        videoPlayerLayer = AVPlayerLayer()
        super.init()
    }

    convenience init?(coder decoder: NSCoder) {
        self.init();
    }

···

Em qui, 28 de abr de 2016 às 12:36, Shannon Potter via swift-evolution < swift-evolution@swift.org> escreveu:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

    // MARK: Properties

    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer

    // MARK: - Object Lifecycle

    override init(nibName: String?, bundle nibBundle: NSBundle?) {
        super.init(nibName: nibName, bundle: nibBundle)

        commonInitialization()
    }

    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)

        commonInitialization()
    }

    private func commonInitialization() {
        videoPlayer = AVPlayer(...)
        videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
    }

}

This does not work. Both properties are non-optional, and the compiler
complains that they are not initialized in either init method. It seems
rather common to want a single point of contact regarding object
initialization, regardless of the path taken to initialize that object.
Ideally, objects could all be funneled to one designated initializer, but
this isn’t always the case.

What are people’s thoughts about either a specialized function that is
always called at the very end of each object’s lifecycle OR some sort of
attribute for a function that hints that the compiler should follow it if
called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

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


(Hooman Mehr) #5

Besides the ages old designated initializer pattern that is already suggested (having a few designated initializers and a bunch of convenience initializers), this is how I have been dealing with this since Swift 1.0:

import UIKit
import AVFoundation

class SomeViewController: UIViewController {
    
    private typealias My = SomeViewController
    
    // MARK: Properties
    
    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer
    
    // MARK: - Object Lifecycle
    
    override init(nibName: String?, bundle nibBundle: NSBundle?) {

        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(nibName: nibName, bundle: nibBundle)
    }
    
    required init?(coder decoder: NSCoder) {
        
        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(coder: decoder)
    }
    
    private static func commonInitialization() -> (AVPlayer, AVPlayerLayer) {
        
        let player = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov"))
        let layer = AVPlayerLayer(player: player)
        
        return (player,layer)
    }
}

It is not perfect, but good enough for me. I usually use this when I have more than one designated initializer and they share a significant amount of code. I usually also have input parameters for this commonInitialization static or class method. I make it a class method when I anticipate subclassing of the class.

Side Note: As you see, I typically define a couple of private type aliases (Usually `I` and/or `My`) to help with readability of code involving static members.

···

On Apr 27, 2016, at 2:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

   // MARK: Properties

   private var videoPlayer: AVPlayer
   private var videoPlayerLayer: AVPlayerLayer

   // MARK: - Object Lifecycle

   override init(nibName: String?, bundle nibBundle: NSBundle?) {
       super.init(nibName: nibName, bundle: nibBundle)

       commonInitialization()
   }

   required init?(coder decoder: NSCoder) {
       super.init(coder: decoder)

       commonInitialization()
   }

   private func commonInitialization() {
       videoPlayer = AVPlayer(...)
       videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
   }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

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


(Vladimir) #6

I think I like this idea. It is clear that it is init() and 'defer' says that it is called at the end of each init. IMO we need exactly some kind of 'init' as only in init we can set un-initialized stored properties.

But, why implicitly unwrapped optionals are not solution here? I.e.

private var videoPlayer: AVPlayer!
private var videoPlayerLayer: AVPlayerLayer!

···

On 28.04.2016 19:04, Basem Emara via swift-evolution wrote:

I see what you’re saying and the forced optionals is pretty inconvenient.

As far as syntax, how about more of a “deferred” init that gets triggered regardless like this:

defer init() {
    // Always gets called no matter what designated init triggers
}

On Apr 27, 2016, at 5:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

   // MARK: Properties

   private var videoPlayer: AVPlayer
   private var videoPlayerLayer: AVPlayerLayer

   // MARK: - Object Lifecycle

   override init(nibName: String?, bundle nibBundle: NSBundle?) {
       super.init(nibName: nibName, bundle: nibBundle)

       commonInitialization()
   }

   required init?(coder decoder: NSCoder) {
       super.init(coder: decoder)

       commonInitialization()
   }

   private func commonInitialization() {
       videoPlayer = AVPlayer(...)
       videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
   }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

}
_______________________________________________
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


(Howard Lovatt) #7

I like the `defer init` idea but suggest you have to explicitly call it at
the end of all the other non-convenience `init`s. The advantage of an
explicit call are two fold:

  1. It is obvious what is happening
  2. You can pass arguments

···

On Friday, 29 April 2016, Basem Emara via swift-evolution < swift-evolution@swift.org> wrote:

I see what you’re saying and the forced optionals is pretty inconvenient.

As far as syntax, how about more of a “deferred” init that gets triggered
regardless like this:

defer init() {
    // Always gets called no matter what designated init triggers
}

> On Apr 27, 2016, at 5:52 PM, Shannon Potter via swift-evolution < > swift-evolution@swift.org <javascript:;>> wrote:
>
> Consider a relatively-common init pattern:
>
> class SomeViewController: UIViewController {
>
> // MARK: Properties
>
> private var videoPlayer: AVPlayer
> private var videoPlayerLayer: AVPlayerLayer
>
> // MARK: - Object Lifecycle
>
> override init(nibName: String?, bundle nibBundle: NSBundle?) {
> super.init(nibName: nibName, bundle: nibBundle)
>
> commonInitialization()
> }
>
> required init?(coder decoder: NSCoder) {
> super.init(coder: decoder)
>
> commonInitialization()
> }
>
> private func commonInitialization() {
> videoPlayer = AVPlayer(...)
> videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
> }
>
> }
>
> This does not work. Both properties are non-optional, and the compiler
complains that they are not initialized in either init method. It seems
rather common to want a single point of contact regarding object
initialization, regardless of the path taken to initialize that object.
Ideally, objects could all be funneled to one designated initializer, but
this isn’t always the case.
>
> What are people’s thoughts about either a specialized function that is
always called at the very end of each object’s lifecycle OR some sort of
attribute for a function that hints that the compiler should follow it if
called in an init function to check for property initialization?
>
> func commonInit() {
>
> }
>
> or
>
> @extend_init private func commonInitialization() {
>
> }
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <javascript:;>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <javascript:;>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-- Howard.


(Basem Emara) #8

Hey Hooman, that’s very elegant. I didn’t think of it like that and will definitely use, thx!

Though wouldn’t “defer init()” take it a step further: 1) reduce redundant boilerplate 2) prevent forgetting/bugs and 3) smaller memory allocation footprint? It also fits well with the existing Swift 2 “defer” keyword used in functions. Here’s what your sample would look like:

import UIKit
import AVFoundation

class SomeViewController: UIViewController {
    
    // MARK: Properties
    
    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer
    
    // MARK: - Object Lifecycle
    
    override init(nibName: String?, bundle nibBundle: NSBundle?) {

        super.init(nibName: nibName, bundle: nibBundle)
    }
    
    required init?(coder decoder: NSCoder) {
        
        super.init(coder: decoder)
    }
   
    defer init() {
        videoPlayer = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov"))
        videoPlayerLayer = AVPlayerLayer(player: player)
    }
}

···

On Apr 30, 2016, at 2:18 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

Besides the ages old designated initializer pattern that is already suggested (having a few designated initializers and a bunch of convenience initializers), this is how I have been dealing with this since Swift 1.0:

import UIKit
import AVFoundation

class SomeViewController: UIViewController {
    
    private typealias My = SomeViewController
    
    // MARK: Properties
    
    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer
    
    // MARK: - Object Lifecycle
    
    override init(nibName: String?, bundle nibBundle: NSBundle?) {

        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(nibName: nibName, bundle: nibBundle)
    }
    
    required init?(coder decoder: NSCoder) {
        
        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(coder: decoder)
    }
    
    private static func commonInitialization() -> (AVPlayer, AVPlayerLayer) {
        
        let player = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov"))
        let layer = AVPlayerLayer(player: player)
        
        return (player,layer)
    }
}

It is not perfect, but good enough for me. I usually use this when I have more than one designated initializer and they share a significant amount of code. I usually also have input parameters for this commonInitialization static or class method. I make it a class method when I anticipate subclassing of the class.

Side Note: As you see, I typically define a couple of private type aliases (Usually `I` and/or `My`) to help with readability of code involving static members.

On Apr 27, 2016, at 2:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

   // MARK: Properties

   private var videoPlayer: AVPlayer
   private var videoPlayerLayer: AVPlayerLayer

   // MARK: - Object Lifecycle

   override init(nibName: String?, bundle nibBundle: NSBundle?) {
       super.init(nibName: nibName, bundle: nibBundle)

       commonInitialization()
   }

   required init?(coder decoder: NSCoder) {
       super.init(coder: decoder)

       commonInitialization()
   }

   private func commonInitialization() {
       videoPlayer = AVPlayer(...)
       videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
   }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

}
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(David Waite) #9

This is probably the best pattern to deal with this, since there are a number of ugly problems to deal with if passing an incomplete object around. initializers exist with a lot of special case rules around self because they are reusing the term for something that is actually not yet self, but an incubating object.

There are challenges just in passing this incubating self into another function or static method, or (even worse) calling a method on that incomplete object which might be dynamically dispatched. For example, you may require synthesizing something self-like to pass around instead, and have to create new versions of the functions that can operate with the structure and honor rules about the behavior if say a read against an uninitialized videoPlayerLayer happens. Even solving the problems of making this work, you would need to deterministically know the values were set (including in the case where the method you called was actually redefined by a subclass in another module), and apply them back into the incubating self object.

tl;dr initializers are complex and the language has restrictions and requirements around initializers to hide complexity and enforce safety.

-DW

···

On Apr 30, 2016, at 12:18 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

Besides the ages old designated initializer pattern that is already suggested (having a few designated initializers and a bunch of convenience initializers), this is how I have been dealing with this since Swift 1.0:

import UIKit
import AVFoundation

class SomeViewController: UIViewController {
    
    private typealias My = SomeViewController
    
    // MARK: Properties
    
    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer
    
    // MARK: - Object Lifecycle
    
    override init(nibName: String?, bundle nibBundle: NSBundle?) {

        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(nibName: nibName, bundle: nibBundle)
    }
    
    required init?(coder decoder: NSCoder) {
        
        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(coder: decoder)
    }
    
    private static func commonInitialization() -> (AVPlayer, AVPlayerLayer) {
        
        let player = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov"))
        let layer = AVPlayerLayer(player: player)
        
        return (player,layer)
    }
}

It is not perfect, but good enough for me. I usually use this when I have more than one designated initializer and they share a significant amount of code. I usually also have input parameters for this commonInitialization static or class method. I make it a class method when I anticipate subclassing of the class.

Side Note: As you see, I typically define a couple of private type aliases (Usually `I` and/or `My`) to help with readability of code involving static members.

On Apr 27, 2016, at 2:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

   // MARK: Properties

   private var videoPlayer: AVPlayer
   private var videoPlayerLayer: AVPlayerLayer

   // MARK: - Object Lifecycle

   override init(nibName: String?, bundle nibBundle: NSBundle?) {
       super.init(nibName: nibName, bundle: nibBundle)

       commonInitialization()
   }

   required init?(coder decoder: NSCoder) {
       super.init(coder: decoder)

       commonInitialization()
   }

   private func commonInitialization() {
       videoPlayer = AVPlayer(...)
       videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
   }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

}
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Basem Emara) #10

Good point about unwrapped optionals, but there's no compiler checks to make sure they get initialized.

The "defer init" solves the same problem that "defer" within functions resolved. There's no need to duplicate the function call in every init, or forget to call one as new initializers are added.

···

On Apr 28, 2016, at 12:50 PM, Vladimir.S <svabox@gmail.com> wrote:

I think I like this idea. It is clear that it is init() and 'defer' says that it is called at the end of each init. IMO we need exactly some kind of 'init' as only in init we can set un-initialized stored properties.

But, why implicitly unwrapped optionals are not solution here? I.e.

private var videoPlayer: AVPlayer!
private var videoPlayerLayer: AVPlayerLayer!

On 28.04.2016 19:04, Basem Emara via swift-evolution wrote:
I see what you’re saying and the forced optionals is pretty inconvenient.

As far as syntax, how about more of a “deferred” init that gets triggered regardless like this:

defer init() {
   // Always gets called no matter what designated init triggers
}

On Apr 27, 2016, at 5:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

  // MARK: Properties

  private var videoPlayer: AVPlayer
  private var videoPlayerLayer: AVPlayerLayer

  // MARK: - Object Lifecycle

  override init(nibName: String?, bundle nibBundle: NSBundle?) {
      super.init(nibName: nibName, bundle: nibBundle)

      commonInitialization()
  }

  required init?(coder decoder: NSCoder) {
      super.init(coder: decoder)

      commonInitialization()
  }

  private func commonInitialization() {
      videoPlayer = AVPlayer(...)
      videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
  }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

}
_______________________________________________
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


(David Sweeris) #11

To avoid the extra overhead. Also, they’re sorta going away in Swift 3: https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md

- Dave Sweeris

···

On Apr 28, 2016, at 11:50 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

I think I like this idea. It is clear that it is init() and 'defer' says that it is called at the end of each init. IMO we need exactly some kind of 'init' as only in init we can set un-initialized stored properties.

But, why implicitly unwrapped optionals are not solution here? I.e.


(David Sweeris) #12

Eh, “going away” might be too strong… They’re changing a lot. There’s still the extra overhead though.

···

On Apr 28, 2016, at 7:49 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 28, 2016, at 11:50 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

I think I like this idea. It is clear that it is init() and 'defer' says that it is called at the end of each init. IMO we need exactly some kind of 'init' as only in init we can set un-initialized stored properties.

But, why implicitly unwrapped optionals are not solution here? I.e.

To avoid the extra overhead. Also, they’re sorta going away in Swift 3: https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md

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


(Vladimir) #13

They *will* be initialized with nil. It's your business to fill any other value to them. So you do this in commonInitialization()
But again, I agree that such "defer init()" will be the best(IMO) solution here.

···

On 28.04.2016 20:27, Basem Emara wrote:

Good point about unwrapped optionals, but there's no compiler checks to make sure they get initialized.

The "defer init" solves the same problem that "defer" within functions resolved. There's no need to duplicate the function call in every init, or forget to call one as new initializers are added.

On Apr 28, 2016, at 12:50 PM, Vladimir.S <svabox@gmail.com> wrote:

I think I like this idea. It is clear that it is init() and 'defer' says that it is called at the end of each init. IMO we need exactly some kind of 'init' as only in init we can set un-initialized stored properties.

But, why implicitly unwrapped optionals are not solution here? I.e.

private var videoPlayer: AVPlayer!
private var videoPlayerLayer: AVPlayerLayer!

On 28.04.2016 19:04, Basem Emara via swift-evolution wrote:
I see what you’re saying and the forced optionals is pretty inconvenient.

As far as syntax, how about more of a “deferred” init that gets triggered regardless like this:

defer init() {
   // Always gets called no matter what designated init triggers
}

On Apr 27, 2016, at 5:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

  // MARK: Properties

  private var videoPlayer: AVPlayer
  private var videoPlayerLayer: AVPlayerLayer

  // MARK: - Object Lifecycle

  override init(nibName: String?, bundle nibBundle: NSBundle?) {
      super.init(nibName: nibName, bundle: nibBundle)

      commonInitialization()
  }

  required init?(coder decoder: NSCoder) {
      super.init(coder: decoder)

      commonInitialization()
  }

  private func commonInitialization() {
      videoPlayer = AVPlayer(...)
      videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
  }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

}
_______________________________________________
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


(Vladimir) #14

Can't see any significant extra overhead because of implicity unwrapped property for your code. Could you clarify?

As for "going away" - as I understand, just ImplicitlyUnwrappedOptional type itself will be removed, but not the "implicitly unwrapped" feature:
"Appending ! to the type of a Swift declaration will give it optional type and annotate the declaration with an attribute stating that it may be implicitly unwrapped when used."

So I still think right now the solution for your issue is implicitly unwrapped optional. But again, I 100% support the idea of "defer init", more specifically the idea of explicitly called "defer init" in other inits, if "defer init()" is defined.

···

On 29.04.2016 3:49, David Sweeris wrote:

On Apr 28, 2016, at 11:50 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

I think I like this idea. It is clear that it is init() and 'defer' says that it is called at the end of each init. IMO we need exactly some kind of 'init' as only in init we can set un-initialized stored properties.

But, why implicitly unwrapped optionals are not solution here? I.e.

To avoid the extra overhead. Also, they’re sorta going away in Swift 3: https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md

- Dave Sweeris.


(Vladimir) #15

I like the `defer init` idea but suggest you have to explicitly call it at
the end of all the other non-convenience `init`s. The advantage of an
explicit call are two fold:

  1. It is obvious what is happening
  2. You can pass arguments

Like this idea very much. IMO it is important to see that "defer init" will be called at the end when we are looking at the code of init. Otherwise we need to don't forget that "defer init" could be also defined and are called at the end of this init.

P.S. Sorry for duplicate.

···

On 29.04.2016 2:21, Howard Lovatt via swift-evolution wrote:

On Friday, 29 April 2016, Basem Emara via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    I see what you’re saying and the forced optionals is pretty inconvenient.

    As far as syntax, how about more of a “deferred” init that gets
    triggered regardless like this:

    defer init() {
        // Always gets called no matter what designated init triggers
    }

    > On Apr 27, 2016, at 5:52 PM, Shannon Potter via swift-evolution > <swift-evolution@swift.org> wrote:
    >
    > Consider a relatively-common init pattern:
    >
    > class SomeViewController: UIViewController {
    >
    > // MARK: Properties
    >
    > private var videoPlayer: AVPlayer
    > private var videoPlayerLayer: AVPlayerLayer
    >
    > // MARK: - Object Lifecycle
    >
    > override init(nibName: String?, bundle nibBundle: NSBundle?) {
    > super.init(nibName: nibName, bundle: nibBundle)
    >
    > commonInitialization()
    > }
    >
    > required init?(coder decoder: NSCoder) {
    > super.init(coder: decoder)
    >
    > commonInitialization()
    > }
    >
    > private func commonInitialization() {
    > videoPlayer = AVPlayer(...)
    > videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
    > }
    >
    > }
    >
    > This does not work. Both properties are non-optional, and the
    compiler complains that they are not initialized in either init method.
    It seems rather common to want a single point of contact regarding
    object initialization, regardless of the path taken to initialize that
    object. Ideally, objects could all be funneled to one designated
    initializer, but this isn’t always the case.
    >
    > What are people’s thoughts about either a specialized function that
    is always called at the very end of each object’s lifecycle OR some
    sort of attribute for a function that hints that the compiler should
    follow it if called in an init function to check for property
    initialization?
    >
    > func commonInit() {
    >
    > }
    >
    > or
    >
    > @extend_init private func commonInitialization() {
    >
    > }
    > _______________________________________________
    > 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

--
-- Howard.

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


(Chris Lattner) #16

SE-0054 shouldn’t eliminate use of IUO’s for properties that are “delay initialized”. The only effect it will have is that it will prevent downstream references to those properties from accidentally propagating the IUO’ness.

-Chris

···

On Apr 28, 2016, at 5:49 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 28, 2016, at 11:50 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

I think I like this idea. It is clear that it is init() and 'defer' says that it is called at the end of each init. IMO we need exactly some kind of 'init' as only in init we can set un-initialized stored properties.

But, why implicitly unwrapped optionals are not solution here? I.e.

To avoid the extra overhead. Also, they’re sorta going away in Swift 3: https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md


(Shannon Potter) #17

Agreed. The static option is definitely better. But I think a deferred option would be that much more elegant.

···

On Apr 30, 2016, at 11:30 AM, Basem Emara <contact@basememara.com> wrote:

Hey Hooman, that’s very elegant. I didn’t think of it like that and will definitely use, thx!

Though wouldn’t “defer init()” take it a step further: 1) reduce redundant boilerplate 2) prevent forgetting/bugs and 3) smaller memory allocation footprint? It also fits well with the existing Swift 2 “defer” keyword used in functions. Here’s what your sample would look like:

import UIKit
import AVFoundation

class SomeViewController: UIViewController {
    
    // MARK: Properties
    
    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer
    
    // MARK: - Object Lifecycle
    
    override init(nibName: String?, bundle nibBundle: NSBundle?) {

        super.init(nibName: nibName, bundle: nibBundle)
    }
    
    required init?(coder decoder: NSCoder) {
        
        super.init(coder: decoder)
    }
   
    defer init() {
        videoPlayer = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov"))
        videoPlayerLayer = AVPlayerLayer(player: player)
    }
}

On Apr 30, 2016, at 2:18 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

Besides the ages old designated initializer pattern that is already suggested (having a few designated initializers and a bunch of convenience initializers), this is how I have been dealing with this since Swift 1.0:

import UIKit
import AVFoundation

class SomeViewController: UIViewController {
    
    private typealias My = SomeViewController
    
    // MARK: Properties
    
    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer
    
    // MARK: - Object Lifecycle
    
    override init(nibName: String?, bundle nibBundle: NSBundle?) {

        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(nibName: nibName, bundle: nibBundle)
    }
    
    required init?(coder decoder: NSCoder) {
        
        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(coder: decoder)
    }
    
    private static func commonInitialization() -> (AVPlayer, AVPlayerLayer) {
        
        let player = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov"))
        let layer = AVPlayerLayer(player: player)
        
        return (player,layer)
    }
}

It is not perfect, but good enough for me. I usually use this when I have more than one designated initializer and they share a significant amount of code. I usually also have input parameters for this commonInitialization static or class method. I make it a class method when I anticipate subclassing of the class.

Side Note: As you see, I typically define a couple of private type aliases (Usually `I` and/or `My`) to help with readability of code involving static members.

On Apr 27, 2016, at 2:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

   // MARK: Properties

   private var videoPlayer: AVPlayer
   private var videoPlayerLayer: AVPlayerLayer

   // MARK: - Object Lifecycle

   override init(nibName: String?, bundle nibBundle: NSBundle?) {
       super.init(nibName: nibName, bundle: nibBundle)

       commonInitialization()
   }

   required init?(coder decoder: NSCoder) {
       super.init(coder: decoder)

       commonInitialization()
   }

   private func commonInitialization() {
       videoPlayer = AVPlayer(...)
       videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
   }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

}
_______________________________________________
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


(Austin Zheng) #18

'defer' in this example means something different than 'defer' as used in Swift today. I would expect a 'defer init()' to be called immediately before the main initializer goes out of scope, like how a defer block works today, but this defer init() must be called when the main initializer first comes into scope (before the super call).

I don't think there's a clarity gain here. If I retroactively add an initializer to a class I don't have access to via an extension, how do I know whether or not a defer initializer I can't see is being called, or what it's doing? What if I want common initializer code after the super.init call? For symmetry, do we need 'predefer' and 'postdefer'? How do defer init()s interact with access control - can I silently break my initializer just by changing its access control level?

A static function that initializers can call, however, has the notable benefit of behaving the exact same way as a static function anywhere else in code while still respecting the complex init member usage rules.

Austin

···

On Apr 30, 2016, at 11:30 AM, Basem Emara via swift-evolution <swift-evolution@swift.org> wrote:

Hey Hooman, that’s very elegant. I didn’t think of it like that and will definitely use, thx!

Though wouldn’t “defer init()” take it a step further: 1) reduce redundant boilerplate 2) prevent forgetting/bugs and 3) smaller memory allocation footprint? It also fits well with the existing Swift 2 “defer” keyword used in functions. Here’s what your sample would look like:

import UIKit
import AVFoundation

class SomeViewController: UIViewController {
    
    // MARK: Properties
    
    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer
    
    // MARK: - Object Lifecycle
    
    override init(nibName: String?, bundle nibBundle: NSBundle?) {

        super.init(nibName: nibName, bundle: nibBundle)
    }
    
    required init?(coder decoder: NSCoder) {
        
        super.init(coder: decoder)
    }
   
    defer init() {
        videoPlayer = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov"))
        videoPlayerLayer = AVPlayerLayer(player: player)
    }
}

On Apr 30, 2016, at 2:18 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Besides the ages old designated initializer pattern that is already suggested (having a few designated initializers and a bunch of convenience initializers), this is how I have been dealing with this since Swift 1.0:

import UIKit
import AVFoundation

class SomeViewController: UIViewController {
    
    private typealias My = SomeViewController
    
    // MARK: Properties
    
    private var videoPlayer: AVPlayer
    private var videoPlayerLayer: AVPlayerLayer
    
    // MARK: - Object Lifecycle
    
    override init(nibName: String?, bundle nibBundle: NSBundle?) {

        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(nibName: nibName, bundle: nibBundle)
    }
    
    required init?(coder decoder: NSCoder) {
        
        (videoPlayer, videoPlayerLayer) = My.commonInitialization()

        super.init(coder: decoder)
    }
    
    private static func commonInitialization() -> (AVPlayer, AVPlayerLayer) {
        
        let player = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov"))
        let layer = AVPlayerLayer(player: player)
        
        return (player,layer)
    }
}

It is not perfect, but good enough for me. I usually use this when I have more than one designated initializer and they share a significant amount of code. I usually also have input parameters for this commonInitialization static or class method. I make it a class method when I anticipate subclassing of the class.

Side Note: As you see, I typically define a couple of private type aliases (Usually `I` and/or `My`) to help with readability of code involving static members.

On Apr 27, 2016, at 2:52 PM, Shannon Potter via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Consider a relatively-common init pattern:

class SomeViewController: UIViewController {

   // MARK: Properties

   private var videoPlayer: AVPlayer
   private var videoPlayerLayer: AVPlayerLayer

   // MARK: - Object Lifecycle

   override init(nibName: String?, bundle nibBundle: NSBundle?) {
       super.init(nibName: nibName, bundle: nibBundle)

       commonInitialization()
   }

   required init?(coder decoder: NSCoder) {
       super.init(coder: decoder)

       commonInitialization()
   }

   private func commonInitialization() {
       videoPlayer = AVPlayer(...)
       videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
   }

}

This does not work. Both properties are non-optional, and the compiler complains that they are not initialized in either init method. It seems rather common to want a single point of contact regarding object initialization, regardless of the path taken to initialize that object. Ideally, objects could all be funneled to one designated initializer, but this isn’t always the case.

What are people’s thoughts about either a specialized function that is always called at the very end of each object’s lifecycle OR some sort of attribute for a function that hints that the compiler should follow it if called in an init function to check for property initialization?

func commonInit() {

}

or

@extend_init private func commonInitialization() {

}
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(David Sweeris) #19

It's probably not significant in UI code, but if you’re writing something that’ll be accessed a 100k or a 1M times in what should be a tight loop, it all adds up.

- Dave Sweeris

···

On Apr 29, 2016, at 3:00 AM, Vladimir.S <svabox@gmail.com> wrote:

Can't see any significant extra overhead because of implicity unwrapped property for your code. Could you clarify?