简体   繁体   中英

Why does my Swift iOS build get error: “Cannot find 'Sleep' in scope”

The.swift file is part of an iOS ObjC static lib. When I try to build, I get the build error.

Sleep is because I want to make sure that stopscan is done and then want to completely and gracefully tear down CBCentralManager.

import UIKit
import CoreBluetooth
import os  

    // Instantiate BLE central service:
    var BLE_Central_instance = BLE_Central();

 . . .  


    // Tears down BLE central
    public func stop_BLE_Central()
    {
        os_log("BLE_Central: stop_BLE_Central...")     
        BLE_Central_instance.stop_CBCentralManager()
        os_log("BLE_Central: stop_BLE_Central done.")
    }

. . .  

    @objc open class BLE_Central: NSObject
    {  
    . . .  
        @objc public func stop_CBCentralManager()                           
        {
           os_log("BLE_Central: stop_CBCentralManager...")     
            
            centralManager.stopScan()
            os_log("        Scanning stopped (ie. centralManager)")
    
            Sleep(5)
    
            centralManager = nil 
    
            data.removeAll(keepingCapacity: false)
            
     
            os_log("BLE_Central: stop_CBCentralManager done.")
       }

Going through the various approaches here,

Sleep(5)

This doesn't work because there is no such function Sleep() . It's possible to fix that by using

Thread.sleep(forTimeInterval: 5)

That avoids the error but it's really bad design for a couple of reasons. The first is that you're blocking the thread for 5 seconds, and from the look of things, it's the main thread. That completely locks up your user interface during that time. No UI elements can respond if the main thread is blocked.

You can avoid that by using something like

DispatchQueue.main.asyncAfter(deadline: .now() + 5) { 
    // Do delayed stuff here
}

That doesn't block the current thread, it says, keep on running for now but do this stuff in 5 seconds. So that's better, or at least less bad.

The second reason that Thread.sleep is a terrible idea here is that it's a poor band-aid on the situation. There's no guarantee that 5 seconds is the right amount of time. It might be too much, or too little. The amount of time you need might be inconsistent. You're hoping 5 seconds is enough but you have no way to know that.

A better way is to look for the thing you actually want to know. You're using CBCentralManager and you want to know when it stops scanning. So, use the isScanning property on CBCentralManager and do your stuff when its value changes from true to false .

First, make sure to use @objc in your central manager declaration, and add an observation property:

@objc fileprivate(set) var central: CBCentralManager
var observation: NSKeyValueObservation?

Then register for notifications that isScanning has changed, and handle those changes:

    observation = observe(\.central.isScanning, options: NSKeyValueObservingOptions.new) { (manager, change) in
        guard let isScanning = change.newValue else { return }
        if !isScanning {
            // Do your delayed stuff here
        }
    }

If you want to perform something is 5 seconds time, do it like this:

DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [self] in
        centralManager = nil
        data.removeAll(keepingCapacity: false)
    }

If place this code where you had your "Sleep(5)" call, it will perform the two statements in its body on the main thread 5 seconds after "now".

It will do this asynchronously, meaning that the rest of your app will keep going after this call.

Thread.sleep(forTimeInterval: 5)  // delay to allow completion of any final notification

Credit to paulw for Thread.sleep

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM