settitle xcode swift

xcode - settitle - set title swift



¿Búsqueda de matriz segura(verificada por límites) en Swift, a través de enlaces opcionales? (11)

Si tengo una matriz en Swift, y trato de acceder a un índice que está fuera de los límites, hay un error de tiempo de ejecución sorprendente:

var str = ["Apple", "Banana", "Coconut"] str[0] // "Apple" str[3] // EXC_BAD_INSTRUCTION

Sin embargo, hubiera pensado con todo el encadenamiento y la seguridad opcionales que Swift trae, sería trivial hacer algo como:

let theIndex = 3 if let nonexistent = str[theIndex] { // Bounds check + Lookup print(nonexistent) ...do other things with nonexistent... }

En lugar de:

let theIndex = 3 if (theIndex < str.count) { // Bounds check let nonexistent = str[theIndex] // Lookup print(nonexistent) ...do other things with nonexistent... }

Pero este no es el caso, tengo que usar la sentencia ol '' if para verificar y asegurar que el índice sea menor que str.count .

Intenté agregar mi propia implementación de subscript() , pero no estoy seguro de cómo pasar la llamada a la implementación original, o de acceder a los elementos (basados ​​en índices) sin usar la notación de subíndices:

extension Array { subscript(var index: Int) -> AnyObject? { if index >= self.count { NSLog("Womp!") return nil } return ... // What? } }


Swift 4

Una extensión para aquellos que prefieren una sintaxis más tradicional:

extension Array where Element: Equatable { func object(at index: Int) -> Element? { return indices.contains(index) ? self[index] : nil } }


Válido en Swift 2

A pesar de que esto ya ha sido respondido muchas veces, me gustaría presentar una respuesta más acorde a dónde va la moda de la programación Swift, que en palabras de Crusty es: "Piensa en el protocol primero"

• ¿Qué queremos hacer?
- Obtener un elemento de una Array dado un índice solo cuando sea seguro, y nil contrario
• ¿En qué debe basar esta funcionalidad su implementación?
- Array subscript ing
• ¿De dónde obtiene esta característica?
- Su definición de struct Array en el módulo Swift tiene.
• ¿Nada más genérico / abstracto?
- Adopta el protocol CollectionType que lo asegura también.
• ¿Nada más genérico / abstracto?
- Adopta protocol Indexable también ...
• Sí, suena como lo mejor que podemos hacer. ¿Podemos luego extenderlo para tener esta característica que queremos?
- ¡ Pero tenemos tipos muy limitados (sin Int ) y propiedades (sin count ) con los que trabajar ahora!
• Será suficiente. El stdlib de Swift se hace bastante bien;)

extension Indexable { public subscript(safe safeIndex: Index) -> _Element? { return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil } }

¹: no es cierto, pero da la idea


Creo que esta no es una buena idea. Parece preferible construir código sólido que no resulte en tratar de aplicar índices fuera de los límites.

Tenga en cuenta que si se produce un error de este tipo falla silenciosamente (como lo sugiere su código anterior) al devolver nil es propenso a producir errores aún más complejos e intratables.

Podría hacer su anulación de la misma manera que usó y simplemente escribir los subíndices a su manera. El único inconveniente es que el código existente no será compatible. Creo que encontrar un enlace para anular la x [i] genérica (también sin un preprocesador de texto como en C) será un desafío.

Lo más cercano que puedo pensar es

// compile error: if theIndex < str.count && let existing = str[theIndex]

EDIT : Esto realmente funciona. ¡¡Un trazador de líneas!!

func ifInBounds(array: [AnyObject], idx: Int) -> AnyObject? { return idx < array.count ? array[idx] : nil } if let x: AnyObject = ifInBounds(swiftarray, 3) { println(x) } else { println("Out of bounds") }


Encontré la matriz segura obtener, configurar, insertar, eliminar, muy útil. Prefiero registrar e ignorar los errores, ya que todo lo demás pronto se vuelve difícil de manejar. Código completo abajo

/** Safe array get, set, insert and delete. All action that would cause an error are ignored. */ extension Array { /** Removes element at index. Action that would cause an error are ignored. */ mutating func remove(safeAt index: Index) { guard index >= 0 && index < count else { print("Index out of bounds while deleting item at index /(index) in /(self). This action is ignored.") return } remove(at: index) } /** Inserts element at index. Action that would cause an error are ignored. */ mutating func insert(_ element: Element, safeAt index: Index) { guard index >= 0 && index <= count else { print("Index out of bounds while inserting item at index /(index) in /(self). This action is ignored") return } insert(element, at: index) } /** Safe get set subscript. Action that would cause an error are ignored. */ subscript (safe index: Index) -> Element? { get { return indices.contains(index) ? self[index] : nil } set { remove(safeAt: index) if let element = newValue { insert(element, safeAt: index) } } } }

Pruebas

import XCTest class SafeArrayTest: XCTestCase { func testRemove_Successful() { var array = [1, 2, 3] array.remove(safeAt: 1) XCTAssert(array == [1, 3]) } func testRemove_Failure() { var array = [1, 2, 3] array.remove(safeAt: 3) XCTAssert(array == [1, 2, 3]) } func testInsert_Successful() { var array = [1, 2, 3] array.insert(4, safeAt: 1) XCTAssert(array == [1, 4, 2, 3]) } func testInsert_Successful_AtEnd() { var array = [1, 2, 3] array.insert(4, safeAt: 3) XCTAssert(array == [1, 2, 3, 4]) } func testInsert_Failure() { var array = [1, 2, 3] array.insert(4, safeAt: 5) XCTAssert(array == [1, 2, 3]) } func testGet_Successful() { var array = [1, 2, 3] let element = array[safe: 1] XCTAssert(element == 2) } func testGet_Failure() { var array = [1, 2, 3] let element = array[safe: 4] XCTAssert(element == nil) } func testSet_Successful() { var array = [1, 2, 3] array[safe: 1] = 4 XCTAssert(array == [1, 4, 3]) } func testSet_Successful_AtEnd() { var array = [1, 2, 3] array[safe: 3] = 4 XCTAssert(array == [1, 2, 3, 4]) } func testSet_Failure() { var array = [1, 2, 3] array[safe: 4] = 4 XCTAssert(array == [1, 2, 3]) } }


He rellenado la matriz con nil s en mi caso de uso:

let components = [1, 2] var nilComponents = components.map { $0 as Int? } nilComponents += [nil, nil, nil] switch (nilComponents[0], nilComponents[1], nilComponents[2]) { case (_, _, .Some(5)): // process last component with 5 default: break }

También verifique la extensión del subíndice con safe: etiqueta de Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/


Para aprovechar la respuesta de Nikita Kukushkin, a veces es necesario asignar de forma segura a los índices de matriz, así como leer de ellos, es decir

myArray[safe: badIndex] = newValue

Así que aquí hay una actualización de la respuesta de Nikita (Swift 3.2) que también permite escribir de forma segura en índices de matriz mutables, agregando el nombre de parámetro safe:.

extension Collection { /// Returns the element at the specified index iff it is within bounds, otherwise nil. subscript(safe index: Index) -> Element? { return indices.contains(index) ? self[ index] : nil } } extension MutableCollection { subscript(safe index: Index) -> Element? { get { return indices.contains(index) ? self[ index] : nil } set(newValue) { if let newValue = newValue, indices.contains(index) { self[ index] = newValue } } } }


Si realmente desea este comportamiento, huele a que quiere un Diccionario en lugar de un Array. Los diccionarios devuelven nil cuando acceden a las claves faltantes, lo que tiene sentido porque es mucho más difícil saber si una clave está presente en un diccionario, ya que esas claves pueden ser cualquier cosa, donde en una matriz la clave debe estar dentro de un rango de: 0 para count . Y es increíblemente común repetir en este rango, donde puede estar absolutamente seguro de tener un valor real en cada iteración de un bucle.

Creo que la razón por la que no funciona de esta manera es una elección de diseño hecha por los desarrolladores de Swift. Toma tu ejemplo:

var fruits: [String] = ["Apple", "Banana", "Coconut"] var str: String = "I ate a /( fruits[0] )"

Si ya sabe que el índice existe, como lo hace en la mayoría de los casos en los que utiliza una matriz, este código es excelente. Sin embargo, si el acceso a un subíndice puede devolver nil entonces ha cambiado para que el tipo de método de subscript de Array sea ​​opcional. Esto cambia su código a:

var fruits: [String] = ["Apple", "Banana", "Coconut"] var str: String = "I ate a /( fruits[0]! )" // ^ Added

Lo que significa que tendría que desempaquetar un opcional cada vez que iterara a través de una matriz, o hiciera cualquier otra cosa con un índice conocido, solo porque rara vez podría acceder a un índice fuera de límites. Los diseñadores de Swift optaron por un menor desempaque de opciones, a expensas de una excepción de tiempo de ejecución al acceder a índices fuera de límites. Y es preferible un bloqueo que un error lógico causado por un error nil que no esperaba en sus datos en algún lugar.

Y estoy de acuerdo con ellos. Por lo tanto, no cambiará la implementación predeterminada de Array porque rompería todo el código que espera valores no opcionales de los arrays.

En su lugar, podría Array subclase de Array y anular el subscript para devolver un opcional. O, más prácticamente, podría extender Array con un método sin subíndices que haga esto.

extension Array { // Safely lookup an index that might be out of bounds, // returning nil if it does not exist func get(index: Int) -> T? { if 0 <= index && index < count { return self[index] } else { return nil } } } var fruits: [String] = ["Apple", "Banana", "Coconut"] if let fruit = fruits.get(1) { print("I ate a /( fruit )") // I ate a Banana } if let fruit = fruits.get(3) { print("I ate a /( fruit )") // never runs, get returned nil }

Actualización de Swift 3

func get(index: Int) -> T? necesita ser reemplazado por func get(index: Int) -> Element?


La respuesta de Alex tiene un buen consejo y una solución para la pregunta, sin embargo, me he topado con una mejor manera de implementar esta funcionalidad:

Swift 3.2 y posteriores

extension Collection { /// Returns the element at the specified index if it is within bounds, otherwise nil. subscript (safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } }

Swift 3.0 y 3.1

extension Collection where Indices.Iterator.Element == Index { /// Returns the element at the specified index if it is within bounds, otherwise nil. subscript (safe index: Index) -> Generator.Element? { return indices.contains(index) ? self[index] : nil } }

Gracias a Hamish por encontrar la solución para Swift 3 .

Swift 2

extension CollectionType { /// Returns the element at the specified index if it is within bounds, otherwise nil. subscript (safe index: Index) -> Generator.Element? { return indices.contains(index) ? self[index] : nil } }

Ejemplo

let array = [1, 2, 3] for index in -20...20 { if let item = array[safe: index] { print(item) } }


extension Array { subscript (safe index: Index) -> Element? { return 0 <= index && index < count ? self[index] : nil } }

  • O (1) rendimiento
  • tipo seguro
  • trata correctamente con los opcionales para [MyType?] (devuelve MyType ??, que se puede desempaquetar en ambos niveles)
  • No conduce a problemas para los Sets.
  • código conciso

Aquí hay algunas pruebas que corrí para usted:

let itms: [Int?] = [0, nil] let a = itms[safe: 0] // 0 : Int?? a ?? 5 // 0 : Int? let b = itms[safe: 1] // nil : Int?? b ?? 5 // nil : Int? let c = itms[safe: 2] // nil : Int?? c ?? 5 // 5 : Int?


extension Array { subscript (safe index: UInt) -> Element? { return Int(index) < count ? self[Int(index)] : nil } }

Usando la mención anterior, la extensión devuelve nil si en cualquier momento el índice se sale del límite.

let fruits = ["apple","banana"] print("result-/(fruits[safe : 2])")

resultado - nil


  • Debido a que las matrices pueden almacenar valores nulos, no tiene sentido devolver un valor nulo si una llamada a la matriz [índice] está fuera de los límites.
  • Debido a que no sabemos cómo le gustaría a un usuario manejar problemas fuera de los límites, no tiene sentido usar operadores personalizados.
  • Por el contrario, use el flujo de control tradicional para desenvolver objetos y garantizar la seguridad del tipo.

si se deja index = array.checkIndexForSafety (index: Int)

let item = array[safeIndex: index]

si se deja index = array.checkIndexForSafety (index: Int)

array[safeIndex: safeIndex] = myObject

extension Array { @warn_unused_result public func checkIndexForSafety(index: Int) -> SafeIndex? { if indices.contains(index) { // wrap index number in object, so can ensure type safety return SafeIndex(indexNumber: index) } else { return nil } } subscript(index:SafeIndex) -> Element { get { return self[index.indexNumber] } set { self[index.indexNumber] = newValue } } // second version of same subscript, but with different method signature, allowing user to highlight using safe index subscript(safeIndex index:SafeIndex) -> Element { get { return self[index.indexNumber] } set { self[index.indexNumber] = newValue } } } public class SafeIndex { var indexNumber:Int init(indexNumber:Int){ self.indexNumber = indexNumber } }