Swift - Encadenamiento opcional

El proceso de consultar, llamar a propiedades, subíndices y métodos en un opcional que puede ser 'nulo' se define como encadenamiento opcional. El encadenamiento opcional devuelve dos valores:

  • si el opcional contiene un 'valor', llamar a su propiedad, métodos y subíndices relacionados devuelve valores

  • si el opcional contiene un valor 'nil', todas sus propiedades, métodos y subíndices relacionados devuelven nil

Dado que varias consultas a métodos, propiedades y subíndices se agrupan, la falla en una cadena afectará a toda la cadena y dará como resultado un valor "nulo".

Encadenamiento opcional como alternativa al desenvolvimiento forzado

El encadenamiento opcional se especifica después del valor opcional con '?' para llamar a una propiedad, método o subíndice cuando el valor opcional devuelve algunos valores.

Encadenamiento opcional '?' Acceso a métodos, propiedades y subíndices. Encadenamiento opcional '!' forzar el desenvolvimiento
? se coloca después del valor opcional para llamar a la propiedad, método o subíndice ! se coloca después del valor opcional para llamar a la propiedad, método o subíndice para forzar el desenvolvimiento del valor
Falla graciosamente cuando el opcional es 'nulo' El desenvolvimiento forzado desencadena un error de tiempo de ejecución cuando el opcional es 'nil'

Programa para encadenamiento opcional con '!'

class ElectionPoll {
   var candidate: Pollbooth?
}

lass Pollbooth {
   var name = "MP"
}

let cand = ElectionPoll()
let candname = cand.candidate!.name

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

fatal error: unexpectedly found nil while unwrapping an Optional value
0 Swift 4 0x0000000103410b68
llvm::sys::PrintStackTrace(__sFILE*) + 40
1 Swift 4 0x0000000103411054 SignalHandler(int) + 452
2 libsystem_platform.dylib 0x00007fff9176af1a _sigtramp + 26
3 libsystem_platform.dylib 0x000000000000000b _sigtramp + 1854492939
4 libsystem_platform.dylib 0x00000001074a0214 _sigtramp + 1976783636
5 Swift 4 0x0000000102a85c39
llvm::JIT::runFunction(llvm::Function*, std::__1::vector > const&) + 329
6 Swift 4 0x0000000102d320b3
llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*,
std::__1::vector<std::__1::basic_string, std::__1::allocator >,
std::__1::allocator<std::__1::basic_string, std::__1::allocator > > > const&,
char const* const*) + 1523
7 Swift 4 0x000000010296e6ba Swift 4::RunImmediately(Swift
4::CompilerInstance&, std::__1::vector<std::__1::basic_string,
std::__1::allocator >, std::__1::allocator<std::__1::basic_string,
std::__1::allocator > > > const&, Swift 4::IRGenOptions&, Swift 4::SILOptions
const&) + 1066
8 Swift 4 0x000000010275764b frontend_main(llvm::ArrayRef,
char const*, void*) + 5275
9 Swift 4 0x0000000102754a6d main + 1677
10 libdyld.dylib 0x00007fff8bb9e5c9 start + 1
11 libdyld.dylib 0x000000000000000c start + 1950751300
Stack dump:
0. Program arguments:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/
usr/bin/Swift 4 -frontend -interpret - -target x86_64-apple-darwin14.0.0 -
target-cpu core2 -sdk
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/
SDKs/MacOSX10.10.sdk -module-name main
/bin/sh: line 47: 15672 Done cat <<'SWIFT 4'
import Foundation
</std::__1::basic_string</std::__1::basic_string</std::__1::basic_string</std::
__1::basic_string

El programa anterior declara 'encuesta electoral' como nombre de clase y contiene 'candidato' como función de membresía. La subclase se declara como 'cabina de votación' y 'nombre' como su función de membresía que se inicializa como 'MP'. La llamada a la superclase se inicializa creando una instancia 'cand' con opcional '!'. Dado que los valores no se declaran en su clase base, el valor 'nil' se almacena, devolviendo un error fatal por el procedimiento de desenvolver forzado.

Programa para encadenamiento opcional con '?'

class ElectionPoll {
   var candidate: Pollbooth?
}

class Pollbooth {
   var name = "MP"
}
let cand = ElectionPoll()

if let candname = cand.candidate?.name {
   print("Candidate name is \(candname)")
} else {
   print("Candidate name cannot be retreived")
}

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Candidate name cannot be retreived

El programa anterior declara 'encuesta electoral' como nombre de clase y contiene 'candidato' como función de membresía. La subclase se declara como 'cabina de votación' y 'nombre' como su función de membresía que se inicializa como 'MP'. La llamada a la superclase se inicializa creando una instancia 'cand' con opcional '?'. Dado que los valores no están declarados en su clase base, el bloque controlador else almacena e imprime en la consola.

Definición de clases de modelo para propiedades de acceso y encadenamiento opcionales

El lenguaje Swift 4 también proporciona el concepto de encadenamiento opcional, para declarar más de una subclases como clases modelo. Este concepto será muy útil para definir modelos complejos y acceder a las propiedades, métodos y subpropiedades de subíndices.

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var street: String?

   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let rectname = rectangle()
if let rectarea = rectname.print?.cprint {
   print("Area of rectangle is \(rectarea)")
} else {
   print("Rectangle Area is not specified")
}

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Rectangle Area is not specified

Métodos de llamada mediante encadenamiento opcional

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }

   func circleprint() {
      print("Area of Circle is: \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()

if circname.print?.circleprint() != nil {
   print("Area of circle is specified)")
} else {
   print("Area of circle is not specified")
}

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Area of circle is not specified

La función circleprint () declarada dentro de la subclase circle () se llama creando una instancia llamada 'circname'. La función devolverá un valor si contiene algún valor; de lo contrario, devolverá algún mensaje de impresión definido por el usuario marcando la declaración 'if circname.print? .Circleprint ()! = Nil'.

Acceso a subíndices mediante encadenamiento opcional

El encadenamiento opcional se utiliza para establecer y recuperar un valor de subíndice para validar si la llamada a ese subíndice devuelve un valor. '?' se coloca antes de las llaves del subíndice para acceder al valor opcional en el subíndice particular.

Programa 1

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname =  radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()

if let radiusName = circname.print?[0].radiusname {
   print("The first room name is \(radiusName).")
} else {
   print("Radius is not specified.")
}

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Radius is not specified.

En el programa anterior, no se especifican los valores de instancia para la función de pertenencia 'radiusName'. Por lo tanto, la llamada del programa a la función devolverá solo otra parte, mientras que para devolver los valores tenemos que definir los valores para la función de pertenencia particular.

Programa 2

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")
let printing = circle()

printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing

if let radiusName = circname.print?[0].radiusname {
   print("Radius is measured in \(radiusName).")
} else {
   print("Radius is not specified.")
}

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Radius is measured in Units.

En el programa anterior, se especifican los valores de instancia para la función de pertenencia 'radiusName'. Por lo tanto, la llamada del programa a la función ahora devolverá valores.

Acceso a subíndices de tipo opcional

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }

   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")

let printing = circle()
printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing

var area = ["Radius": [35, 45, 78, 101], "Circle": [90, 45, 56]]
area["Radius"]?[1] = 78
area["Circle"]?[1]--

print(area["Radius"]?[0])
print(area["Radius"]?[1])
print(area["Radius"]?[2])
print(area["Radius"]?[3])

print(area["Circle"]?[0])
print(area["Circle"]?[1])
print(area["Circle"]?[2])

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Optional(35)
Optional(78)
Optional(78)
Optional(101)
Optional(90)
Optional(44)
Optional(56)

Se puede acceder a los valores opcionales de los subíndices haciendo referencia a sus valores de subíndice. Se puede acceder a él como subíndice [0], subíndice [1], etc. Los valores predeterminados del subíndice para 'radio' se asignan primero como [35, 45, 78, 101] y para 'Círculo' [90, 45, 56]] . Luego, los valores del subíndice se cambian como Radio [0] a 78 y Círculo [1] a 45.

Vinculación de múltiples niveles de encadenamiento

También se pueden vincular múltiples subclases con sus métodos de superclase, propiedades y subíndices mediante encadenamiento opcional.

Se pueden vincular múltiples encadenamientos de opcionales -

Si recuperar el tipo no es opcional, el encadenamiento opcional devolverá un valor opcional. Por ejemplo, si String a través del encadenamiento opcional, devolverá String? Valor

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?

   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()

if let radiusName = circname.print?[0].radiusname {
   print("The first room name is \(radiusName).")
} else {
   print("Radius is not specified.")
}

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Radius is not specified.

En el programa anterior, no se especifican los valores de instancia para la función de pertenencia 'radiusName'. Por lo tanto, la llamada del programa a la función devolverá solo otra parte, mientras que para devolver los valores tenemos que definir los valores para la función de pertenencia particular.

Si el tipo de recuperación ya es opcional, el encadenamiento opcional también devolverá un valor opcional. Por ejemplo, si String? ¿Se accede a través del encadenamiento opcional y devolverá String? Valor..

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")
let printing = circle()

printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing

if let radiusName = circname.print?[0].radiusname {
   print("Radius is measured in \(radiusName).")
} else {
   print("Radius is not specified.")
}

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Radius is measured in Units.

En el programa anterior, se especifican los valores de instancia para la función de pertenencia 'radiusName'. Por lo tanto, la llamada del programa a la función ahora devolverá valores.

Encadenamiento de métodos con valores de retorno opcionales

El encadenamiento opcional también se utiliza para acceder a métodos definidos por subclases.

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("Area of Circle is: \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()

if circname.print?.circleprint() != nil {
   print("Area of circle is specified)")
} else {
   print("Area of circle is not specified")
}

Cuando ejecutamos el programa anterior usando el patio de recreo, obtenemos el siguiente resultado:

Area of circle is not specified