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:
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:
.