Advertencia: la inicialización de 'UnsafeBufferPointer <T>' da como resultado un puntero de búfer colgante

10

Después de actualizar a Swift 5.2 / Xcode 11.4 recibí una advertencia para el siguiente código:

extension Data {

    init<T>(from value: T) {
        var value = value
        let pointer = UnsafeBufferPointer(start: &value, count: 1)
        self.init(buffer: pointer)
    }

    func to<T>(type: T.Type) -> T {
        return self.withUnsafeBytes { $0.load(as: T.self) }
    }
}

En la línea permiten puntero = UnsafeBufferPointer (inicio: & valor, cuentan: 1) Tengo

La inicialización de 'UnsafeBufferPointer' da como resultado un puntero de búfer colgante

Puedo usar @silenceWarning pero es una solución sucia. ¿Quizás necesito almacenar el puntero en algún lugar y limpiarlo en el futuro?

Exey Panteleev
fuente
Es extraño cómo todos se apresuran a actualizar sin molestarse en leer las notas de la versión, que son bastante explícitas al respecto.
mate
developer.apple.com/documentation/xcode_release_notes/… y busque danling. bugs.swift.org/browse/SR-2790 parece tener una discusión más completa sobre esto.
Roy Falk

Respuestas:

3

Esto nunca fue seguro, me alegra que el equipo de Swift lo haya limpiado:

let pointer = UnsafeBufferPointer(start: &value, count: 1)

Al final de esta línea de código, pointeres inmediatamente inválido. No hay promesa de que valueincluso exista en la siguiente línea de código. No estoy seguro de lo que estaba tratando de lograr aquí, pero esta nunca fue una forma segura de hacerlo. Lo que probablemente esté buscando es uno de los .withUnsafeBytesmétodos, que depende de en qué estaba trabajando.

Rob Napier
fuente
3
Si bien su respuesta es probablemente correcta, sería mucho mejor si mostrara un ejemplo de cómo esto podría fallar. Hay algunos ejemplos ( stackoverflow.com/a/27456220/5276890 ) de conversiones y conversiones utilizando el puntero inseguro * flotante que ahora genera esta advertencia.
Roy Falk
3

Tenía un código que parecía casi exactamente lo que estabas haciendo y recibía la misma advertencia. La mía difería ligeramente de una manera que es relevante para la discusión

init<T>(from value: T) {
    var value = value
    self.init(buffer: UnsafeBufferPointer(start: &value, count: 1))
}

Esto todavía genera la advertencia de que UnsafeBufferPointer está produciendo un puntero colgante, pero las sugerencias dicen "produce un puntero válido solo durante la duración de la llamada a 'init (start: count :)'"

Pero el retorno de UnsafeBufferPointer no está asignado a nada, por lo que no podría usarlo fuera del alcance del init si lo intentara. Entonces el compilador aquí me advierte que no haga algo que no puedo hacer de todos modos.

Supongo que Data.init (buffer:) podría estar almacenando el ptr, pero supongo que si acepta un UnsafeBufferPointer, está aceptando la responsabilidad de usarlo correctamente

De todos modos, eso todavía no soluciona tu problema. Eludí la advertencia con esto

init<T>(from value: T) {
    var value = value
    var myData = Data()
    withUnsafePointer(to:&value, { (ptr: UnsafePointer<T>) -> Void in
        myData = Data( buffer: UnsafeBufferPointer(start: ptr, count: 1))
    })
    self.init(myData)
}

Y esto no genera la advertencia y parece funcionar (en mi aplicación de todos modos). Si pasa reunión con los expertos aquí es otra cuestión.

Algo así me pone nostálgico por los días de HLock y HUnlock

Greg
fuente
3

También me encontré con estas advertencias molestas.

var str = "aaaaabbbbbccccc"
var num1 = 1
var num2 = 22

var data = Data()
// Initialization of 'UnsafeBufferPointer<String>' results in a dangling buffer pointer
data.append(UnsafeBufferPointer(start: &str, count: 1)) 
// Initialization of 'UnsafeBufferPointer<Int>' results in a dangling buffer pointer
data.append(UnsafeBufferPointer(start: &num1, count: 1))
// Initialization of 'UnsafeBufferPointer<Int>' results in a dangling buffer pointer 
data.append(UnsafeBufferPointer(start: &num2, count: 1)) 

Teniendo en cuenta la respuesta de @ Greg, puse el Data.appenden withUnsafePointerel cierre 's, y no muestra advertencias más.

withUnsafePointer(to: &str) { data.append(UnsafeBufferPointer(start: $0, count: 1)) } // ok
withUnsafePointer(to: &num1) { data.append(UnsafeBufferPointer(start: $0, count: 1)) } // ok
withUnsafePointer(to: &num2) { data.append(UnsafeBufferPointer(start: $0, count: 1)) } // ok

Aquí esta la extensión

extension Data {
    init<T>(value: T) {
        self = withUnsafePointer(to: value) { (ptr: UnsafePointer<T>) -> Data in
            return Data(buffer: UnsafeBufferPointer(start: ptr, count: 1))
        }
    }

    mutating func append<T>(value: T) {
        withUnsafePointer(to: value) { (ptr: UnsafePointer<T>) in
            append(UnsafeBufferPointer(start: ptr, count: 1))
        }
    }
}
Chen OT
fuente
SECOappend(.init(value: value))
Leo Dabus