Swift initializer call Dispatch Queue async - ios

Is it wrong to call async from Swift object initializer such as this one
let serialQueue = DispatchQueue(label: "com.myApp.SerialQueue")
private let property1:Int?
public override init()
{
super.init()
/* Initialize properties */
setupProperties()
serialQueue.async { [unowned self] in
self.nonBlockingSetup()
}
}
private func setupProperties() {
self.property1 = 1
}
private func nonBlockingSetup() {
//Some non-blocking code that shouldn't run on main thread
}
Some people say async call is problematic before init returns. Need to know what Swift language says about it.
EDIT: Is there any difference if I modify the code as follows:
public override init()
{
super.init()
/* Initialize properties */
setupProperties()
callNonBlockingCodeAsync()
}
private func callNonBlockingCodeAsync() {
serialQueue.async { [unowned self] in
self.nonBlockingSetup()
}
}

To answer your question, I tried out the simple example.
Errors are very much self explanatory, in the initialisation process dispatchQueue are capturing self reference right before it's actual initialisation.
You are running into the concurrency problem where initialisation of object is necessary before using it.
dispatchQueue uses closures to provide DispatchWorkItem and as you know closures captures values surrounding it's scope.
Update
One work around would be to give default values to your properties but
I am not sure if that will help you.

In general, a constructor should not do any meaningful work.
Having a constructor that executes code delayed (because it's async) will be unexpected for anyone using that class (quite possibly including you in 6 months), and can therefore lead to bugs. In such cases it's usually better to have a separate initialization method, which makes it clear to an api user that there is something more going on.
If you absolutely want to make sure the initialization method is called, I usually make the constructor private and add a class method for construction. Again this signals api users that there is something going on behind the scenes.

Related

How do I write thread-safe code that uses a completionHandler with a function that delegates code to an instance of OperationQueue?

I've been using the CloudKitShare sample code found here as a sample to help me write code for my app. I want to use performWriterBlock and performReaderBlockAndWait as found in BaseLocalCache using a completionHandler without violating the purposes of the design of the code, which focuses on being thread-safe. I include code from CloudKitShare below that are pertinent to my question. I include the comments that explain the code. I wrote comments to identify which code is mine.
I would like to be able to use an escaping completionHandler if possible. Does using an escaping completionHandler still comply with principles of thread-safe code, or does it in any way violate the purpose of the design of this sample code to be thread-safe? If I use an escaping completionHandler, I would need to consider when the completionHandler actually runs relative to other code outside of the scope of the actual perform function that uses the BaseLocalCache perform block. I would for one thing need to be aware of what other code runs in my project between the time the method executes and the time operationQueue in BaseLocalCache actually executes the block of code and thus the completionHandler.
class BaseLocalCache {
// A CloudKit task can be a single operation (CKDatabaseOperation)
// or multiple operations that you chain together.
// Provide an operation queue to get more flexibility on CloudKit operation management.
//
lazy var operationQueue: OperationQueue = OperationQueue()
// This sample ...
//
// This sample uses this dispatch queue to implement the following logics:
// - It serializes Writer blocks.
// - The reader block can be concurrent, but it needs to wait for the enqueued writer blocks to complete.
//
// To achieve that, this sample uses the following pattern:
// - Use a concurrent queue, cacheQueue.
// - Use cacheQueue.async(flags: .barrier) {} to execute writer blocks.
// - Use cacheQueue.sync(){} to execute reader blocks. The queue is concurrent,
// so reader blocks can be concurrent, unless any writer blocks are in the way.
// Note that Writer blocks block the reader, so they need to be as small as possible.
//
private lazy var cacheQueue: DispatchQueue = {
return DispatchQueue(label: "LocalCache", attributes: .concurrent)
}()
func performWriterBlock(_ writerBlock: #escaping () -> Void) {
cacheQueue.async(flags: .barrier) {
writerBlock()
}
}
func performReaderBlockAndWait<T>(_ readerBlock: () -> T) -> T {
return cacheQueue.sync {
return readerBlock()
}
}
}
final class TopicLocalCache: BaseLocalCache {
private var serverChangeToken: CKServerChangeToken?
func setServerChangeToken(newToken: CKServerChangeToken?) {
performWriterBlock { self.serverChangeToken = newToken }
}
func getServerChangeToken() -> CKServerChangeToken? {
return performReaderBlockAndWait { return self.serverChangeToken }
}
// Trial: How to use escaping completionHandler? with a performWriterBlock
func setServerChangeToken(newToken: CKServerChangeToken?, completionHandler: #escaping (Result<Void, Error>)->Void) {
performWriterBlock {
self.serverChangeToken = newToken
completionHandler(.success(Void()))
}
}
// Trial: How to use escaping completionHandler? with a performReaderBlockAndWait
func getServerChangeToken(completionHandler: (Result<CKServerChangeToken, Error>)->Void) {
performReaderBlockAndWait {
if let serverChangeToken = self.serverChangeToken {
completionHandler(.success(serverChangeToken))
} else {
completionHandler(.failure(NSError(domain: "nil CKServerChangeToken", code: 0)))
}
}
}
}
You asked:
Does using an escaping completionHandler still comply with principles of thread-safe code, or does it in any way violate the purpose of the design of this sample code to be thread-safe?
An escaping completion handler does not violate thread-safety.
That having been said, it does not ensure thread-safety, either. Thread-safety is solely a question of whether you ever access some shared resource from one thread while mutating it from another.
If I use an escaping completionHandler, I would need to consider when the completionHandler actually runs relative to other code outside of the scope of the actual perform function that uses the BaseLocalCache perform block.
Yes, you need to be aware that the escaping completion handler is called asynchronously (i.e., later). That is less of a thread-safety concern than a general understanding of the application flow. It is only a question of what you might be doing in that closure.
IMHO, the more important observation is that the completion handler is called on the cacheQueue used internally by BaseLocalCache. So, the caller needs to be aware that the closure is not called on the caller’s current queue, but on cacheQueue.
It should be noted that elsewhere in that project, they employ another common pattern, where the completion handler is dispatched back to a particular queue, e.g., the main queue.
Bottom line, thread-safety is not a question of whether a closure is escaping or not, but rather (a) from what thread does the method call the closure; and (b) what the supplied closure actually does:
Do you interact with the UI? Then you will want to ensure that you dispatch that back to the main queue.
Do you interact with your own properties? Then you will want to make sure you synchronize all of your access with them, either with actors, relying on the main queue, use your own serial queues, or a reader-writer pattern like in the example you shared with us.
If you are ever unsure about your code’s thread-safety, you might consider temporarily turning on TSAN as described in Diagnosing Memory, Thread, and Crash Issues Early

What is the appropriate strategy for using #MainActor to update UI?

Suppose you have a method that executes asynchronously in a global context. Depending on the execution you need to update the UI.
private func fetchUser() async {
do {
let user = try await authService.fetchCurrentUser()
view.setUser(user)
} catch {
if let error = error {
view.showError(message: error.message)
}
}
}
Where is the correct place to switch to the main thread?
Assign #MainActor to the fetchUser() method:
#MainActor
private func fetchUser() async {
...
}
Assign #MainActor to the setUser(_ user: User) and showError(message: String) view's methods:
class SomePresenter {
private func fetchUser() async {
do {
let user = try await authService.fetchCurrentUser()
await view.setUser(user)
} catch {
if let error = error {
await view.showError(message: error.message)
}
}
}
}
class SomeViewController: UIViewController {
#MainActor
func setUser(_ user: User) {
...
}
#MainActor
func showError(message: String) {
...
}
}
Do not assign #MainActor. Use await MainActor.run or Task with #MainActor instead to run setUser(_ user: User) and showError(message: String) on the main thread (like DispatchQueue.main.async):
private func fetchUser() async {
do {
let user = try await authService.fetchCurrentUser()
await MainActor.run {
view.setUser(user)
}
} catch {
if let error = error {
await MainActor.run {
view.showError(message: error.message)
}
}
}
}
Option 2 is logical, as you are letting functions that must run on the main queue, declare themselves as such. Then the compiler can warn you if you incorrectly call them. Even simpler, you can declare the class that has these functions to be #MainActor, itself, and then you don't have to declare the individual functions as such. E.g., because a well-designed view or view controller limits itself to just view-related code, it is safe for that whole class to be declared as #MainActor and be done with it.
Option 3 (in lieu of option 2) is brittle, requiring the app developer to have to remember to manually run them on the main actor. You lose compile-time warnings should you fail to do the right thing. Compile-time warnings are always good. But WWDC 2021 video Swift concurrency: Update a sample app points out that even if you adopt option 2, you might still use MainActor.run if you need to call a series of MainActor methods and you might not want to incur the overhead of awaiting one call after another, but rather wrap the group of main actor functions in a single MainActor.run block. (But you might still consider doing this in conjunction with option 2, not in lieu of it.)
In the abstract, option 1 is arguably a bit heavy-handed, designating a function that does not necessarily have to run on the main actor to do so. You should only use the main actor where it is explicitly needed/desired. That having been said, in practice, I have found that there is often utility in having presenters (or controllers or view models or whatever pattern you adopt) run on the main actor, too. This is especially true if you have, for example, synchronous UITableViewDataSource or UICollectionViewDataSource methods grabbing model data from the presenter. If you have the relevant presenter using a different actor, you cannot always return to the data source synchronously. So you might have your presenter methods running on the main actor, too. Again, this is best considered in conjunction with option 2, not in lieu of it.
So, in short, option 2 is prudent, but is often married with options 1 and 3 as appropriate. Routines that must run on the main actor should be designated as such, rather than placing that burden on the caller.
The aforementioned Swift concurrency: Update a sample app covers many of these practical considerations and is worth watching if you have not already.

Memory Leak Kotlin Native library in iOS

I'm building a Kotlin library to use in my iOS app using Kotlin/Native. After I call some methods in the library from Swift, which works, I also want to call methods in Swift from the library. To accomplish this I implemented an interface in the library:
class Outbound {
interface HostInterfaceForTracking {
fun calcFeatureVector(bitmap: Any?): Array<Array<FloatArray>>?
}
var hostInterface: HostInterfaceForTracking? = null
fun registerInterface(hostInterface: HostInterfaceForTracking) {
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
}
}
This is implemented on the Swift side like this:
class HostInterfaceForTracking : OutboundHostInterfaceForTracking {
var t : Outbound? = nil
init() {
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
func calcFeatureVector(bitmap: Any?) -> KotlinArray<KotlinArray<KotlinFloatArray>>? {
do {
var test : Any? = (bitmap as! Bitmap).bitmap
return nil
} catch {
return nil
}
}
}
The TrackingWrapper looks like this:
class TrackingWrapper : NSObject {
static var instance: TrackingWrapper? = nil
var inbound: Inbound? = nil
var worker: Worker
override init() {
self.worker = Worker()
super.init()
initInboundInterface()
}
func initInboundInterface() {
runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
HostInterfaceForTracking()
}
}
func runOnMatchingLibraryThread(block: #escaping() -> Void) {
worker.enqueue {
block()
}
}
}
The function runOnMatchingLibraryThread is needed because every call to the TrackingLibrary needs to be called from the exact same thread, so the Worker class initializes a thread and enqueues every method to that thread.
The Bitmap in this case is simply a wrapper for an UIImage, which I already accessed with the .bitmap call, so I've tried to access the wrapped UIImage and save it in the test variable. The library gets the current camera frame from the Swift side every few frames and sends the current image wrapped as a Bitmap to the method calcFeatureVector depicted here.
Problem: My memory load starts increasing as soon as the app starts until the point it crashes. This is not the case if I don't access the wrapped UIImage (var test : Any? = (bitmap as! Bitmap)). So there is a huge memory leak, just by accessing the wrapped variable on the Swift side. Is there anything I've missed or is there any way to release the memory?
Looks like you have a circular dependency here:
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
You are asking a property inside HostInterfaceForTracking to maintain a strong reference to the same instance of HostInterfaceForTracking. You should be using [weak self] to avoid the circular reference.
EDIT:
Ok after seeing the rest of you code theres a lot to unpack. There is a lot of unnecessary bouncing back and forth between classes, functions and threads.
There is no need to use runOnMatchingLibraryThread to just create an instance of something. You only need to use that for the code processing the image itself (I would assume, I haven't seen anything so far that requires being split off into another thread). Inside TrackingWrapper, you can create a singleton more easily, and matching the swift pattern by simply doing this as the first line:
static let shared = TrackingWrapper()
And everywhere you want to use it, you can just call TrackingWrapper.shared. This is more common and will avoid one of the levels of indirection in the code.
I'm not sure what Worker or Inbound are, but again these can and should be created inside the TrackingWrapper init, rather than branching Inbound's init, to use another thread.
Inside initInboundInterface you are creating an instance of HostInterfaceForTracking() which doesn't get stored anywhere. The only reason HostInterfaceForTracking is continuing to stay in memory after its creation, is because of the internal circular dependency inside it. This is 100% causing some form of a memory issue for you. This should probably also be a property on TrackingWrapper, and again, its Init should not be called inside runOnMatchingLibraryThread.
Having HostInterfaceForTracking's init, also using runOnMatchingLibraryThread is problematic. If we inline all the code whats happening is this:
TrackingWrapper
init() {
self.runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
}
Having all these classes unnecessarily keep coming back to TrackingWrapper is going to cause issues.
Inside HostInterfaceForTracking 's init, no need to be creating Outbound on a separate thread. First line in this class can simply be:
var t : Outbound = OutBound()
Or do it in the init if you prefer. Either way will also remove the issue of needing to unwrap Outbound before using it.
Inside Outbound you are storing 2 references to the hostInterface instance:
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
I would have imagined there should only be 1. If there are now multiple copies of a class that has a circular dependency, which has multiple calls to separate threads. This again will cause issues.
I'm still not sure on the differences between Swift and Kotlin. In Swift when passing self into a function to be stored, the class storing it would mark the property as weak, like so:
weak var hostInterface: ......
Which will avoid any circular dependency from forming. A quick google says this isn't how things work in Kotlin. It might be better to look into the swift side passing in a closure (lambda on kotlin) and the kotlin side executing that. This might avoid the need to store a strong reference. Otherwise you need to be looking into some part of your code setting hostInterface back to null. Again its a bit hard to say only seeing some of the code and not knowing how its working.
In short, it looks like the code is very over complicated, and needs to be simplified, so that all these moving pieces can be tracked easier.

Are delegates necessary here?

This might be very basic. But, I am not very sure if the delegates are necessary in the following scenario?
Are delegates used in synchronous ways? If yes, is it good to call a delegate method in a function called by a caller who is a delegate[Like the example below]?
class FooViewController: UIViewController {
func login() {
let loginHelper = LoginHelper()
loginHelper.fooDelegate = self
loginHelper.shouldEnableLogin()
}
func enableLogin() {
// Do some UI updates
}
func reset() {
// Clear some values in the views
}
}
class LoginHelper {
weak var delegate: fooDelegate?
func shouldEnableLogin() {
//clear some text views
delegate.reset()
//do some validation, synchronous
delegate.enableLogin()
}
}
Delegates are a design pattern that allows one object to send messages to another object when a specific event happens. Imagine an object A calls an object B to perform an action. Once the action is complete, object A should know that B has completed the task and take necessary action, this can be achieved with the help of delegates!
I think the crux here is your question "Are delegates used in synchronous ways?".
The fundamental delegate mechanism is synchronous: I.e. the called delegate method will be on the same thread as the caller. So if the caller is your object then you control what thread this occurs on.
However the caller could create a new thread and then call the delegate method from that. So if the caller is not yours, check the documentation for it carefully before relying on the call being on the same thread.

Concern about memory when choosing between notification vs callback closure for network calls?

Many posts seem to advise against notifications when trying to synchronize functions, but there are also other posts which caution against closure callbacks because of the potential to inadvertently retain objects and cause memory issues.
Assume inside a custom view controller is a function, foo, that uses the Bar class to get data from the server.
class CustomViewController : UIViewController {
function foo() {
// Do other stuff
// Use Bar to get data from server
Bar.getServerData()
}
}
Option 1: Define getServerData to accept a callback. Define the callback as a closure inside CustomViewController.
Option 2: Use NSNotifications instead of a callback. Inside of getServerData, post a NSNotification when the server returns data, and ensure CustomViewController is registered for the notification.
Option 1 seems desirable for all the reasons people caution against NSNotification (e.g., compiler checks, traceability), but doesn't using a callback create a potential issue where CustomViewController is unnecessarily retained and therefore potentially creating memory issues?
If so, is the right way to mitigate the risk by using a callback, but not using a closure? In other words, define a function inside CustomViewController with a signature matching the getServerData callback, and pass the pointer to this function to getServerData?
I'm always going with Option 1 you just need to remember of using [weak self] or whatever you need to 'weakify' in order to avoid memory problems.
Real world example:
filterRepository.getFiltersForType(filterType) { [weak self] (categories) in
guard let strongSelf = self, categories = categories else { return }
strongSelf.dataSource = categories
strongSelf.filteredDataSource = strongSelf.dataSource
strongSelf.tableView?.reloadData()
}
So in this example you can see that I pass reference to self to the completion closure, but as weak reference. Then I'm checking if the object still exists - if it wasn't released already, using guard statement and unwrapping weak value.
Definition of network call with completion closure:
class func getFiltersForType(type: FilterType, callback: ([FilterCategory]?) -> ()) {
connection.getFiltersCategories(type.id).response { (json, error) in
if let data = json {
callback(data.arrayValue.map { FilterCategory(attributes: $0) } )
} else {
callback(nil)
}
}
}
I'm standing for closures in that case. To avoid unnecessary retains you just need to ensure closure has proper capture list defined.

Resources