how to avoid an object being deallocated? - swift

static func appendMissingObjToArray(var oldArray:[AnyObject],newArray:[AnyObject]){
for n in newArray{
var isExist = false
for o in oldArray{
if(n.isEqual(o)){
//exist
isExist = true
break
}
}
if(!isExist){
oldArray.append(n)
}
}
}
The above function is the append some data from newArray to oldArray.
when this function is done. And getting data from the oldArray, I got BAD ACCESS error. so I think it is due to the newly added object in oldArray have been deallocated and the newArray is released.
Any thing I can do for avoid this?

I believe that you need to declare your oldArray parameter as an inout. As follows:
//...
static func appendMissingObjToArray(inout oldArray:[AnyObject],newArray:[AnyObject]){
//...
You can then do e.g (note the ampersand & below:
class01.appendMissingObjToArray(&myOldArray, newArray: myNewArray)
println(myOldArray) // will contain the appended result
Done like this myOldArray (the passed array) will be mutated. So you may need to pass a copy of your original if retaining the original is important.

Related

didSet and Getter in Swift array

I want to initialise elements only if an array has been set from outside the current class. Looks like a use for didSet!
// external API
var arr: [Display] = [] {
didSet {
array = arr
initialiseElements(arr: arr)
}
}
// internal
private var array: [Display] = []
but when we return the value from arr it might not be correct - I want to use array as a backing store.
I tried to use a getter on arr but this isn't allowed in Swift. How can I prevent the use of arr within the class, or otherwise ensure we only initiliseElements(:) via calls outside the class?
Why use didSet? why not set?
var arr: [Display] {
set {
array = newValue
initialiseElements(arr: newValue)
}
get {
return self.array
}
}
How can I prevent the use of arr within the class?
You can't, swift doesn't have such access control
otherwise ensure we only initiliseElements(:) via calls outside the class
Again, you can't.
It makes no sense logically as well, think of what you are asking for, you are asking class to declare a variable which itself cant set (read only) but should expose to it outside class to write (read + write) it?
How is that possible? What is the use case you are trying to solve here? If for some reason you ended up with this solution, may be solutioning is wrong! re think of your implementation.
Hope this helps

How can I deallocate all references elements from an array?

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.

Cannot assign to property: function call returns immutable value

Consider the following example.
struct AStruct{
var i = 0
}
class AClass{
var i = 0
var a: A = A(i: 8)
func aStruct() -> AStruct{
return a
}
}
If I try to mutate the the variable of a instance of class AClass it compiles successfully.
var ca = AClass()
ca.a.i = 7
But If I try to mutate the return value of aStruct method, the compile screams
ca.aStruct().i = 8 //Compile error. Cannot assign to property: function call returns immutable value.
Can someone explain this.
This is compiler's way of telling you that the modification of the struct is useless.
Here is what happens: when you call aStruct(), a copy of A is passed back to you. This copy is temporary. You can examine its fields, or assign it to a variable (in which case you would be able to access your modifications back). If the compiler would let you make modifications to this temporary structure, you would have no way of accessing them back. That is why the compiler is certain that this is a programming error.
Try this.
var aValue = ca.aStruct()
aValue.i = 9
Explanation
aStruct() actually returns a copy of the original struct a. it will implicitly be treated as a constant unless you assign it a var.
Try using a class instead of a struct, as it's passed by reference and holds onto the object, while a struct is passed by value (a copy is created).

Are computed properties evaluated every time they are accessed?

I have two questions about computed properties in Swift.
Are computed properties evaluated every time they are accessed? Or they are stored somewhere for future access?
What kind of property is this, since I couldn't google it out:
let navigationController: UINavigationController = {
var navigator = UINavigationController()
navigator.navigationBar.translucent = false
return navigator
}()
Is this also evaluated every time it is accessed?
That is NOT a computed property.
let navigationController: UINavigationController = {
var navigator = UINavigationController()
navigator.navigationBar.translucent = false
return navigator
}()
It is just a stored property populated with the result of the value returned by this block of code.
var navigator = UINavigationController()
navigator.navigationBar.translucent = false
return navigator
The block is executed when the instance of the class is instantiated. Only once.
So writing this
struct Person {
let name: String = {
let name = "Bob"
return name
}() // <- look at these
}
is equivalent to this
struct Person {
let name: String
init() {
self.name = "Bob"
}
}
IMHO the first approach is better because:
it does allow you to declared and populate a property in the same "space"
it's more clear
does prevent duplication of code if you have multiple initializers
Note #1: Storing a closure inside a property
As dfri noted in the comment below, the block of code does end with (). It means that the code is evaluated and the result assigned to the property.
On the other hand, if we remove the () at the end of the block, we get something different, infact the block is not evaluated.
In this case Swift tries to assign a stored closure to the property. This will produce a compile error since the property has this type UINavigationController.
With the correct syntax we can put a closure inside a property.
struct Person {
let sayHello: ()->() = { print("Hello") }
}
Now we have a sayHello property which contains a closure. The closure receives 0 parameters and does return Void.
let bob = Person()
bob.sayHello // this does NOT execute the code inside closure
bob.sayHello() // this does execute the code in the closure and does print the message
Note #2: let's talk about Computed Properties
So we made clear that code in this question is not a Computed Property.
However, as EmilioPelaez noted in another comment below, we should also state that a Computed Property is evaluated each time it is accessed.
In the example below I created a Computed Property age. As you can see each time I invoke it, the code in the block gets executed as well.
Example of a Computed Property (age)

Array of nil Objects in Swift

My class has a property that, in other languages, would be a simple Array of Strings, which would be initialized at an object's instantiation. In Swift, I have come up with the following:
class Foo {
var myArray: (String!)[]!
init(arraySize: Int, sourceOfData: SomeOtherClass){
myArray = Array<(String!)>(count: arraySize, repeatedValue:nil)
/* ... code to set the elements of the array using sourceOfData ... */
}
}
This is the only way I have been able to compile my code that allows pre-allocation of the Array's elements. However, I think all those exclamation marks make my code hard to read.
I know I can change my repeatedValue to an arbitrary non-nil string, and simplify the type to String[]!, but that would be a hack.
Also, I can do:
class Foo {
let myArray: String[] = []
init(sourceOfData: SomeOtherClass){
/*loop over sourceOfData*/{
myArray.append(/* computed String value */)
}
}
}
However, this has clearly worse performance, as the compiler cannot guess the length of my Array and allocate a contiguous block of memory for it. Normally, I would not care too much about optimizing the performance of this part of my code, but for this class it is critical.
Is there any way to have legible types without compromising performance?
You don't need to mark myArray as optional as long as you populate it in your init(). And if you can loop over sourceOfData using map something like this would work:
class Foo {
var myArray: String[]
init(sourceOfData: SomeOtherClass) {
myArray = sourceOfData.map {
return $0.computeStringValue()
}
}
}
if you really do need to use a loop, and you can at least determine how large an array you need, you can do something like this:
class Foo {
var myArray: String[]
init(sourceOfData: SomeOtherClass) {
myArray = Array<String>(count: SomeOtherClass.count, repeatedValue: "")
for i, item in enumerate(sourceOfData) {
myArray[i] = item.computeStringValue()
}
}
}
One last note about performance: LLVM is a very sophisticated compiler. You say that it is "obviously worse performance", but this is the kind of code for which static analysis may actually be able to determine the appropriate size of the array. I suggest profiling it with real data for your use case.
You can use the reserveCapacity method on Array. This will make sure there is enough pre-allocated memory to hold your data.
class Foo {
var myArray: String[]
init(sourceOfData: SomeOtherClass) {
myArray.reserveCapacity(sourceOfData.count)
// loop over data calling .append()
}
}