I have recently developed an iOS application with Swift which handles lots of background HTTP tasks and updates not only UI, but also static data for current session (lots of arrays, variables, etc) according to response data. I may be counted as new at iOS Developing and there are some points in which I am confused:
Updating UI from a background task is handled via GCD API. I have always handled these updates using:
dispatch_async(dispatch_get_main_queue, {
// Update UI
})
Let me give a scenario and clarify my point:
I have a view controller with a UITableView subview. This table view will display the list of something (lets say user names). I prepared and resumed an NSURLSessionDataTask:
let request = NSMutableURLRequest(URL: someURL)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
// Handle error case
// Parse data, and receive a user list
// AppData.userList = parsed list
// Update table view
}
Some of my testers have faced some crashes related with dispatch calls and run loop in which I could not find the reason behind. I think this is related with my dispatch calls. Now I am reconsidering my design for this and here are my questions:
Any clear comment or guiding would be very helpful! Thanks a lot already now
First things first:
let request = NSMutableURLRequest(URL: someURL)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { [weak self]
(data, response, error) in
if let weakself = self {
// ...
dispatch_async(dispatch_get_main_queue()) {
// update UI
}
}
}
Whenever you're making asynchronous calls, you need to make sure there are no self
references to prevent any possible circular reference (memory leak).
What is the difference of updating static data (array, dictionary, etc.) inside and outside of a dispatch_async call on main queue in completion handler of a http task (UI will be updated inside a dispatch call anyway, after the updates on my data)?. How can I ensure thread safety for background threads while reading from, inserting into or removing from an array?
There is no difference between updating data inside and outside of a dispatch_async
. You just need to make sure you're not mutating the array or dictionary while iterating through them. You can either do this by locking the data structure or creating a temporary shallow copy.
For example, if you're reading an Array that might be altered by another thread:
var integers = Array<Int>()
// The following creates an IMMUTABLE shallow copy of mutable array
let ints = integers
for value in ints {
// use value
}
// OR use locking
objc_sync_enter(integers)
for value in integers {
// use value
}
objc_sync_exit(integers)
// in another thread - lock before mutating
objc_sync_enter(integers)
integers.append(someIntValue)
objc_sync_exit(integers)
Of course, you can use other locking mechanisms. But the point is, you just need to make sure data is accessed in thread safe manner.
Does making a dispatch_async call inside a closure (for task completion handler) may cause any problem?
The answer is NO. As long as you make sure no reference to self
exists within those closures and data accessible by competing threads are accessed/altered in a thread safe manner.