Развертывание необязательного значения (возвращаемого data.withUnsafeBytes (_ :)) иногда не работает с защитой - PullRequest
0 голосов
/ 25 октября 2018

У меня проблема с оператором let let, который ведет себя странно.Весь код ниже.В противном случае блок оператора guard let data = readData, let size = sizeOfData else ... в методе readActivity(subdata: Data) выполняется неправильно, даже если readData и sizeOfData не nil.

Код

import Foundation

enum ActivityDataReaderError: Error {
    case activityIsReadingOtherCentral
    case bluetooth(Error?)
    case staleData
}

protocol ActivityDataReaderDelegate: class {
    func didReadActivity(data: Data)
    func didFailToReadActivity(error: ActivityDataReaderError)
}

final class ActivityDataReader {
    private var sizeOfData: Int?
    private var isOtherDeviceReading: Bool {
        // 0xFFFF
        return sizeOfData == 65535
    }
    private var readData: Data?

    var isEmpty: Bool {
        return sizeOfData == nil
    }

    weak var delegate: ActivityDataReaderDelegate?

    static func timestampValue(_ timestamp: UInt32) -> Data {
        var value = timestamp
        return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
    }

    func reset() {
        readData = nil
        sizeOfData = nil
        NSLog("reset() -- \(Thread.current)")
    }

    func readActivity(data: Data?, error: Error? = nil) {
        guard let data = data else {
            delegate?.didFailToReadActivity(error: .bluetooth(error))
            return
        }
        let isFirstChunk = readData == nil
        if isFirstChunk {
            let sizeData = data.subdata(in: 0..<2)
            sizeOfData = sizeData.withUnsafeBytes { $0.pointee }
            guard !isOtherDeviceReading else {
                delegate?.didFailToReadActivity(error: .activityIsReadingOtherCentral)
                return
            }
            NSLog(String("readActivity() Size of data: \(String(describing: sizeOfData))"))
            let subdata = data.subdata(in: 2..<data.count)
            readActivity(subdata: subdata)
        } else {
            readActivity(subdata: data)
        }
    }

    private func readActivity(subdata: Data) {
        if let lastReadData = readData {
            readData = lastReadData + subdata
        } else {
            readData = subdata
        }
        guard let data = readData, let size = sizeOfData else {
            NSLog("WTF? data:\(String(describing: readData)), "
                + "sizeOfData: \(String(describing: sizeOfData)), "
                + "thread: \(Thread.current)")
            assertionFailure("WTF")
            return
        }
        NSLog("subdata: \(String(describing: subdata)), "
            + "totalReadBytes: \(data.count), "
            + "size: \(size)")
        if data.count == size {
            delegate?.didReadActivity(data: data)
            reset()
        }
    }
}

Тест

Тест, который иногда проходит, а иногда дает сбой из-за assertionFailure("WTF").

class ActivityDataServiceReaderTests: XCTestCase {
    var service: ActivityDataReader?

    override func setUp() {
        super.setUp()
        service = ActivityDataReader()
    }

    override func tearDown() {
        service = nil
        super.tearDown()
    }

    func testBufferIsNotEmpty() {
        NSLog("testBufferIsNotEmpty thread: \(Thread.current)")
        guard let service = service else { fatalError() }
        let firstDataBytes = [UInt8.min]
        let data1 = Data(bytes: [7, 0] + firstDataBytes)
        service.readActivity(data: data1)
        XCTAssertFalse(service.isEmpty)

        service.reset()
        XCTAssertTrue(service.isEmpty)
    }
}

Журнал консоли в случае сбоя

2018-10-25 14:53:30.033573+0200 GuardBug[84042:11188210] WTF? data:Optional(1 bytes), sizeOfData: Optional(7), thread: <NSThread: 0x600003399d00>{number = 1, name = main}

Среда

  • Xcode10
  • swift 4.1 с устаревшей системой сборки
  • swift 4.2

На мой взгляд, нет никакого способа выполнить код в блоке else в guard, пусть elseблок метода readActivity(subdata: Data).Все работает в основном потоке.Я что-то пропустил?Как это возможно, иногда проходы тестов, а иногда сбои?

Спасибо за любую помощь.

Редактировать:

Более узкая проблема охраны let + data.withUnsafeBytes:

func testGuardLet() {
    let data = Data(bytes: [7, 0, UInt8.min])
    let sizeData = data.subdata(in: 0 ..< 2)
    let size: Int? = sizeData.withUnsafeBytes { $0.pointee }
    guard let unwrappedSize = size else {
        NSLog("failure: \(size)")
        XCTFail()
        return
    }
    NSLog("success: \(unwrappedSize)")
}

Журнал:

2018-10-25 16:32:19.497540+0200 GuardBug[90576:11351167] failure: Optional(7)

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

Благодаря помощи по адресу: https://forums.swift.org/t/unwrapping-value-with-guard-let-sometimes-does-not-work-with-result-from-data-withunsafebytes-0-pointee/17357 проблема была со строкой:

let size: Int? = sizeData.withUnsafeBytes { $0.pointee }

Где считанные данные были понижены до Необязательного Int (длиной 8 байт), но сам размерData составлял всего 2 байтадолго.Я понятия не имею, как это возможно, иногда это работает, но решение - которое, кажется, работает правильно - это использовать метод withUnsafeBytes следующим образом:

let size = sizeData.withUnsafeBytes { (pointer: UnsafePointer<UInt16>) in pointer.pointee }

Возвращаемое значение не является обязательным и имеет надлежащийвведите UInt16 (длиной 2 байта).

0 голосов
/ 25 октября 2018

Если вы отметите Документация , появится предупреждение:

Предупреждение. Аргумент байтового указателя не должен храниться и использоваться вне времени жизни вызова замыкания.

Похоже, вы должны иметь дело с размером внутри корпуса крышки

func testGuardLet() {
        let data = Data(bytes: [7, 0, UInt8.min])
        var sizeData = data.subdata(in: 0 ..< 2)
        withUnsafeBytes(of: &sizeData) { bytes in
            print(bytes.count)
            for byte in bytes {
                print(byte)
            }
        }

        let bytes = withUnsafeBytes(of: &sizeData) { bytes in
            return bytes // BUGS ☠️☠️☠️
        }
    }
...