Swift: Self.init called multiple times in initializer - swift

This one has me stumped. I can't figure out why Swift is complaining that self.init is called more than once in this code:
public init(body: String) {
let parser = Gravl.Parser()
if let node = parser.parse(body) {
super.init(document: self, gravlNode: node)
} else {
// Swift complains with the mentioned error on this line (it doesn't matter what "whatever" is):
super.init(document: self, gravlNode: whatever)
}
}
Unless I'm missing something, it's very obvious that it is only calling init once. Funnily enough if I comment out the second line Swift complains that Super.init isn't called on all paths, lol.
What am I missing?
Update:
Ok so the problem was definitely trying to pass self in the call to super.init. I totally forgot I was doing that, ha. I think I had written that experimentally and gotten it to compile and thought that it might actually work, but looks like it's actually a bug that it compiled that way at all.
Anyhow, since passing self to an initializer is kind of redundant since it's the same object, I changed the parent initializer to accept an optional document parameter (it's just an internal initializer so no big deal) and if it's nil I just set it to self in the parent initializer.
For those curious, this is what the parent initializer (now) looks like:
internal init(document: Document?, gravlNode: Gravl.Node) {
self.value = gravlNode.value
super.init()
self.document = document ?? self as! Document
// other stuff...
}

I suspect this is a bad diagnostic (i.e the wrong error message). It would be very helpful if you had a full example we could experiment with, but this line doesn't make sense (and I suspect is the underlying problem):
super.init(document: self, gravlNode: node)
You can't pass self to super.init. You're not initialized yet (you're not initialized until you've called super.init). For example, consider the following simplified code:
class S {
init(document: AnyObject) {}
}
class C: S {
public init() {
super.init(document: self)
}
}
This leads to error: 'self' used before super.init call which I believe is the correct error.
EDIT: I believe Hamish has definitely uncovered a compiler bug. You can exploit it this way in Xcode 8.3.1 (haven't tested on 8.3.2 yet):
class S {
var x: Int
init(document: S) {
self.x = document.x
}
}
class C: S {
public init() {
super.init(document: self)
}
}
let c = C() // malloc: *** error for object 0x600000031244: Invalid pointer dequeued from free list

Related

Using Cuckoo with Swift, why is the call to `stub` complaining that the class from GeneratedMocks doesn't conform to Mock protocol?

Using Cuckoo 1.4.1, I get the error
Global function 'stub(_:block:)' requires that 'DeepFileManager' conform to 'Mock'
I don't know how to proceed, because the instance passed to the stub function was created from the class in GeneratedMocks.swift. The reference to DeepFileManager is the class that is mocked.
So it's a mystery to me why Swift 4 would complain about the conformance of the superclass of the instance I've passed to it. Can you lead me out of this conundrum?
Here's the code for the test:
class RecordingTests: QuickSpec {
override func spec() {
let FAKE_ID = "String";
let mockFileManager: MockDeepFileManager = MockDeepFileManager()
let cut = Recording(id: FAKE_ID, date: nil, status: nil, device: nil, deviceId: nil, deviceName: nil, fileManager: mockFileManager)
describe("A recording") {
context("when it's not on disk") {
it("responds that filePresent is false") {
stub(mockFileManager) {stub in
when(stub.fileExists(audioId: FAKE_ID)).then()
}
expect(cut.filePresent()).to(equal(false))
verify(mockFileManager).fileExists(audioId: "Matchable")
}
}
}
}
And here's the declaration of MockDeepFileManager from GeneratedMocks.swift:
class MockDeepFileManager: DeepFileManager, Cuckoo.ClassMock {
As noted in the comment, an issue was submitted against the package, and the response explains how to make it work.
The problem in the code was calling then() (with no arguments), as then requires a closure function with a signature to match the signature of the stubbed function. A simpler alternative that would achieve the aim is thenReturn (instead of then), which just needs a value of the type of the stubbed function's return.
The maintainer of the Cuckoo package expressed sympathy that Swift issues an error about the protocol, instead pointing to the error in the invocation of then.

swift lazy var with throw init behavior

I am not sure if it is a bug or it is really how things should work?
class A {
init() throws { }
}
class B {
lazy var instance = A()
}
this code compiles without mistakes using XCode 9 and latest Swift version, and works perfect unless Class A init() really throws, then lazy var is null pointer. But shouldn't be this code somehow not be compiled?
This is indeed a bug (SR-7862) – you cannot throw errors out of a property initialiser context (and even if you could, you would be required to prefix the call with try), therefore the compiler should produce an error.
I have opened a pull request to fix this (#17022).
Edit: The patch has now been cherry-picked to the 4.2 branch, so it'll be fixed for the release of Swift 4.2 with Xcode 10 (and until the release you can try a 4.2 snapshot).
As an answer to your question:
But shouldn't be this code somehow not be compiled?
Well, at some point your code snippet worked without any issue (because -as you mentioned- the class A init doesn't actually throws), so it could be compiled without any problem. To make it more clear, consider it as a similar case to the following one:
let myString: String? = nil
print(myString!) // crashes!
it will get compiled just fine! although we all know that it crashes when evaluating myString!, i,e we do know it causes a run-time crash, but that doesn't mean that the compiler should prevent it because it could be valid at some point (for instance if we declare it as let myString: String? = "Hello"); Similarly to your case, it could be valid at some point -as mentioned above-.
Usually, for such cases we -as developers- are the responsible to handle it based on what's the desired behavior(s).
Referring to this case, we might need to ask:
"How can we implement the instance lazy variable to catch an error (with a do-catch block)?"
Actually, this code won't compile:
class B {
lazy var instance:A = {
do {
let myA = try A()
return myA
} catch {
print(error)
}
}()
}
complaining that:
Missing return in a closure expected to return 'A'
because obviously reaching the catch block means that there is nothing to be returned. Also, as you mentioned even if you implemented it as
lazy var instance = A()
you will not get a compile-time error, however trying to use it with an actual throwing should leads to run time error:
let myB = B()
print(myB.instance) // crash!
What I would suggest for resolving this issue is to declare instance as lazy optional variable:
class B {
lazy var instance:A? = {
do {
let myA = try A()
return myA
} catch {
print(error)
}
return nil
}()
}
At this point, if we assume that A initializer always throws, trying to access it:
let myB = B()
print(myB.instance)
should log:
caught error
nil
without causing any crash. Otherwise, it should works fine, for instance:
let myB = B()
myB.instance?.doSomething() // works fine

Error call function (instance member cannot be used)

It's not a duplicate cause with lazy i have an issu too :
"A C function pointer cannot be formed from a closure that captures context"
In my main class I have "detectChangeMidi" but in this code, when I try to call a function I don't understand why I can't.
(i can't use var too, anything of my class)
I'm not expert in swift, then try to explain to me what's going one.
I use the CoreMidi librarie.
UPDATE :
I replace the code by minimaliste code for better entendement.
import Foundation
import CoreMIDI
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
gestionMidi()
//...
}
func gestionMidi() {
//...
let midiNotif:MIDINotifyProc = detectChangeMidi
MIDIClientCreate("Swift3 Test Client" as CFString, midiNotif, nil, &midiClient)
//...
}
func plop(){
print("bla bla bla")
}
let detectChangeMidi: #convention(c) (UnsafePointer<MIDINotification>, UnsafeMutableRawPointer?) -> Swift.Void =
{ midiNotification,unkown in
var notification = midiNotification.pointee
self.plop() //problem here
//...
}
}
Your entire agenda here is misguided; it is not at all obvious what you can be trying to do. You cannot declare a property or function as being convention(c) and also refer to self. But you don't need to! If your goal is to pass a function as a parameter where a C pointer-to-function is expected, just pass the function. Anyway you'll have a much easier time in Swift if you call MIDIClientCreateWithBlock(_:_:_:) instead.
The problem is with the scope as I can judge from the error message.
Also the thing to get attention to is that you're executing plop() in closure, and that requires self.plop().
also midiNotification,unkown in - unkown seems to be a typo.
Check where you declare the function itself. From this snippet it's hard to understand what's your declaration scope.

In Swift, can you write an extension which returns a typed instance of the class the extension is on?

This is one of those things that seems simple enough, but doesn't work as you'd expect.
I'm working on a 'fluent/chaining'-style API for my classes to allow you to set properties via functions which can be chained together so you don't have to go crazy with initializers. Plus, it makes it more convenient when working with functions like map, filter and reduce which share the same kind of API.
Consider this RowManager extension...
extension RowManager
{
#discardableResult
public func isVisible(_ isVisible:Bool) -> RowManager
{
self.isVisible = isVisible
return self
}
}
This works exactly as one would expect. But there's a problem here... if you're working with a subclass of RowManager, this downcasts the object back to RowManager, losing all of the subclass-specific details.
"No worries!" I thought. "I'll just use Self and self to handle the type!" so I changed it to this...
extension RowManager
{
#discardableResult
public func isVisible(_ isVisible:Bool) -> Self // Note the capitalization representing the type, not instance
{
self.isVisible = isVisible
return self // Note the lowercase representing the instance, not type
}
}
...but that for some reason won't even compile giving the following error...
Command failed due to signal: Segmentation fault: 11
UPDATE
Doing more research, this seems to be because our code both is in, and also uses, dynamic libraries. Other questions here on SO also talk about that specific error in those cases. Perhaps this is a bug with the compiler because as others have correctly pointed out, this code works fine in a stand-alone test but as soon as the change is made in our code, the segmentation fault shows up.
Remembering something similar with class functions that return an instance of that type, I recalled how you had to use a private generic function to do the actual cast, so I tried to match that pattern with the following...
extension RowManager
{
#discardableResult
public func isVisible(_ isVisible:Bool) -> Self // Note the capitalization
{
self.isVisible = isVisible
return getTypedSelf()
}
}
private func getTypedSelf<T:RowManager>() -> T
{
guard let typedSelfInstance = self as? T
else
{
fatalError() // Technically this should never be reachable.
}
return typedSelfInstance
}
Unfortunately, that didn't work either.
For reference, here's the class-based code I attempted to base that off of (className is another extension that simply returns the string-representation of the name of the class you called it on)...
extension UITableViewCell
{
/// Attempts to dequeue a UITableViewCell from a table, implicitly using the class name as the reuse identifier
/// Returns a strongly-typed optional
class func dequeue(from tableView:UITableView) -> Self?
{
return self.dequeue(from:tableView, withReuseIdentifier:className)
}
/// Attempts to dequeue a UITableViewCell from a table based on the specified reuse identifier
/// Returns a strongly-typed optional
class func dequeue(from tableView:UITableView, withReuseIdentifier reuseIdentifier:String) -> Self?
{
return self.dequeue_Worker(tableView:tableView, reuseIdentifier:reuseIdentifier)
}
// Private implementation
private class func dequeue_Worker<T:UITableViewCell>(tableView:UITableView, reuseIdentifier:String) -> T?
{
return tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) as? T
}
}
At WWDC Apple confirmed this was a Swift compiler issue that something else In our codebase was triggering, adding there should never be a case where you get a Seg11 fault in the compiler under any circumstances, so this question is actually invalid. Closing it now, but I will report back if they ever address it.

Passing generic struct for unnamed default parameter results in garbage properties

I'm seeing some odd behaviour in a class I created a while ago, where it seems that a struct's properties are changing immediately after being passed (copied) to a method.
I've boiled it down to a simple test case that can be run in a playground:
struct StructToPass<T> {
let x: T
}
class MyClass<T> {
func createAndPassStructWithValue(value: T) {
let structToPass = StructToPass(x: value)
println("Before passing to method: \(structToPass.x)")
passStruct(structToPass)
}
func passStruct(_ theStruct: StructToPass<T>? = nil) {
println("Inside method: \(theStruct!.x)")
}
}
let myClass = MyClass<Int>()
myClass.createAndPassStructWithValue(42)
Looking at the relevant printed statements, it shows that the struct's x property has changed:
// Before passing to method: 42
// Inside method: 140734543799888
Creating the struct outside the class and calling passStruct(_:) directly causes the playground to crash, as does writing passStruct(_:) as a function:
// Causes playground to crash:
let aStruct = StructToPass(x: 42)
myClass.passStruct(aStruct)
// Also causes playground to crash:
func passStruct<T>(_ theStruct: StructToPass<T>? = nil) {}
passStruct(aStruct)
Changing the passStruct(_:) method/function to use the default external parameter name fixes the issue, as does introducing another parameter (before/after the default parameter):
// This works:
func passStruct<T>(theStruct: StructToPass<T>? = nil) {
println("Inside function: \(theStruct!.x)")
}
passStruct(theStruct: aStruct)
// This also works:
func passStruct<T>(_ theStruct: StructToPass<T>? = nil, someOtherParam: Int) {
println("Inside function: \(theStruct!.x)")
}
passStruct(aStruct, 42)
Is this a compiler bug? It seems the compiler doesn't like it when a generic function/method with a single argument with a default value doesn't use an external parameter name. It's a specific case, but I think it ought to work. If it shouldn't work, there ought to be a compiler warning.
110% compiler bug. I've even tried this out of Playground. It all happily compiles until you want to add a line which actually does something, like sending a passStruct. There's all kinds of things wrong with this. I even had this fail:
func passStruct<T>(_ theStruct: StructToPass<T>? = (nil as StructToPass<T>?)) {
println("Inside function: \(theStruct!.x)")
}
which I kinda thought might be the problem (even though it shouldn't be I've had that elsewhere).
Well found! Report it. They're clearly not finished with generics. In my experiments I found that generic class properties aren't allowed.
static let nilStruct: StructToPass<T>? = nil
does not compile, with one of the "not yet supported" error messages.