DispatchQueue.main.sync returning exc_bad_instruction Swift 3
Asked Answered
C

4

31

I want to display an ActivityIndicatorView in my app, but when I call the sync method from the main thread, the app crashes with the error: exc_bad_instruction (code=exc_i386_invop subcode=0x0) I'm using xcode 8.0 and swift 3

Can someone please help me?

 func POST(endpoint:NSString!,body:NSString!,vc:UIViewController? = nil)->NetworkResult{
    let result = NetworkResult()
    DispatchQueue.main.sync {
        self.displayActivityIndicator(viewController: vc)
    }
    let urlStr = self.url.appending(endpoint as String).appending(self.getHashAutenticacao() as String)
    print(urlStr)
    let request = NSMutableURLRequest(url: URL(string: urlStr)!, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData, timeoutInterval: 20)
    print(request.debugDescription)
    request.setValue("application/json", forHTTPHeaderField: "Accept")
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpMethod = "POST"
    request.httpBody = body.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: true)

    // send the request
    var data: NSData!
    do {
        data = try NSURLConnection.sendSynchronousRequest(request as URLRequest, returning: &self.response) as NSData!
    } catch let error1 as NSError {
        self.error = error1
        data = nil
    }
    if let httpResponse = self.response as? HTTPURLResponse {
        result.resultCode = httpResponse.statusCode

        if httpResponse.statusCode == 200{
            if data != nil{
                if data.length > 0{
                    let json = (try! JSONSerialization.jsonObject(with: data as Data, options: JSONSerialization.ReadingOptions.mutableContainers))
                    if let jsonArray:NSArray = json as? NSArray{
                        result.data = jsonArray
                    }else{
                        if let jsonDict:NSDictionary = json as? NSDictionary{
                            result.data = [jsonDict]
                        }
                    }
                }
            }
        }
    }else {
        result.message = self.error!.debugDescription as NSString?
    }

    DispatchQueue.main.sync {
        self.hideActivityIndicator(viewController: vc)
    }
    return result
}
Corissa answered 20/10, 2016 at 12:35 Comment(3)
what is -displayActivityIndicator(_:) method doing?Zooplasty
Do not load data form a server synchronously, it causes bad user experience. Get used to the asynchronous pattern.Armindaarming
I just noticed this too, i have a method that can return from main or secondary thread (which probably is not such a good idea but it makes sense for my case). I think is something new because i never worried about dispatching main thread from main thread till now.Godparent
U
55

What you are trying to do here is to launch the main thread synchronously from a background thread before it exits. This is a logical error.

You should do it like this:

DispatchQueue.global().async(execute: {
    print("teste")
    DispatchQueue.main.sync{
        print("main thread")
    }
})
Unshackle answered 20/10, 2016 at 12:43 Comment(7)
@RodrigoCosta what are you trying to achieve with DispatchQueue.main.sync { self.displayActivityIndicator(viewController: vc) }?Unshackle
The method POST needs to return data to the caller, the method displayActivityIndicator, displays an UIActivityIndicatorView into vc.view. I can't use async because i need data returned from the post.Corissa
The method POST needs to return data to the caller, the method displayActivityIndicator, displays an UIActivityIndicatorView into vc.view. I can't use async because i need data returned from the post.Corissa
@RodrigoCosta it will never work. Read the accepted answer to this question: #31776614. It is the proper way to do it.Unshackle
@RodrigoCosta welcome. I would appreciate, if you accept it.Unshackle
Why does DispatchQueue.main.sync mean I launch it from a background thread?Putout
This answer makes no sense. The code after the DispatchQueue.global().async(execute: { block is still going to run before the print("main thread") code is run. Just use DispatchQueue.main.async and don't bother with the global background queue.Robers
M
46

Maybe it's because you are trying to DispatchQueue.main.sync from the Main thread. You can check if you are already in the main thread like:

if Thread.isMainThread {
  // do stuff
} else {
  DispatchQueue.main.sync {
    // do stuff
  }
}
Merriott answered 6/6, 2018 at 15:36 Comment(2)
Thanks, saved me some time. I find it kind of strange though that calling DispatchQueue.main.sync on the main thread leads to an error.Drawshave
There is nothing strange. You put block of code to be executed on main thread. So, if it already on main thread, that's obvious error..)Chandra
O
5

If you want to read the value from the main thread, here is a handy function:

func syncMain<T>(_ closure: () -> T) -> T {
    if Thread.isMainThread {
        return closure()
    } else {
        return DispatchQueue.main.sync(execute: closure)
    }
}

Usage:

// we are in BG or Main, does not matter
let value = syncMain {
    // we are in Main for sure
    return ...
}
Owenism answered 25/5, 2021 at 14:5 Comment(0)
F
0

According to the Apple documentation given below. https://developer.apple.com/documentation/dispatch/dispatchqueue/1452870-sync

Calling the sync function in the current target queue(main thread in this case) may lead to a deadlock state. Because of this, an error will be thrown. So you have to change the thread or you have to call asynchronously.

Suggestion: You can use a global thread to execute the function.

DispatchQueue.global().sync {
    self.displayActivityIndicator(viewController: vc)
}

Or also you can use the async function in the main thread.

DispatchQueue.main.async {
    self.displayActivityIndicator(viewController: vc)
}
Fascist answered 12/9, 2023 at 1:24 Comment(3)
Your first option is bad because you can't run UI code on a background thread.Robers
Got it! What about second option @HangarRash?Fascist
The 2nd option is the correct one. It will work whether the current thread is on the main queue or not.Robers

© 2022 - 2024 — McMap. All rights reserved.