Search code examples
classswiftstrong-references

Swift class: reference cycle


When I run the below program, it produces segmentation fault. Can you please help me figure out why? Thanks

class Animal:NSObject{
    var  name:String!
    var age:UInt!

    weak var spouse:Animal?
    init(name:String,age:UInt){
        self.name=name
        self.age=age
    }

    func description() ->String{ //to become printable
        return "name= \(name) and age=\(age) spouse=\(spouse)"
    }
}


let dog=Animal(name:"Lucky",age:3)
let cat=Animal(name:"Branson",age:4)
dog.spouse=cat
cat.spouse=dog //It doesnt crash if I comment this line out
println(dog)

Solution

  • The problem is infinite recursion in your printing. Once you set up the full cycle, to print an animal, you print its spouse, which prints its spouse, which print’s its spouse etc. forever until you run out of stack space and crash.

    You need to break that by printing out an animal’s spouse without calling the full print of that animal, something like this:

    class Animal: NSObject {
        // you should avoid using implicitly unwrapped optionals
        // unless you absolutely have to for a specific reason that
        // doesn’t appear to apply here (so remove the !s)
        var name: String
        var age: UInt
        weak var spouse: Animal?
    
        init(name: String, age: UInt) {
            self.name = name
            self.age = age
        }
    }
    
    // to make something printable, you need to conform
    // to the Printable protocol
    extension Animal: Printable {
        // And make description is a var rather than a function
        override var description: String {
            let spousal_status = spouse?.name ?? "None"
            return "name=\(name) and age=\(age), spouse=\(spousal_status)"
        }
    }
    
    
    let dog = Animal(name: "Lucky", age: 3)
    let cat = Animal(name: "Branson", age: 4)
    dog.spouse = cat
    dog.description
    cat.spouse = dog
    println(dog)  // Prints name=Lucky and age=3, spouse=Branson
    

    Note, you have to implement Printable fully with the protocol and var to avoid this problem, otherwise you’ll get the default implementation, which will still experience the issue.

    btw, Swift style convention is to put spaces between things like =, ->, before { etc. (and in fact you can occasionally cause compilation problems if you don’t). Jury’s still out on a: b vs a:b though I find the latter a bit harder to read.