Is it reliable to use an unowned(unsafe) reference while the object in question is deinitializing? - swift

Update
To clarify, the access of the object during its deinitialization is not being done in its deinit method explicitly. The object in question has listers that get added to it (closures) and these closures are all executed within the deinit method. It is within these closures that accesses of the object is being performed with unowned references. And it is the replacement of those unowned references with unowned(unsafe) references that results in EXC_BAD_ACCESS' from no longer occuring.
It is these unowned(unsafe) references that I'm referring to when asking if they're safe to use if always executed during the object in question's deinit.
Original
I wrote a lot of code predicated on being able to clean up unowned references in the deinitializers of their unowned object. Lo and behold, that is not a feature of unowned references. But apparently it is of unowned(unsafe) references, at least that is the way it appears to be working right now — what once caused a crash accessing an unowned reference during its object's deinitialization, now is no longer crashing and is working as expected.
If guaranteed that all unowned references will not be accessed after deinitialization of their object, would it be safe to use it?
For more details, the aforementioned cleaning up entails removing the object from a set where the hashability is based off its contents' object identities. So if it's a plain unowned reference, when the set attempts to access its hash, it will crash if that procedure is being performed while the object is already deinitializing.
The reason the objects aren't removed from the set before they are deinitialized is because this code is a component of library that enables the addition of nodes to a directed acyclic graph. As a feature, I decided that I would not require consumers of the library to have to remove the nodes when they're done with them, they can simply add them to the graph, then when they're done, release their object (the node) as they would anyways, and because the library adds listeners onto the nodes to remove them from the graph in their deinitializers, it was anticipated that it wouldn't be a problem — that the graph would be able to be cleaned up transparently. Obviously it's a little more complicated now that it's apparent that unowned(safe) references can't be accessed while the object they're referencing is deinitializing.
If unowned(unsafe) works in the way it appears to, it would be a solution to this problem.

The only difference between unowned(safe) and unowend(unsafe) is that the save variant is implemented using proxy objects and it will reliably crash your app when you access it illegally.
The unsafe variant on the other hand is just a plain C-Style pointer which will sometimes "just work" (if by coincidence the memory has not been reused anyway) and sometimes will strangely crash or just report unpredicable results.
unowned is the same as unowned(safe)
Nevertheless, during deinit you may access all the propertys of your object, see The Documentation
And also:

I am not sure exactly what you have implemented but it looks like you are trying to duplicate the mechanism with tables Swift uses internally for keeping track of deallocations of weak references.
If guaranteed that all unowned references will not be accessed after
deinitialization of their object, would it be safe to use it?
Yes it would be safe. If you have this guarantee I think it would also be simpler to turn all your variables to implicitly unwrapped weak variables.
So if it's a plain unowned reference, when the set attempts to access
its hash, it will crash if that procedure is being performed while the
object is already deinitializing.
Obviously it's a little more complicated now that it's apparent that
unowned(safe) references can't be accessed while the object they're
referencing is deinitializing.
I do not think this is the reason for the crash, the memory is freed after deinitialization, during deinitialization you still have access to the instance to perform any manual cleanup you need, I would suggest to replace the complicated solution that keeps track of deallocated references, and simply rely on Swift to set to nil objects that are deallocated using weak references. If you do not want to refactor you code to handle optionals when make them explicitly unwrapped.
However if during deinitialization you access the object from an other reference(outside deinit) it will fail, this is to ensure consistency. See here that access an instance that is deinitialized will cause an app to crash.

Related

Weak Self in GCD, accessing TableView property of self

I see a lot of code like this:
DispatchQueue.main.async {
self.tableView.reloadData()
}
In this context, where tableView is a property of self, should one instead ensure weak self is captured like this:
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
I think so, but this type of "unsafe" code is so ubiquitous across codebases I wonder if I'm missing something?
You said:
In this context, where tableView is a property of self, should one instead ensure weak self is captured like this:
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
No, you don’t actually need to use weak reference to self in this case. There is no persistent strong reference cycle here. The worst that would happen (in the absence of weak reference) is that it will keep a strong reference to self until the dispatched block finishes running. But that closure will generally run practically immediately and the fleeting strong reference will be eliminated very quickly.
So, as such, no, you don’t have to use weak reference here. It is not technically incorrect to do so, but adds a syntactic noise for practically no benefit. Yes, we should be diligent about breaking potential strong reference cycles with weak references, but this is not one of those cases.
By the way, the choice of the term “unsafe” muddles the topic a bit. “Unsafe” has a very specific meaning when talking about memory references: It generally refers to dangling references to deallocated objects when not using runtime safety checks. As The Swift Programming Language: Automatic Reference Counting says (emphasis from original):
Swift also provides unsafe unowned references for cases where you need to disable runtime safety checks—for example, for performance reasons. As with all unsafe operations, you take on the responsibility for checking that code for safety.
You indicate an unsafe unowned reference by writing unowned(unsafe). If you try to access an unsafe unowned reference after the instance that it refers to is deallocated, your program will try to access the memory location where the instance used to be, which is an unsafe operation.
Obviously, both strong and weak references are safe (the former keeps the object from being released, the latter sets its references to nil when the object is deallocated). Even unowned references often employ runtime safety checks (though it still results in runtime error if you use a unowned reference after the object is deallocated). An “unsafe” reference is used in high-performance scenarios, where you are absolutely confident in your unowned references are being handled properly and that you’re willing to run your code without runtime safety checks.
I suspect you didn’t mean “unsafe” in this context, but I mention it so that you are aware of the very specific meaning that this word has within the context of ARC and memory management.

ARC doesn't apply to struct and enum, how are they deallocated in Swift

Since ARC doesn't apply to struct and enum, then how are they deallocated from the memory? I have to get stuck when it asked in the interviews and try to find the correct answer but can't find much info on it googling. I know swift is smart at handling value types. But how?
The memory management of objects (instances of classes) is relatively difficult, because objects can outlive a function call, the life of other objects, or even the life of the threads that allocated them. They're independent entities on the heap, that need book keeping to make sure they're freed once they're not needed (once they're no longer referenced from any other threads/objects, they're unreachable, thus can't possible be needed, so are safe to delete).
On the other hand, structs and enums just have their instances stored inline:
If they're declared as a global variable, they're stored in the program text.
If they're declared as a local variable, they're allocated on the stack (or in registers, but never mind that).
If they're allocated as a property of another object, they're just
stored directly inline within that object.
They're only ever deleted
by virtue of their containing context being deallocated, such as when
a function returns, or when an object is deallocated.

Is reset member function for weak pointers atomic?

If one creates a shared pointer to an object using std::make_shared, and use a weak pointer to it as an observer. When the reference count of the shared pointer hits zero, the object is not deallocated because the weak pointer keeps it alive. (If I am not mistaken here.) Suppose that after a call of member function lock() on that weak pointer, and it turns out that it has expired. Now the programmer wants to call reset() to trigger destruction of the object, because the object is quite large.
The question is: is reset an atomic operation? If the answer is NO, my next question is that why the standard doesn't requires it being atomic.
The object is only deallocated after each weak_ptr that references the object is reset.
You don't modify single weak_ptr from multiple threads, so reset of a single weak_ptr don't need to be atomic.
C++20 introduces a helper class std::atomic, that guarantees, quote
The partial template specialization of std::atomic for std::weak_ptr allows users to manipulate weak_ptr objects atomically.
If multiple threads of execution access the same std::weak_ptr object
without synchronization and any of those accesses uses a non-const
member function of weak_ptr then a data race will occur unless all
such access is performed through an instance of
std::atomic>.
If one is not using C++20, check this SO answer by Chris Jester-Young for workaround.

Swift: How to check if an object is being released

I'm developing a small weak datastructures framework.
A collection of weakely wrapped object has a remove() method.
Inside the method i will delete an object if present and eventually purge the wrappers containing nil references.
The problem raise in the case the weakCollection.remove(object) is called inside a object.deinit() (may happen indirectly).
In this case (since i need to make a copy for comparison reasons) i will have a SIGABORT due to trying to reference an object that is being deallocated and this is forbidden in swift.
Cannot form weak reference to instance (0x608000199710) of class
XXX. It is possible that this object was
over-released, or is in the process of deallocation.
Normally i'd just document it, throw an error or a warning and skip. But i'd like to be safe about this and only remove an object when is not in "releasing" state.
One way would be reading the referenceCount, but in swift is not a good idea. Most other reflection/meta techniques i can think of are too expensive.
Plus: Another thing i'd really appreciate to know is if there is any notification/kno/observer i can connect to in order to be notified when object is being released.

For what variables should you use weak var in Swift? How do I figure out the leaks?

I am building an app in Swift. I am creating my views in an entirely a programmatic way. In some instances, I have a ViewController that instantiates custom views. In addition, I may have variables like "var User" which gets populated after an Alamofire network call and is used in various UI elements throughout the view controller. Besides declaring delegates as weak var, are there any other rules that apply?
Also, is there a way for me to figure out whether I have a strong reference that should be a weak one? What should I be looking for?
The basic concept behind reference counting in Swift is one of ownership. Objects should hold strong references to any other objects that they "own", in the sense that they're responsible for the lifecycle of the other object, either alone or in conjunction with other objects.
A lot of object reference graphs in a typical application are hierarchical - one object owns a bunch of other objects, which each have their own children, etc. For example, a ViewController owns its window, the window owns its views, each view owns its subviews, and each subview owns the images, strings, or other content it displays. These are all strong references.
Weak references will typically be used for references that don't imply ownership. The delegate example is a good one - in most cases, a view does not own the delegate. The delegate object has a lifecycle independent of the view. In many cases, the delegate will be the same object that created/owns the view in the first place, for example a ViewController.
You do not want a strong reference that goes from a "child" to its "parent". That creates a circular reference, and both the child and the parent will hang around in memory until the application exits.
In addition to delegates and other "backwards-pointing" references, you will also see weak references used in caches, where you want to quickly return an object if it's requested a second time, but the cache shouldn't keep the object in memory if nobody's currently using it.
To properly answer your question, we would need considerably more detail, (please ...) added to your original question.
I would frankly caution you that "there are no rules" with regards to any issue as fundamentally "touchy" as weak references. Be especially careful not to "follow rules," imagining that thereby you will "be 'safe,'" when other aspects of your application's design do not clearly call for their use.
A "weak" reference is defined as a reference from one thing to another which, you assert, "is not sufficient to cause the referenced object to not be garbage-collected." If the memory-manager does decide to "reap" the object, it is supposed to set your "weak" references to NULL. It can do this at any time will do this at the most-inconvenient time.
One possibility worth contemplating in your application design is to use properties, backed by "getter" routines, instead of actual variables. Or, instead of storing a (weak ...) reference to something, put it into some sort of a "collection" and store its id. Yes, various forms of "getter routines" will be executed each-and-every time, but in the long run that might be more reliable than relying too-much on the memory manager. If you know that "all of the code, wherever situated," will have to pass through "this 'getter' routine," you can concentrate your bug-avoidance efforts at that one deliberate pinch-point.