How can I deallocate all references elements from an array? - swift

I'm trying to create a retrying mechanism for our network calls. I have created 2 classes. One a retry Class and another a Manager in case I wanted I can cancel all classes.
class Retry {
var url: String
var maxRetries: Int
init (url: String, retryCount: Int){
self.url = url
self.maxRetries = maxRetries
poll()
RetryManager.shared.add(self)
}
private func poll(){
guard retryCount == 0 else{
print("error")
}
getRequest()
}
private func getRequest(){
// make network request
// if no response is received poll again
}
func cancel(){
maxRetries = 0
}
}
Manager class
class RetryManager{
static let sharedInstance = RetryManager()
var retries : [Retry?] = []
private init(){
}
func register(retry: Retry){
retries.append(retry)
}
func remove(retry: Retry){
retry.cancel() // XX; Do I also need this or just removing it is fine?
retries = retries.filter({$0 !== retry})
}
func cancelAll(){
retries.forEach({$0?.cancel()}) // ZZ; Do I also need this? or just removing them is fine?
retries.removeAll()
}
}
My Retry instances are to be used for making network calls.
My major question is about my cancel mechanism. Will doing a RetryManager.shared.cancelAll() suffice for deallocation? Or I need to run cancel or each cancel instance (ie XX, ZZ are also necessary)?
Currently everything works fine, but I'm not sure if I how it would work if I have multiple pointers...would I need to do:
for index..<retries.count{
retries[index] = nil
}
As far as I understand that won't help, it's same as doing retries.removeAll()
I also read In Swift, how do I set every element inside an array to nil? question but was told to open a new question

Not sure if I can answer your question, but I will try with my best understanding :).
Apple's Swift handbook on Automatic Reference Counting (ARC) covers your question very well.
Usually you don't need to have an array of optionals,
var retries = [Retry]()
...
retries.removeAll()
will nicely remove all containing objects and delete the references to these objects. From your context presented above I do not understand why you need to declare an array of optionals. As you know Swift optionals under the hood are just a typed wrapper class Optional<Type>, which doesn't work around the memory allocation problem.
How does array reference objects?
The array will increase the contained objects' reference count by one, that is, a strong reference.
To ensure the objects in the array to be deallocated, one must make their reference count equal zero. Removing them from the array will do the trick, if nothing else is referencing the contained objects.
Beware of the reference cycle though. In your case, you should not hold reference to the retries array in Retry instance. Otherwise even if you set the retries array to nil, the array and its contained objects still have strong reference to each other, meaning their reference counts will never reduce to zero, causing memory leak.

Related

Is it safe to move a mutex in Swift?

I was doing some research on mutexes and came across the following Swift code:
class Lock {
private var mutex: pthread_mutex_t = {
var mutex = pthread_mutex_t()
pthread_mutex_init(&mutex, nil)
return mutex
}()
func someFunc() {
pthread_mutex_lock(&mutex)
defer { pthread_mutex_unlock(&mutex) }
...
}
}
The code defines and initializes a pthread_mutex_t within the closure, then assigns the returned value to a class property. It then lock and unlocks within several of the functions as shown.
Since one should also call pthread_mutex_destroy, it implies that some sort of allocation is occurring within the mutex which may or may not reference the address of the original value.
In effect, the mutex is initialized in one place and stored in another.
The question is whether or not it's safe or correct to do this?
What if the mutex initializer needed arguments?
private var mutex: pthread_mutex_t = {
var recursiveMutex = pthread_mutex_t()
var recursiveMutexAttr = pthread_mutexattr_t()
pthread_mutexattr_init(&recursiveMutexAttr)
pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE)
pthread_mutex_init(&recursiveMutex, &recursiveMutexAttr)
return recursiveMutex
}()
The later strikes me as definitely being incorrect, as the attribute storage whose address is passed into the mutex will disappear when the closure collapses.
It's not, this code is broken.
To work, the pthread_mutex_t would need to be initialized in-place within a class instance, and never copied out. The class would need to expose lock/unlock methods which operator on the instance-variable in-place.
Value Types
Note that pthread_mutex_t, pthread_rwlock_t, and os_unfair_lock are value types, not reference types. That means that if you use = on them, you make a copy. This is important, because these types can't be copied! If you copy one of the pthread types, the copy will be unusable and may crash when you try to use it.
– By Mike Ash, Friday Q&A 2017-10-27: Locks, Thread Safety, and Swift: 2017 Edition
Check out https://cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html
And this example usage of pthread_mutex_t in Swift: https://github.com/mattgallagher/CwlUtils/blob/0bfc4587d01cfc796b6c7e118fc631333dd8ab33/Sources/CwlUtils/CwlMutex.swift#L60-L105

Does Swift know not to initialize an object if my set already contains it?

I have a global var notes: Set<Note> that contains notes initialized with downloaded data.
In the code below, does Swift know to skip the initialization of my Note object if notes already contains it?
for dictionary in downloadedNoteDictionaries {
let note = Note(dictionary: dictionary)
notes.insert(note)
}
I'm wondering because my app downloads dozens of notes per request and initializing a Note object seems rather computationally expensive.
If the answer to my question is no, then how could I improve my code's performance?
My Note class—which I just realized should probably be a struct instead—has the property let id: Int64 as its sole essential component, but apparently, you can't access an element of a set by its hash value? I don't want to use Set's instance method first(where:) because it has a complexity of O(n), and notes could contain millions of Note objects.
You cannot rely on Swift to eliminate the construction of a new Note in your code. Your Set needs to ask the Note for its hashValue, and may need to call == with your Note as an argument. Those computations require the Note object. Possibly if Swift can inline everything, it can notice that your hashValue and == depend only on the id property, but it is certainly not guaranteed to notice or to act on that information.
It sounds like you should be using an [Int64: Note] instead of a Set<Note>.
No, Swift will not avoid creating the new Note object. The problem here is trying to determine if an object already exists in a set. In order to check if an object already exists in the set, you must have some way to identify this object consistently and have that identification persist across future reads and writes to that set. Assuming we want to adopt Swift's hashing enhancements which deprecates the old methods for having to manually provide a hashValue for any object conforming to the Hashable, we should not use a Set as a solution to this problem. The reason is because Swift's new recommended hashing methods use a random seed to generate hashes for added security. Depending on hash values to identify an element in a set alone would therefore not be possible.
It seems that your method of identifying these objects are by an id. I would do as Rob suggests and use a dictionary where the keys are the id. This would help you answer existential questions in order avoid instantiating a Note object.
If you need the resulting dictionary as a Set, you can still create a new Set from this resulting dictionary sequence.
It is possible to pull out a particular element from a set as long as you know how to identify it, and the operations would be O(1). You would use these two methods:
https://developer.apple.com/documentation/swift/set/2996833-firstindex
https://developer.apple.com/documentation/swift/set/2830801-subscript
Here is an example that I was able to run in a playground. However, this assumes that you have a way to identify in your data / model the id of the Note object beforehand in order to avoid creating the Note object. In this case, I am assuming that a Note shares an id with a Dictionary it holds:
import UIKit
struct DictionaryThatNoteUses {
var id: String
init(id: String = UUID().uuidString) {
self.id = id
}
}
struct Note: Hashable {
let dictionary: DictionaryThatNoteUses
var id: String {
return dictionary.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Note, rhs: Note) -> Bool {
return lhs.id == rhs.id
}
}
var downloadedNoteDictionaries: [DictionaryThatNoteUses] = [
DictionaryThatNoteUses(),
DictionaryThatNoteUses(),
DictionaryThatNoteUses()
]
var notesDictionary: [String: Note] = [:]
var notesSet: Set<Note> = []
// Add a dictionary with the same id as the first dictionary
downloadedNoteDictionaries.append(downloadedNoteDictionaries.first!) // so now there are four Dictionary objects in this array
func createDictionaryOfNotes() {
func avoidCreatingDuplicateNotesObject(with id: String) {
print("avoided creating duplicate notes object with id \(id)") // prints once because of the duplicated note at the end matching the first, so a new Note object is not instantiated
}
downloadedNoteDictionaries.forEach {
guard notesDictionary[$0.id] == nil else { return avoidCreatingDuplicateNotesObject(with: $0.id) }
let note = Note(dictionary: $0)
notesDictionary[note.id] = note
}
}
createDictionaryOfNotes()
// Obtain a set for set operations on those unique Note objects
notesSet = Set<Note>(notesDictionary.values)
print("number of items in dictionary = \(notesDictionary.count), number of items in set = \(notesSet.count)") // prints 3 and 3 because the 4th object was a duplicate
// Grabbing a specific element from the set
// Let's test with the second Note object from the notesDictionary
let secondNotesObjectFromDictionary = notesDictionary.values[notesDictionary.values.index(notesDictionary.values.startIndex, offsetBy: 1)]
let thirdNotesObjectFromDictionary = notesDictionary.values[notesDictionary.values.index(notesDictionary.values.startIndex, offsetBy: 2)]
if let secondNotesObjectIndexInSet = notesSet.firstIndex(of: secondNotesObjectFromDictionary) {
print("do the two objects match: \(notesSet[secondNotesObjectIndexInSet] == secondNotesObjectFromDictionary)") // prints true
print("does the third object from dictionary match the second object from the set: \(thirdNotesObjectFromDictionary == notesSet[secondNotesObjectIndexInSet])") // prints false
}

Array element assignment causes expensive _ArrayBufferProtocol.init(copying:)

I have the following class, grossly simplified
class MyClass {
var largeArray: [Int] = []
init() {
largeArray.reserveCapacity(10000000)
... lots of code to add 10000000 various elements to largeArray
}
func mutateArray(idx: Int) {
largeArray[idx] = someVal
}
}
Surprisingly, when profiling this code, calls to mutateArray turned out to be very expensive, with most of the time spent
in _ArrayBufferProtocol.init(copying:), and some in _swift_release_dealloc. The time spent is proportional to the number of calls to mutateArray, indicating
that this happens every time the method is called.
Why is this happening? Is there any way to avoid it?
Your array's buffer leaks out of its MyClass encapsulation somewhere.
If largeArray is initialized within a MyClass object, it has sufficient capacity reserved up front, and you've never let anyone else have access to your class, or alias it yourself, then you couldn't possibly cause a CoW copy.
You should set var largeArray to private. Not only will that enforce the capsulation you're in need of, but it'll also show you what else is accessing this.

Swift Closures: Must Capture Lists Be Exhaustive?

Suppose I have a Swift class like this:
#objc final MyClass : NSObject
{
let classPropertyString = "A class property"
func doStuff()
{
let localString = "An object local to this function"
DispatchQueue.global(qos: .userInitiated).async { [classPropertyString] in
// Do things with 'classPropertyString' and 'localString'
}
}
}
My question is: when I write a capture list, am I responsible for EXHAUSTIVELY listing all the things to which I want the closure to hold a strong reference?
In other words, if I omit localString from my capture list (as I've done here), will the closure still automatically capture a strong reference to it or am I in for a bad time?
There are several minor quirks with your question that make it tricky to answer clearly, but I think I understand the underlying concern, and the short answer is "no." But your example is impossible, so the answer is "it's impossible." And if it were possible, there'd be no strong reference (nor would there be a need for one), so the question still would be kind of "it's impossible." Even so, let's walk through what's going on here.
First, closure can't reference localString unless it's reassigned somehow in the comment inside doStuff(). closure is assigned at a level where localString is not in scope. Closures can only capture variables that are in scope when they are assigned, not when they're called. But let's go back to the original version of this question, before it was edited. That version did have the case you're describing:
#objc final myClass : NSObject
{
let classPropertyString = "A class property"
func doStuff()
{
let localString = "An object local to this function"
DispatchQueue.global(qos: .userInitiated).async { [classPropertyString] in // (1)
// Do things with 'classPropertyString' and 'localString'
}
// (2)
}
}
There's no problems here. classPropertyString is copied into the closure, avoiding any retain loops. localString is referenced by the closure, and so it's preserved as long as the closure exists.
Because you listed classPropertyString in the capture list, it is evaluated at point (1) and copied into the closure. Because you implicitly captured localString, it is treated as a reference. See Capture Lists in the Swift Programming Language Reference for some excellent examples of exactly how this works in different cases.
In no case (*) will Swift allow the underlying storage for something you're using in a closure to disappear behind your back. That's why the typical concern is excessive retains (memory leaks) rather than dangling references (crashes).
(*) "In no case" here is a lie. There are several ways that Swift will allow it, but almost all of them involve "Unsafe" which is your warning about that. The major exception is unowned, and of course anything involving ! types. And Swift is not typically thread-safe, so you need to be careful about that...
The last comment about thread-safety is a place where the subtle distinctions between implicit and explicit captures can really matter. Consider this case where you modify an implicitly captured value on two queues:
func doStuff() -> String
{
var localString = "An object local to this function"
DispatchQueue.global(qos: .userInitiated).async {
localString = "something else"
callFunction(localString)
}
localString = "even more changes"
return localString
}
What happens in that case? Good grief, never do that. I believe it's undefined behavior and that localString could be anything including corrupted memory, at least in the most general case (it might be defined behavior for calling .async; I'm not sure). But don't do it.
But for your normal cases, there is no reason to explicitly capture local variables. (I sometimes wish Swift had gone the C++ way and said it was required, but it isn't.)
Ok, one more way implicit and explicit are different that might drive home how they work. Consider a stateful closure like this (I build these pretty often):
func incrementor() -> () -> Int {
var n = 0
return {
n += 1
return n
}
}
let inc = incrementor()
inc() // 1
inc() // 2
inc() // 3
let inc2 = incrementor()
inc2() // 1
See how the local variable n is captured by the closure, and can be modified after it goes out of scope. And see how inc2 has its own version of that local variable. Now try that with explicit capture.
func incrementor() -> () -> Int {
var n = 0
return { [n] in // <---- add [n]
n += 1 // Left side of mutating operator isn't mutable: 'n' is an immutable capture
return n
}
}
Explicit captures are copies and they're immutable. Implicit captures are references, and so have the same mutability as the thing they reference.

Struct cannot have a stored property that references itself [duplicate]

Reference cycles in Swift occur when properties of reference types have strong ownership of each other (or with closures).
Is there, however, a possibility of having reference cycles with value types only?
I tried this in playground without succes (Error: Recursive value type 'A' is not allowed).
struct A {
var otherA: A? = nil
init() {
otherA = A()
}
}
A reference cycle (or retain cycle) is so named because it indicates a cycle in the object graph:
Each arrow indicates one object retaining another (a strong reference). Unless the cycle is broken, the memory for these objects will never be freed.
When capturing and storing value types (structs and enums), there is no such thing as a reference. Values are copied, rather than referenced, although values can hold references to objects.
In other words, values can have outgoing arrows in the object graph, but no incoming arrows. That means they can't participate in a cycle.
As the compiler told you, what you're trying to do is illegal. Exactly because this is a value type, there's no coherent, efficient way to implement what you're describing. If a type needs to refer to itself (e.g., it has a property that is of the same type as itself), use a class, not a struct.
Alternatively, you can use an enum, but only in a special, limited way: an enum case's associated value can be an instance of that enum, provided the case (or the entire enum) is marked indirect:
enum Node {
case None(Int)
indirect case left(Int, Node)
indirect case right(Int, Node)
indirect case both(Int, Node, Node)
}
Disclaimer: I'm making an (hopefully educated) guess about the inner workings of the Swift compiler here, so apply grains of salt.
Aside from value semantics, ask yourself: Why do we have structs? What is the advantage?
One advantage is that we can (read: want to) store them on the stack (resp. in an object frame), i.e. just like primitive values in other languages. In particular, we don't want to allocate dedicated space on the heap to point to. That makes accessing struct values more efficient: we (read: the compiler) always knows where exactly in memory it finds the value, relative to the current frame or object pointer.
In order for that to work out for the compiler, it needs to know how much space to reserve for a given struct value when determining the structure of the stack or object frame. As long as struct values are trees of fixed size (disregarding outgoing references to objects; they point to the heap are not of interest for us), that is fine: the compiler can just add up all the sizes it finds.
If you had a recursive struct, this fails: you can implement lists or binary trees in this way. The compiler can not figure out statically how to store such values in memory, so we have to forbid them.
Nota bene: The same reasoning explains why structs are pass-by-value: we need them to physically be at their new context.
Quick and easy hack workaround: just embed it in an array.
struct A {
var otherA: [A]? = nil
init() {
otherA = [A()]
}
}
You normally cannot have a reference cycle with value types simply because Swift normally doesn't allow references to value types. Everything is copied.
However, if you're curious, you actually can induce a value-type reference cycle by capturing self in a closure.
The following is an example. Note that the MyObject class is present merely to illustrate the leak.
class MyObject {
static var objCount = 0
init() {
MyObject.objCount += 1
print("Alloc \(MyObject.objCount)")
}
deinit {
print("Dealloc \(MyObject.objCount)")
MyObject.objCount -= 1
}
}
struct MyValueType {
var closure: (() -> ())?
var obj = MyObject()
init(leakMe: Bool) {
if leakMe {
closure = { print("\(self)") }
}
}
}
func test(leakMe leakMe: Bool) {
print("Creating value type. Leak:\(leakMe)")
let _ = MyValueType(leakMe: leakMe)
}
test(leakMe: true)
test(leakMe: false)
Output:
Creating value type. Leak:true
Alloc 1
Creating value type. Leak:false
Alloc 2
Dealloc 2
Is there, however, a possibility of having reference cycles with value types only?
Depends on what you mean with "value types only".
If you mean completely no reference including hidden ones inside, then the answer is NO. To make a reference cycle, you need at least one reference.
But in Swift, Array, String or some other types are value types, which may contain references inside their instances. If your "value types" includes such types, the answer is YES.