How to handle crashes caused by semaphore in iOS? - ios

Crashes when the class is destructed, how to handle unfinished semaphore?
class CrashTestViewCtrl: UIViewController {
private var semaphore = DispatchSemaphore(value: 2)
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async { [weak self] in
self?.semaphore.wait()
// do something ......
}
}
deinit {
print("……deinit……")
}
}

It crashes because of lack of semaphore.signal() and wrong setup of semaphore.
You should call semaphore.signal() after you are done with the async method.
You should add semaphore.wait() outside of the async method.
In your case, it could be like this;
class CrashTestViewCtrl: UIViewController {
private var semaphore = DispatchSemaphore(value: 2)
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async { [weak self] in
// do something ......
self?.semaphore.signal()
}
semaphore.wait()
}
deinit {
print("……deinit……")
}
}

Related

swift set crash in asynchous queue

I found the weird crash when using Set in async queue closure below, confirmed that it only happens in async queue, but Array works.
func testA1() {
var set = Set<Int>()
for i in 0...10 {
DispatchQueue.global().async {
set.update(with: i) // Crash here: EXC_BAD_ACCESS
// set.insert(i)
}
}
print(set as Any)
}
func testA2() {
var set = Set<Int>()
for i in 0...10 {
DispatchQueue.global().sync {
set.update(with: i) // Works!
}
}
print(set as Any)
}
func testB() {
var array = [Int]() // Works!
for i in 0...10 {
DispatchQueue.global().async {
array.append(i)
}
}
print(array as Any)
}
Swift version:
Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
Target: x86_64-apple-darwin20.2.0
My bad or swift bug? Why?
Most of datatypes in Swift aren't thread-safe.
You should interact using different ways.
protocol ThreadSafeExecutor {
var semaphore: DispatchSemaphore { get set }
func wait()
func signal()
}
extension ThreadSafeExecutor {
func wait() {
semaphore.wait()
}
func signal() {
semaphore.signal()
}
}
class YourClass: ThreadSafeExecutor {
func someMethod() {
wait()
defer {
signal()
}
/// thread-safe code
}
}

UINavigationController return nil Swift

When i try print(self.navigationController) in viewWillApear or viewDidLoad all ok. But when delegate return response from API print(self.navigationController) return nil. What could it be?
extension EnterpriseList: APIDataDelegate {
func successRequest() { //print(self.navigtionController) == nil
DispatchQueue.main.async {
self.navigationController?.popToRootViewController(animated: true)
}
}
func badRequest() {
DispatchQueue.main.async {
Alert.showWarningAlert(withTitle: "Внимание!", andMessage: "Ошибка получения данных, попробуйте чуть позже", whereSender: self)
}
}
}
class EnterpriseList: UIViewController {
let dataSource = DataSource()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
dataSource.makeAPICall()
}
}
extension EnterpriseList: APIDataDelegate {
func successRequest() {
DispatchQueue.main.async {
self.navigationController!.popToRootViewController(animated: true)
}
}
func badRequest() {
DispatchQueue.main.async {
}
}
}
protocol APIDataDelegate: class {
func successRequest()
func badRequest()
}
class DataSource {
var delegate: APIDataDelegate?
private let queue = DispatchQueue(label: "Test", qos: .background, attributes: [], autoreleaseFrequency: .inherit, target: nil)
func makeAPICall() {
queue.asyncAfter(deadline: .now() + 2) {[weak self] in
self?.delegate?.successRequest()
}
queue.asyncAfter(deadline: .now() + 10) {[weak self] in
self?.delegate?.successRequest()
}
}
}
The crash is because you are calling the method on deallocated object.
Here is how to fix this:
Make your delegate is weak and you are correctly deallocating the objects
weak var delegate: APIDataDelegate?
Thx #Manoj for answers it's halped me. But i found solution in other. Besides SurveyList i have UserMenu where i create static let view controllers. Removing static i solved my problem.

Why doesn't my completion block work?

Implementing solution given here How to make a synchronous request using Alamofire?
I don't get any errors, it just doesn't work as expected. In tableViewController
override func viewDidLoad() {
super.viewDidLoad()
loadData() { (didCompleteRequest) in
if (didCompleteRequest) {
self.TodosTableView.delegate = self
self.TodosTableView.dataSource = self
print("loading successfull")
} else {
print("loading failed")
}
}
print("leaving viewDidLoad")
}
func loadData(completion: #escaping (Bool) -> Void) {
Alamofire.request(TodosViewController.serverAdress + "projects/index.json").responseJSON { response in
do {
// async stuff
} catch {
completion(false)
}
print("leaving loadData")
completion(true)
}
}
output I get
leaving viewDidLoad
leaving loadData
loading successfull
apparently, the first element should be the last one
First viewDidLoad is running in the main thread. So when you put this loadData() in viewDidLoad controls dispatched to background thread where alamofire works on, and the main thread continues and prints leaving viewDidLoad
Try this
override func viewDidLoad() {
super.viewDidLoad()
self.TodosTableView.delegate = self
self.TodosTableView.dataSource = self
loadData() { (didCompleteRequest) in
if (didCompleteRequest) {
self.TodosTableView.reloadData()
print("loading successfull")
} else {
print("loading failed")
}
}
print("leaving viewDidLoad")
}
You are call block code after get response from server. so first call "print("leaving viewDidLoad")".
response code get with delay so call block code with delay

How to make function for HTTP request on other class in Swift?

I'm writing a app to make a http requests with SwiftHTTP, but I want to create a class to make this, and in the ViewController I call the functions to do this. So, I create a UIButton to call the functions.
I don't know if I need use threads or exists ways to make this easy.
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self,action:#selector(self.clickRequest(sender:)), for: UIControlEvents.touchUpInside )
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#objc func clickRequest (sender: UIButton) {
}
}
HTTPService.swift
import Foundation
import SwiftHTTP
class HTTPService {
}
How I make this in others Apps. But inside of ViewController.swift.
HTTP.GET("https://www.host.com/example",requestSerializer: JSONParameterSerializer()) {
response in
if let err = response.error {
print(err.localizedDescription)
return
}
} catch let err as NSError {
print(err)
}
}
Create one method with successBlock and failureBlock inside your HTTPService service class like below,
class HTTPService {
func makeRequest(params: [String: Any], successBlock: () -> Void, failureBlock: () -> Void) {
HTTP.GET("https://www.host.com/example",requestSerializer: JSONParameterSerializer()) {
response in
if let err = response.error {
print(err.localizedDescription)
failureBlock() // Call failure block
return
} else {
successBlock() // Call success block
}
} catch let err as NSError {
print(err)
failureBlock() // Call failure block
}
}
}
You can call this method like below,
#objc func clickRequest (sender: UIButton) {
HTTPService().makeRequest(params: ["name": "userName"], successBlock: {
// Handle API success here. E.g Reloading table view
}) {
// Handle API failure here. E.g Showing error to user
}
}
Thanks

Do one action when method is finished in Swift

I want do one action when one method is finished, I execute one method in other method and I want the second method stop until the first method is finished.
I have that method:
func ejecutarOBJC(){
let txtNombre = self.view.viewWithTag(4) as? UITextField
let textoNombre=txtNombre?.text
let txtContra = self.view.viewWithTag(5) as? UITextField
let textoContra=txtContra?.text
let instanceOfCustomObject: SQLViewController = SQLViewController()
instanceOfCustomObject.nombre = textoNombre;
instanceOfCustomObject.contra = textoContra;
instanceOfCustomObject.obtenerExistenciaUsuario()
}
And also the other method:
func otherMethod(){
ejecutarOBJC()
//I want continue with that method when the execution of the other method finish
}
This is how you would achieve this:
func methodOne() {
//Method one code here
methodTwo()
}
func methodTwo() {
//Method Two code here.
}
As per your comment, here is how to wait when using async code:
func methodOne() {
//Code goes here
methodTwo { () -> () in
//Method two has finished
}
}
func methodTwo(completion: () -> ()) {
//Code goes here
completion()
}
Use closures:
func callCompetionFunction()
{
// Tira mola meseda (do your stuff)
completionFunction("a parameter") { () -> Void in
print("function copleted")
}
}
func completionFunction(param: AnyObject, completion: ()->Void)
{
// Do your stuff
completion()
}
To test it in a view controller
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
callCompetionFunction()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func callCompetionFunction()
{
// Tira mola meseda (do your stuff)
print("do something here")
completionFunction("a parameter") { () -> Void in
print("function copleted")
}
}
func completionFunction(param: AnyObject, completion: ()->Void)
{
// Do your stuff
if param is String
{
print("parameter = \(param)")
}
print("going to execute completion closure")
completion()
}
}

Resources