Creating object graph in swift, Core Data or vanilla objects?

Hi,

I also posted this question on cocoa-dev. It’s mainly about swift and core data, so I thought it might be a good idea to post on swift forums also. I apologize if this is not appropriate.

Here is the post:

I'm using Core Data as my model, but I don't want to actually persist anything. Anytime a view controller is shown, I load json data from my server and populate the store.

The problem is that objects in Core Data persist between view controllers, even with in-memory store type. But when I load json data, I’d like to have a fresh start. So it seems I need to remove existing objects first.

You might say I shouldn't use Core Data in this case, just use vanilla objects. But Core Data solves lots of problems, like no need to worry about memory leak for circular dependencies (my model is pretty complex, lots of many-to-many relationships etc), NSFetchedResultsController, etc. And with vanilla objects, when you have a many-to-many relationship, it’s seems it’s impossible to prevent memory leak:

class Club {
var people: [Person]
}
class Person {
var name: String
var clubs: [Club]
}

Array in swift always create strong reference if I’m not wrong, and the two arrays can create circular references.

If I go with Core Data, to easily delete objects and have a fresh start, one solution involves carefully setting up delete rule between object models and then deleting ones at the level I want, and hope it could cascade throughout, but the problem is that this approach is very error prone. If I misconfigure some delete rules, there won’t be any errors, still leaving some objects in the store.

I wonder what’s the best way to create object graph in swift without the risk of leaking memory, if Core Data is the way to go, is there any better way to easily detect objects that I forgot to delete? Or is there a better way that I can guarantee that the store is clean before importing json data?

use weak when you define instances of either Club or Person to avoid reference cycles.

I wonder where should I put that weak? I tried declare person.clubs as weak, but the compiler complained with 'weak' may only be applied to class and class-bound protocol types, not '[Club]', it seems you can't create weak array this way.

One workaround to get an array of weak references is to use a Weak wrapper.

class Weak<Value: AnyObject> {
	weak var value: Value?
}

Then you can use [Weak<Club>]. It's clunky to use, though, and you might have to worry about purging deallocated values.

I don't have a lot of experience with Core Data, so I can't speak to that approach. But for a pure Swift solution to your example, I'd drop the list of clubs from Person and then have whatever owns the clubs calculate a person's clubs on the fly: clubs.filter { $0.people.contains(person) }.

But if that doesn't make sense for your actual model (i.e. if there are other logical parents of a Person, like a Home), then it sounds to me like you actually would want something database-like.

Because Array is a struct, structs are value types, not reference types, how could a reference cycle possibly happen to a value type? Reference cycles can happen between Person and Club objects, therefore they must be weak. To be precise, either clubs or persons. Creating a reference cycle without defining both a Club and a Person isn't possible, so you will need to somehow have one of the arrays hold weak references. One of the options, like @jarod suggested, is to create a wrapper and hold an array of them:

class Weak<T: AnyObject> {
    weak var value: T?
}

class Club {
    var people: [Person] = []

    deinit { print("club deinit")}
}
class Person {
    var name: String = String()
    var clubs: [Weak<Club>] = []

    deinit { print("person deinit")}
}

var weakClub = Weak<Club>.init()

var club: Club? = Club()
var person: Person? = Person()

// The reference cycle will be a triangle; club, person and weakClub are the vertices.
person?.clubs = [weakClub]
club?.people = [person!] // with these two lines I create the first 2 vertices.

weakClub.value = club // here I complete the reference cycle with a weak vertex.

club = nil
person = nil

// club and person both deallocate together with the weak wrapper.