1

I'm looking at the example from the “Unowned References and Implicitly Unwrapped Optional Properties” section of the book “The Swift Programming Language.”

Their example code is

class Country { let name: String let capitalCity: City! init(name: String, capitalName: String) { self.name = name self.capitalCity = City(name: capitalName, country: self) } } class City { let name: String unowned let country: Country init(name: String, country: Country) { self.name = name self.country = country } } 

This works if I want to deal exclusively with Countries and the only purpose of the City type is to be a capital of a Country. But what happens if I want to create a City?

This creates a runtime exception because no reference to the City's Country is retained since it is an unowned variable:

var chicago = City(name:"Chicago", country: Country(name: "USA", capitalName: "Washington DC")) chicago.country.name // Playground execution failed: error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=EXC_I386_GPFLT). 

How would I allow something like this without creating a Strong Reference Cycle?

1
  • at the end of the day, you still have to deal with retain cycles using all the same strategies as you did in objective-c. Swift just makes it easier to express Commented Jun 5, 2014 at 14:33

2 Answers 2

4

There are two typical solutions:

  • If you want to primarily deal with cities, invert the relationship so that City has a strong reference to Country, and Country points back to an unowned instance.

  • If you want to have cities and countries as primary objects that cross reference each other, put all cities and countries into collections (or other form of store that owns them), and make both references weak. That way they don't own each other, and you don't have a cycle.

The best way to avoid retain cycles is to consider who owns every object. Objects can own each other, but that should be a clear hierarchy (i.e. a tree). If you have connections that go sidewards and and upwards in the hierarchy, make them weak or unowned.

Solution one is the upwards case, solution two is sidewards case.

Edit

  • A third option is, to have Country own a collection of all its cities. I think that makes most sense in this simple case, but it means the Country needs to create all cities in it's initialization, or have a method that adds cities.

Here's an example for the second case. It's quite complex, probably too much so for this simple case, but it illustrates extracting a common owner. I would normally use it if there are a lot of cross references. (Think relational database. The records don't own each other.)

 class Country { let name: String weak var capitalCity: City? init(name: String) { self.name = name } } class City { let name: String unowned let country: Country init(name: String, country: Country, isCapital: Bool) { self.name = name self.country = country if isCapital { country.capitalCity = self } } } class Planet { var countries: [Country] = [] var cities: [City] = [] } let earth = Planet() earth.countries = [ Country(name: "USA"), Country(name: "Canada"), ] earth.cities = [ City(name: "Washington DC", country: earth.countries[0], isCapital: true), City(name: "Chicago", country: earth.countries[0], isCapital: false), City(name: "Ottawa", country: earth.countries[1], isCapital: true), ] 
Sign up to request clarification or add additional context in comments.

1 Comment

Do you have a code example for the second case? Is this similar to @Sulthan's example below except storing them together rather than in separate variables?
0

In your example, nobody is owning the Country instance. That means it gets deallocated (freed) immediately.

var country = Country(name: "USA", capitalName: "Washington DC") var chicago = City(name:"Chicago", country: country) chicago.country.name 

will fix it because our coutry variable will keep USA from deallocating

If you use an unowned reference, you always have to keep a strong reference somewhere else.

4 Comments

But this won't work if he wants to create a capitol city using init function of the Country class. It will create a retain cycle.
@68cherries It won't. The city never retains the country so there can't be any retain cycle.
So the idea would be that your two variables would go out of scope at the same time basically and therefore both be deallocated at the same time?
@BradDwyer Yes. Once you deallocate country, you should also let all its cities be deallocated. The normal approach is that the country actually owns all the cities so you don't have to keep references to cities and you can keep only a country reference.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.