iOS: remove objects or reinitialize arrays in swift? - ios

I'm new in swift and I want to know if the beast approach to reuse an array is to delete all object inside or reinitialize it.
For example if I have:
var my_array:NSMutableArray! //global variable
and the first method that I use:
func firstMethod{
my_array = NSMutableArray()
my_array.addObject("one")
my_array.addObject("two")
my_array.addObject("three")
...
}
for example now in another method I need my_array empty:
the best solution is this:
func newmethod() {
my_array = NSMutableArray() //reinitialize (drastic method)
//here, all new operation with my_array
}
or is this?:
func newmethod() {
if my_array == nil{
my_array = NSMutableArray()
} else {
my_array.removeAllObjects()
}
}

The general answer is it depends. There is a really major danger when it comes to having mutable public properties, particularly when it comes to multithreaded apps.
If you always reinitialize, it is more likely that you're safe from multithreading issues.
If you are emptying the array, you are prone multithreading issues. You're probably going to get index out of bounds crashes and the like when you are emptying the array on one thread and trying to access any index on another thread.
Thread A check's the array's count. It's 10. Thread B empties the
array. Array's count is now 0. Thread A tries to access an object at
index 3. Crash.
Even though your code looks fine, something like this:
if array.count > 3 {
array[3].doThings()
}
Multithreading means that another thread mutating the array between that code checking the count and that code accessing an index.
I would opt for immutable objects as much as possible, and I would avoid mutating mutable objects that have any chance of being accessed in a thread unsafe manner.

Why you are reinitializing the array there is no need to reinitialize it.And if you reinitialize then it will point to another memory address so it would be better to reuse without reinitialize. You can do like this
if (my_array == nil){
my_array = NSMutableArray();
}
else{
//my_array.removeAllObjects();
// Do your stuff here
}

Related

How do i release allocated memory for array?

I am not understanding how to give weak refrence to the array or release allocated memory of array, can anyone tell me how to fix this leak?
var menuDetails:[[String:Any]] = []//this my global array object
Getting following leak even i am using ARC.
Screenshot for array memory leak!
I was just scared about that memory leak,can anyone tell how do i fix it?
You don't want to use a weak reference. If you do that your array will get released immediately.
weak var weakArray: [[String:Any]]? = []
Will contain nil as soon as you create it.
Instead, you should set the array to nil (or empty) once you're done with the contents:
You could use `menuDetails.removeAll() to delete all the entries in the array, or you could change your declaration to make it an Optional
var menuDetails:[[String:Any]]? = []//this my global array object
And then set it to nil when you're done with it:
menuDetails = nil
An object will only be retained if another object has a strong reference to it. As soon as your view controller disappears, it will most likely be deallocated as well, which automatically removes its strong references to other objects. Thus, if imageArray is strongly referenced only by your disappearing view controller, the memory will automatically be released. You do not need to use an autoreleasepool.
In order to store weak references in arrays and/or dictionaries, you need an intermediate structure.
for example:
struct WeakRef
{
weak var object:AnyObject?
init( _ objectRef:AnyObject?)
{ object = objectRef }
}
// use WeakRef when you add object instances to your dictionary (or array)
menuDetails[0]["objectKey"] = WeakRef(yourObject)
// you will need additional code to get the actual object out of the intermediate structure
// and given that it is a weak reference you'll also need to deal with its optionality.
if let yourObject = (menuDetails[0]["objectKey"] as? WeakRef)?.object as? YourClass,
{
// ... do your thing with your object ...
}
The syntax could probably be made more legible by wrapping this in custom operators and generics but this is the general approach to it.

Initialize an empty Results<T> variable

I have a Realm object called Task. I'm displaying these tasks in a table view. I have a variable decalred to hold these objects.
var tasks: Results<Task>!
At initial launch, I'm getting these tasks from an API. Until the local realm is empty. But still when the UITableViewController loads, it fires the data source methods. At this point, the tasks variable can still be nil. So the app crashes at methods like numberOfRowsInSection.
How can I initialize the above variable so that it can be in an empty state and it won't cause crashes?
So the app crashes at methods like numberOfRowsInSection
Don't let it. You are the one who is causing the crash, by assuming that tasks is not nil when in fact it can be. It is your job to check in numberOfSections to see whether tasks is nil. If it is, return 0 so you don't get asked any other questions.
The following code will demonstrate the way how to initialize an empty Results object.
class TaskManager {
private var _realm: Realm?
var realm: Realm {
if _realm == nil {
_realm = try! Realm()
}
return _realm!
}
var tasks: Results<Task> = realm.objects(Task.self).filter("FALSEPREDICATE")
}

Realm: live updates of constant values

I'm using SwiftRealm 2.03 and do not understand the magic how constant data (even metadata) gets updated if the data in realm changes...
Here an example:
private func closePastExistingTravelTimes(){
let travelTimes = fetchTravelTimes(onlyNotClosedTravelTimes: true)
guard travelTimes.count > 1 else {
return
}
let numberOfTravelTimes = travelTimes.count
for index in 0..<numberOfTravelTimes-2{
print("index:\(index) count:\(travelTimes.count)")
try! realm.write {
let travelTime = travelTimes[index]
travelTime.travelPhaseIsClosed = true
realm.add(travelTime, update: true)
}
}
}
I'm loading data in the beginning and store them in an constant.
Then I iterate over the items and change the condition of the query so that the fetched data would change if I would query again. But I don't. What even is more suprising that the constant numberOfTravelTimes is even adjusted as you can see below in the log.
index:0 count:5
index:1 count:4
index:2 count:3
index:3 count:2 --> BAM - Exception
What is happening here? How can I be save in my example?
Realm Results objects are live, meaning if you update an object in such a way that it no longer conforms to a Results query, the Results object will update to exclude it. This means you need to be careful when you base a for loop off a Results object, since if the Results object mutates in the middle of the loop, you'll end up with an exception.
Normally, the easiest, but not the most elegant way to mitigate this is to copy all of the Realm objects in a Results object to a static array so it won't mutate during the loop.
In this particular case however, it would be more appropriate to just enclose the entire for loop inside the Realm write transaction. This is generally best practice (Since it's best to batch as many Realm writes into as few write transactions as possible), but in this case, it will have the added advantage of not updating the contents of the Results object until after you're done with it.
try! realm.write { // Open the Realm write transaction outside of the loop
for index in 0..<numberOfTravelTimes-2 {
print("index:\(index) count:\(travelTimes.count)")
let travelTime = travelTimes[index]
travelTime.travelPhaseIsClosed = true
realm.add(travelTime, update: true)
}
}
You should iterate your results in reverse order.
for index in (0 ..<numberOfTravelTimes-1).reverse()
See if it helps

Object passed by reference will not exist. Swift

I have an array.
var array:[customType] = [] // pseudo code
func Generate_New_Array(){
//initialization of generatedNewArray
array = generatedNewArray
for (index,element) in array{
async_process({
Update_Data_From_Web(&array[index])
})
}
})
}
func Update_Data_From_Web(inout object:customType){
download_process{
object = downloadedData
}
}
The question is , what will should I do if I call Generate_New_Array before Update_Data_From_Web will finish for each of elements. They will store value back to not-existing index in array. How to avoid problems with that.
You have a couple of options:
Make the Generate_New_Array process cancelable, and then cancel the old one before starting the new one.
Make the Generate_New_Array serial so that when you make a subsequent call to this method, it will finish the calls first. For example, you could have this enqueue an operation on a serial queue.
Regardless of which approach you adopt, if this is multithreaded code, make sure you synchronize your interaction with the model object (via GCD queues or locks or whatever).

Remove objects from an array before calling the API

I'm creating a table view which is hooked to an API. However, I'm having trouble with doing a refresh on pull. I've added the logic, however I can't seem to delete all the objects in the array before making a new api call.
Here is my array
var recentArray = Array<News>()
UIRefreshControl function:
func refresh(sender: UIRefreshControl){
lastObjectIndex=0
// remove all objects
getRecent()
self.tableVIew.reloadData()
self.refreshControl?.endRefreshing()
}
How can i remove all objects in my array before calling getRecent, which adds an object to the array?
You can reset your array like this:
recentArray = []
The compiler already knows the type of the array objects, so there's no need to do anything else.
You can remove all objects by calling
recentArray.removeAll(keepCapacity: false)
You can removed all object by adding following code befor getRecent called.
var array = [0, 1, 2, 3]
array.removeAll()
let count = array.count
// count is 0
Hope this will help you.

Resources