Differences between init() methods

What are the differences between :


convenience init()

required init()

override init()

init()


I am getting error, where I have two init methods.

convenience init()
and
init()


Error is : 'self' used before super.init call

I have write

super.init()
, then also no luck. Please let me know about above methods.

Accepted Reply

convenience init is an init you define to pass some parameters.


It must have a different signature than the override init() : usually, it is the parameter you want to pass to the init. But even if you have no parameter to pass, you should pass a dummy one to differentiate signature.


Here is an example in my code



    override init(window: NSWindow!)
    {
        super.init(window: window)
    }

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

    convenience init(dataType: DataType) {
     
        self.init(windowNibName: "XXXXX")        
        content = MyObject(dataType: dataType)  // Initialize the object model
    }

Have a look here to understand all details :


https : / / developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-ID222

Replies

convenience init is an init you define to pass some parameters.


It must have a different signature than the override init() : usually, it is the parameter you want to pass to the init. But even if you have no parameter to pass, you should pass a dummy one to differentiate signature.


Here is an example in my code



    override init(window: NSWindow!)
    {
        super.init(window: window)
    }

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

    convenience init(dataType: DataType) {
     
        self.init(windowNibName: "XXXXX")        
        content = MyObject(dataType: dataType)  // Initialize the object model
    }

Have a look here to understand all details :


https : / / developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-ID222

Claude31 offered a very general answer, which is all we can do given the info you posted. If you post a cut down example of the case you’re trying to cope with, we should be able to offer more specific advice.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Here is my code snippet :



class FileOutputStream : OutputStream {

    fileprivate let filepath:URL
    fileprivate let channel:DispatchIO!

    convenience init?(filename:String) {
    
        let pathURL = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in:.userDomainMask).first!.appendingPathComponent(filename)
        self.init(filepath:pathURL)
    }

    init?(filepath f:URL) {
    
        self.filepath = f
    
        /
        if let cpath = (f.path).cString(using: String.Encoding.utf8) {
        
            let outputflag:Int32 = O_CREAT | O_WRONLY            
            let mode:mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
            self.channel = DispatchIO(type: DispatchIO.StreamType.stream, path:cpath, oflag:outputflag,mode: mode, queue: DispatchQueue.global(qos: DispatchQoS.QoSClass.background)) { (errcode:Int32) -> Void in
            
                if errcode != 0 {
                    print("FileOutputStream: error creating io channel")
                }
            }
        }
        else {
            self.channel = nil
            return nil
        }
        super.init(url: self.filepath, append: true)!
    }

    func write(_ string: String) {
    
        if let dataString = string.data(using: String.Encoding.utf8) {
        
            dataString.withUnsafeBytes {(bytes: UnsafePointer<UInt8>) -> Void in
            
                var data = DispatchData.empty
                data.append(bytes, count: dataString.count)
            
                self.channel.write(offset: 0, data: data, queue: DispatchQueue.global(qos:.background), ioHandler: { (complete: Bool, data: DispatchData?, errorCode: Int32) in
                
                    /
                    if errorCode != 0 {
                        print("FileOutputStream: error writing data to channel")
                    }
                })
            }
        }
    }

    deinit {
    
        self.channel.close(flags: DispatchIO.CloseFlags.stop)
    }
}



@eskimo and @Claude31 Please take a look on my snippet and give me more specification, if possible. Bacause everytime I confused between these four types of difinations of init()

In general the rules for how a subclass should override initialisers are well defined, as discussed in the doc that Claude31 referenced. What’s weird here is OutputStream. The Objective-C declaration lists three designated initialisers for NSOutputStream [1], none of which make sense to call super with from an arbitrary subclass, which is what you have to do.

The good news here is that the superclass initialiser that you call is more-or-less irrelevant. When you subclass NSOutputStream, all the designated initialisers act as no-ops because the instance you get back is your subclass of the abstract NSOutputStream, which is not what you get back when you instantiate NSOutputStream normally. Consider this:

let sConcrete = OutputStream(toMemory: ())
print(type(of:sConcrete))       // prints "__NSCFOutputStream"
print(sConcrete.superclass!)    // prints "NSOutputStream"

This is the whole Cocoa class cluster concept in action; see the so-named section of the Concepts in Objective-C Programming doc.

In contrast, when you subclass NSOutputStream yourself, your subclass is directly layered on top of the abstract NSOutputStream.

class DummyOutputStream : OutputStream {
}

let sDummy = DummyOutputStream(toMemory: ())
print(type(of:sDummy))          // prints "DummyOutputStream"
print(sDummy.superclass!)       // prints "NSOutputStream"

Calling any method on that instance, even something as innocuous as

-streamStatus
, will trigger a failure. For example, this:
print(sDummy.streamStatus)

generates this:

2016-10-28 11:06:01.384 xxst[4901:363171] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -streamStatus only defined for abstract class.  Define -[xxst.DummyOutputStream streamStatus]!'

This means that, when you override NSStream and NSOutputStream methods in your subclass, you must not call super.

If I were in your shoes I’d do two things:

  • override all of the initialisers from

    OutputStream
    and
    Stream
    to make them private (so they don’t show up in code completion) and crashing (so if you find out if they’re called dynamically)
  • add my own designated and convenience initialisers as required

Here’s an example:

struct Blob {
    let squishiness: Int
}

class BlobStream : OutputStream {

    required init(blob: Blob) {
        self.blob = blob
        super.init(toMemory: ())
    }

    convenience init(squishiness: Int) {
        self.init(blob: Blob(squishiness: squishiness))
    }

    private init() {
        fatalError()
    }

    private override init(toBuffer buffer: UnsafeMutablePointer<UInt8>, capacity: Int) {
        fatalError()
    }

    private override init?(url: URL, append shouldAppend: Bool) {
        fatalError()
    }

    let blob: Blob

    … more code here…
}

At this point you have to override and implement every NSStream and NSOutputStream that might be called by your code (or by any code you pass the stream to).

Obviously this is more than a little wacky, so please feel free to file a bug report describing your goạls, the issues you hit, and what you’d like to see improved. I’d appreciate you posting your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] Namely

-initToMemory
,
-initToBuffer:capacity:
and
-initWithURL:append:
.