Setup
Suppose I have two classes:
final class Parent: NSObject
{
var child: Child
}
final class Child: NSObject
{
weak var parent: Parent?
init(parent: Parent)
{
self.parent = parent
}
}
Question:
Now suppose that I want to instantiate a child and establish this relationship in the init() method of Parent. I would do this:
final class Parent: NSObject
{
var child: Child
init()
{
child = Child.init(parent: self) // ERROR!
}
}
Swift whines that I'm using self before super.init(). If I place super.init() before I instantiate child, Swift whines that child isn't assigned a value at the super.init() call.
I've been shutting Swift up by using implicitly-unwrapped optionals, like this:
final class Parent: NSObject
{
var child: Child!
init()
{
super.init()
child.init(parent: self)
}
}
My question is: What do people do in this situation? This is/was a VERY common pattern in Objective-C and Swift produces nothing but headaches. I understand that I could de-couple the assignment of parent from Child's init() so that I could init() child, call super.init(), and then assign parent, but that is not an option in the real-world cases where I run into this. It's also ugly and bloated.
I do realize Swift's intent is to prevent use of an object before it is fully initialized. And that it's possible for child's init() to call back to parent and access state that isn't set up yet, which becomes a danger with the implicitly-unwrapped optional approach.
I cannot find any guidance about Swift best practice here, so I'm asking how people resolve this chicken-and-egg issue. Is turning properties into implicitly unwrapped optionals really the best way around this limitation? Thanks.
You can make it lazy and all your headaches will gone!
final class Parent {
lazy var child = Child(parent: self)
}
final class Child {
weak var parent: Parent?
init(parent: Parent) {
self.parent = parent
}
}
In Swift, all stored properties should be initialized before self is available. Including stored properties in the superclass. So Xcode doesn't let you use self till then. But using lazy means that it is initialized before. So you can use self freely.
Some helpful notes:
Don't inherit from NSObject if you don't need objc advanced features for your class.
In swift naming convention, open parentheses ( will read as with in objective-C. So you don't need withParent as a label in your initializer.
Use internal and external naming of arguments as the convention suggests.
Also note that some of the above notes are written before comments
Related
I have generic Parent and Child classes that should both be able to reference each other:
class Parent<MyChild: Child<Parent<MyChild>>> {
var children = [MyChild]()
}
class Child<MyParent: Parent<Child<MyParent>>> {
var parent: MyParent?
}
I want to be able to create subclasses of Parent and Child that have a reference to each other. The generics should allow each class to use methods of the other custom class as well as those inherited by the base Parent and Child classes, which may refer to the custom generics. EDIT: I need not only to reference methods, but instance variables as well.
This may be comparable to an array (the parent) having a reference to the children and each child having a reference to the array it belongs to. The array would be modelled with a generic Child type, and the child should then be modelled with a generic Parent type.
This code produces the compiler error Generic class 'Parent' references itself. I think this is possible in Java. Is there a way of expressing such a generic relationship in Swift?
You can use protocols to define the relationship between these types and the dependencies between them (with an associated type with generic where clause):
protocol Parent: AnyObject {
associatedtype C: Child where C.P == Self
var children: [C] { get set }
}
protocol Child: AnyObject {
associatedtype P: Parent where P.C == Self
var parent: P? { get }
}
(As an aside, I made these AnyObject because this doubly-linked pattern requires that you are dealing with reference types. Also, I made parent property optional because it will need to be weak to avoid strong reference cycles.)
Then the actual parent and child classes can use these protocols:
class Teacher: Parent {
var name: String
var socialSecurityNumber: String
var children: [Student] = []
}
class Student: Child {
var name: String
var admissionDate: Date
weak var parent: Teacher?
...
}
Again, note that parent is a weak property to avoid strong reference cycle.
Note, when using protocols, rather than having abstract (i.e., pure virtual) Child and Parent classes, we have protocol, and our concrete classes would conform to those protocol. And if we wanted to define methods that all of these conforming classes would share, we would define default implementations in a protocol extension. See WWDC 2015 video on Protocol Oriented Programming.
I am writing my project and wondered.
When I read literature or watch videos, I see that this is bad practice. Why? Is this bad for the system?
What is the difference between this
class SomeClass {
var someView = SomeView()
var someViewModel = SomeViewModel()
// ...
}
and this
class SomeClass {
var someView: SomeView!
var someViewModel: SomeViewModel?
// ...
}
How to get used to it better?
You have to initialize all instance properties somehow. And you have to do it right up front, either in the declaration line or in your init method.
But what if you don't actually have the initial value until later, like in viewDidLoad? Then it is silly to supply a real heavyweight value only to replace it later:
var v = MyView()
override func viewDidLoad() {
self.v = // get _real_ MyView and assign it in place of that
}
Instead, we use an Optional to mark the fact that we have no value yet; until we obtain and assign one, it will be nil:
var v : MyView? // means it is initially `nil`
override func viewDidLoad() {
self.v = // get _real_ MyView and assign it to our property
}
There's nothing wrong with the first way (which is called a "default property value", by the way), and in fact, often times it's preferable. But of course, the devil is in the details:
How would the initialization of a SomeViewModel work? Without acess the initializer parameters of SomeClass, you're stuck with only being able to construct an instance from a parameter-less init, like SomeViewModel(). What exactly could that do? Suppose it was a person view model, and you had PersonViewModel(). What person? Whats their name? What will this default value do at all?
It's not a great pattern if it requires overwriting the default value with some other value in the initializer
It initializes the value up-front, where sometimes a lazy or computed value might be more appropriate.
On iOS 13 a NSManagedObject's objectWillChange will emit when its property changes, but will not notify another NSManagedObject in the relationship.
My temporary workaround is the following:
class Child: NSManagedObject {
#NSManaged var parent: Parent?
override func willSave() {
super.willSave()
if isUpdated {
parent?.objectWillChange.send()
}
}
}
This works. But in my case the model is more complex:
class Child: NSManagedObject {
#NSManaged var parent: Parent?
override func willSave() {
super.willSave()
if isUpdated {
parent?.objectWillChange.send()
// NOTE
// how to let grandParents objectWillChange also omit?
}
}
}
class Parent: NSManagedObject {
#NSManaged var grandParents: Set<GrandParent>
override func willSave() {
super.willSave()
// call grandParents forEach objectWillChange.send() if isUpdated
}
}
At NOTE, I can iterate all grandParent of parent and send notification manually. But I believe there is a better, general solution can make that when an object is dirty, the parent will be marked dirty too. Then all object in the relationship will publish objectWillChange automatically. Is it possible?
objectWillChange is for SwiftUI, to do this properly you need to learn KVO:
Registering Dependent Keys
There are many situations in which the value of one property depends on that of one or more other attributes in another object. If the value of one attribute changes, then the value of the derived property should also be flagged for change. How you ensure that key-value observing notifications are posted for these dependent properties depends on the cardinality of the relationship.
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVODependentKeys.html#//apple_ref/doc/uid/20002179-BAJEAIEE
Can someone please help me to avoid memory leak below
class Base {
var refer: Referenced?
init() {
self.refer = Referenced()
}
deinit {
print("deinit called")
}
}
class Referenced {
var native: Native
init(){
native = Native.init(baseRef: Base())
}
}
struct Native {
var baseRef: Base
}
func testMe () {
var base:Base? = Base()
base?.refer = nil
base = nil
}
testMe()
Looks like there is a cyclic reference in here. Base -> Referenced and Referenced -> Native -> Base
But I am unsure how to break this cycle .
There are two issues here:
As others have pointed out, in general, when you have two or more reference types referring to each other, you need to make one of those references weak to break the strong reference cycle. See Resolving Strong Reference Cycles Between Class Instances.
The more serious problem here is that you have infinite loop. The init of Base is instantiating a new Referenced instance, but the init of Referenced is creating another Base instance (which it’s passing to the Native instance). That new Base instance will then create another Referenced instance, repeating the process, and it will continue doing this until it runs out of memory/stack.
Add print statements inside your various init methods, and you’ll see this infinite loop in action.
You should revisit this model, identify an “ownership” graph. Parent objects can keep strong references to child objects (and possibly instantiate child objects, if appropriate), but child objects should only keep weak references back to their parent objects (and most likely not instantiate new parent objects).
For example, you might do:
class Parent {
var child: Child?
init() {
self.child = Child(parent: self)
}
deinit {
print("deinit called")
}
}
class Child {
weak var parent: Parent?
init(parent: Parent) {
self.parent = parent
}
}
And
func testMe() {
var parent: Parent? = Parent()
parent = nil // in this case, this is unnecessary because `parent` is a local variable, but this is here for illustrative purposes; but note that we don’t have to `nil` the `child`
}
Here, you instantiate a Parent object, which happens to instantiate a child. But note that:
The Parent supplies itself as a parameter to the Child;
The Child only maintains a weak reference back to the Parent; and
The Child obviously does not instantiate a new Parent, but rather just uses the reference that was supplied to it.
You can do a rendition of this with your Native struct, too, but the idea will be the same: Break the strong reference cycle with a weak reference and avoid the infinite loop.
Frankly, in these cases, abstract type names make examples unnecessarily confusing. If you clarify what these various types actual represent with a practical, real-world example, the ownership model will undoubtedly become more self-evident.
Change one of the reference as weak like this :
class Base {
weak var refer: Referenced?
init() {
self.refer = Referenced()
}
deinit {
print("deinit called")
}
}
Hope this helps.
You need to mark either Base.refer or Native.baseRef as weak to break the cycle, i.e. change var to weak var.
First of all, at the code snippet you passed, you have a problem with infinite circular dependency like this:
Base -> Referenced -> Native -> Base...
An instance of Native struct is not referencing the Base instance at the beginning of this chain, it is always creating a new one and this behavior is infinite. Just copy your code to the playground and try to run it. You will receive an "Execution was interrupted" error.
So, your code won't even run. As I suppose, what you are trying to achieve is for a Native struct to hold the reference to the first Base instance. Here is the right solution where we pass the reference from Base to Referenced and to Native that will hold the reference:
class Base {
var refer: Referenced?
init() {
self.refer = Referenced(base: self)
}
deinit {
print("deinit called")
}
}
class Referenced {
var native: Native
init(base: Base){
native = Native.init(baseRef: base)
}
}
struct Native {
var baseRef: Base
}
And now, as Native is referencing to the Base parent, you will struggle with the memory leak you mention in the original question. And this time, to prevent the memory leak you have to make your baseRef var weak like this:
weak var baseRef: Base?
I am confused when you have a child class that inherit from a parent class.
First question is why use super.init? I understand override init so it can override the values that was previously set from the parent but I don't understand the use of super.init..
Second question is why does init have parameters?
EDIT: Also why sometimes, the parent class also have a init??
class car {
var speed = 5
var model: String?
var age: Int?
}
class bmw: car {
override init() {
super.init()
model = "cat"
}
}
In your example, there is no good reason for calling super. But in general any class may have properties to initialize and other initial tasks to perform, and the rule, which says that a designated initializer of a subclass must call a superclass designated initializer, guarantees that this will happen coherently both for the subclass and for the superclass.