OS X崩溃:释放的指针未分配

时间:2018-12-10 15:14:00

标签: swift macos

需要帮助来了解崩溃的真正原因。我已经制作了应该24/7正常运行的应用程序。它从设备读取并准备一些数据,并创建一个Web服务器以将数据发送到探针。 崩溃发生在各种时间之后,因此我无法真正在模拟器中重现它。

这是最后一个崩溃文件的详细信息:

  

自启动以来唤醒的时间:110000秒

     

系统完整性保护:已启用

     

崩溃的线程:37调度队列:com.apple.root.default-qos

     

异常类型:EXC_CRASH(SIGABRT)

     

异常代码:0x0000000000000000,0x0000000000000000

     

异常说明:EXC_CORPSE_NOTIFY

     

已达到调度线程软限制:64(在同步操作中阻塞了太多的调度线程)

     

特定于应用程序的信息:   abort()称为

     

***对象0x247032000错误:未分配释放的指针

线程37的详细信息:

  

线程37崩溃::调度队列:com.apple.root.default-qos

     

0 libsystem_c.dylib 0x00007fff8dab3298 usleep $ NOCANCEL + 0

     

1 libsystem_c.dylib 0x00007fff8dae16e9中止+ 139

     

2 libsystem_malloc.dylib 0x00007fff965fe041免费+ 425

     

3 libswiftCore.dylib 0x0000000106fed219 $ Ss19_SwiftStringStorageCfD + 9

     

4 libswiftCore.dylib 0x0000000106ffba00 _swift_release_dealloc + 16

...

  

46 org.cocoapods.GCDWebServer 0x0000000106c67316-[GCDWebServerConnection(Subclassing)processRequest:completion:] + 128

     

47 org.cocoapods.GCDWebServer 0x0000000106c6392e-[GCDWebServerConnection _startProcessingRequest] + 146

     

48 org.cocoapods.GCDWebServer 0x0000000106c64e13 __45- [GCDWebServerConnection _readRequestHeaders] _block_invoke + 1781

     

49 org.cocoapods.GCDWebServer 0x0000000106c65935 __64- [GCDWebServerConnection(Read)readHeaders:withCompletionBlock:] _ block_invoke + 290

     

50 org.cocoapods.GCDWebServer 0x0000000106c65613 __68- [GCDWebServerConnection(Read)readData:withLength:completionBlock:] _ block_invoke + 307

     

51 libdispatch.dylib 0x00007fff8e73c7a8 __dispatch_read_block_invoke_252 + 39

     

52 libdispatch.dylib 0x00007fff8e72a93d _dispatch_call_block_and_release + 12

     

53 libdispatch.dylib 0x00007fff8e71f40b _dispatch_client_callout + 8

     

54 libdispatch.dylib 0x00007fff8e72329b _dispatch_root_queue_drain + 1890

     

55 libdispatch.dylib 0x00007fff8e722b00 _dispatch_worker_thread3 + 91

     

56 libsystem_pthread.dylib 0x00007fff8ea934de _pthread_wqthread + 1129

     

57 libsystem_pthread.dylib 0x00007fff8ea91341 start_wqthread + 13

代码:

这是处理Web服务器(GCDWebServer)的单调代码。它根据http查询中的ID读取存储在内存中的数据

private let queue = DispatchQueue(label: "gcdwebserver_queue")

private func setupServer(){

    webServer.delegate = self
    webServer.addDefaultHandler(forMethod: "GET", request: GCDWebServerRequest.self) { (req, completion) in

        if let resp = self.response(for: req) {
            return completion(resp)
        }
    }

    queue.async {
        self.webServer.start(withPort: 8521, bonjourName: "GCD Web Server")            
    }

}

下面是一个单例代码,该单例调用modbus(C Modbus库)连接(每30秒)到设备列表并读取数据:

private let modbusQueue = DispatchQueue(label: "modbus_queue")
private func initiateTimer() {
    polling()

    timer?.invalidate()
    if #available(OSX 10.12, *) {
        timer = Timer.scheduledTimer(withTimeInterval: pollingInterval, repeats: true) { (_) in
            self.polling()
        }
    } else {
        timer = Timer.scheduledTimer(timeInterval: pollingInterval, target: self, selector: #selector(polling), userInfo: nil, repeats: true)
    }
}

@objc private func polling() {

    for device in self.devices {

        if !device.isInProcess && device.isEnabled {
            self.modbusQueue.async {
                self.connectAndReadValues(device: device)
            }
        }

    }
}

private func connectAndReadValues(device: Device) {

    device.isInProcess = true

    let connect = device.modbus.connect()
    //handling connection status
    //...

    readValues(forDevice: device)
}

private func readValues(forDevice device: Device){
    //some constants

    do {

        let registers = try device.modbus.readRegisters(from: startAddress, count: registersCount)
        device.readState = .success
        device.modbus.close()
        //parse and save data to the app memory just as a dictionary. It saves only one small dictionary per device
        parseRegisters(controllerIP: device.modbus.host, vendor: vendor, registers: registers, startAdd: startAddress)
    } catch let error {
        //handling errors
    }

    //refreshing interface in the main queue
}

1 个答案:

答案 0 :(得分:1)

“已达到调度线程软限制:64”将是一个不错的起点。看来您正在耗尽工作线程池(只有64个)。

例如,如果modbusQueue是并发队列,则以下内容很容易耗尽您的工作线程:

for device in self.devices {
    if !device.isInProcess && device.isEnabled {
        self.modbusQueue.async {
            self.connectAndReadValues(device: device)
        }
    }
}

有一些解决方案:

  1. 您可以使用操作队列,并设置其maxConcurrentOperationCount

    let queue = OperationQueue()
    queue.name = "modbusQueue"
    queue.maxConcurrentOperationCount = 4
    
    for device in self.devices where !device.isInProcess && device.isEnabled {
        queue.addOperation {
            self.connectAndReadValues(device: device)
        }
    }
    
  2. 如果要保留在调度队列中,可以使用起始值非零的信号量来限制并发程度。常规模式是:

    let semaphore = DispatchSemaphore(value: 4)
    for object in self.objects {
        semaphore.wait()                       // the first four will start immediately; the subsequent ones will wait for signals from prior ones
        queue.async {
            ...
            semaphore.signal()                 // signal that this is done
        }
    }
    

    因此,这一次将尝试不超过四个。而且您可以将整个事件分派到某个后台队列中,因此在执行此操作时不会阻塞调用线程。无论如何,您最终得到:

    DispatchQueue.global().async {
        let semaphore = DispatchSemaphore(value: 4)
        for device in self.devices where !device.isInProcess && device.isEnabled {
            semaphore.wait()
            self.modbusQueue.async {
                self.connectAndReadValues(device: device)
                semaphore.signal()
            }
        }
    }
    
  3. 或者您可以使用concurrentPerform

    DispatchQueue.global().async {
        DispatchQueue.concurrentPerform(iterations: devices.count) { index in
            let device = self.devices[index]
            if !device.isInProcess && device.isEnabled {
                self.connectAndReadValues(device: device)
            }
        }
    }
    

我可能倾向于操作队列模式,但这是一些选择。


FWIW,我会警惕使用计时器进行轮询。您可能要冒使队列严重积压的风险。例如,如果在定时器再次触发时未完成处理事件怎么办?

如果您确实必须进行轮询,则建议您删除计时器,而只需在队列末尾添加一个隔离项,以触发设备的下一次轮询。