¿Cuál es el equivalente rápido de - [Descripción de NSObject]?


163

En Objective-C, uno puede agregar un descriptionmétodo a su clase para ayudar en la depuración:

@implementation MyClass
- (NSString *)description
{
    return [NSString stringWithFormat:@"<%@: %p, foo = %@>", [self class], foo _foo];
}
@end

Luego, en el depurador, puede hacer:

po fooClass
<MyClass: 0x12938004, foo = "bar">

¿Cuál es el equivalente en Swift? La salida REPL de Swift puede ser útil:

  1> class MyClass { let foo = 42 }
  2> 
  3> let x = MyClass()
x: MyClass = {
  foo = 42
}

Pero me gustaría anular este comportamiento para imprimir en la consola:

  4> println("x = \(x)")
x = C11lldb_expr_07MyClass (has 1 child)

¿Hay alguna manera de limpiar esta printlnsalida? He visto el Printableprotocolo:

/// This protocol should be adopted by types that wish to customize their
/// textual representation.  This textual representation is used when objects
/// are written to an `OutputStream`.
protocol Printable {
    var description: String { get }
}

Pensé que esto sería "visto" automáticamente, printlnpero no parece ser el caso:

  1> class MyClass: Printable {
  2.     let foo = 42
  3.     var description: String { get { return "MyClass, foo = \(foo)" } }
  4. }   
  5> 
  6> let x = MyClass()
x: MyClass = {
  foo = 42
}
  7> println("x = \(x)")
x = C11lldb_expr_07MyClass (has 1 child)

Y en su lugar, tengo que llamar explícitamente a description:

 8> println("x = \(x.description)")
x = MyClass, foo = 42

¿Hay una mejor manera?

Respuestas:


124

Para implementar esto en un tipo Swift, debe implementar el CustomStringConvertibleprotocolo y luego también implementar una propiedad de cadena llamada description.

Por ejemplo:

class MyClass: CustomStringConvertible {
    let foo = 42

    var description: String {
        return "<\(type(of: self)): foo = \(foo)>"
    }
}

print(MyClass()) // prints: <MyClass: foo = 42>

Nota: type(of: self)obtiene el tipo de las instancias actuales en lugar de escribir explícitamente 'MyClass'.


3
Gran descubrimiento! Voy a archivar un radar: la salida println de "swift -i sample.swift" y "swift sample.swift && sample" difieren.
Jason

Gracias por esa información. Estaba probando Printable en un parque infantil y, de hecho, no funciona en este momento. Bueno, oye que funciona en una aplicación.
Tod Cunningham

Imprimible funciona en el patio de recreo, pero si la clase desciende de NSObject
dar512

55
En Swift 2.0 ha cambiado a CustomStringConvertible y CustomDebugStringConvertible
Mike Vosseller el

Además, no hay ningún problema al usar CustomStringConvertible y CustomDebugStringConvertible en Playground con Xcode 7.2
Nicholas Credli

54

Ejemplo de uso CustomStringConvertibley CustomDebugStringConvertibleprotocolos en Swift:

PageContentViewController.swift

import UIKit

class PageContentViewController: UIViewController {

    var pageIndex : Int = 0

    override var description : String { 
        return "**** PageContentViewController\npageIndex equals \(pageIndex) ****\n" 
    }

    override var debugDescription : String { 
        return "---- PageContentViewController\npageIndex equals \(pageIndex) ----\n" 
    }

            ...
}

ViewController.swift

import UIKit

class ViewController: UIViewController
{

    /*
        Called after the controller's view is loaded into memory.
    */
    override func viewDidLoad() {
        super.viewDidLoad()

        let myPageContentViewController = self.storyboard!.instantiateViewControllerWithIdentifier("A") as! PageContentViewController
        print(myPageContentViewController)       
        print(myPageContentViewController.description)
        print(myPageContentViewController.debugDescription)
    }

          ...
}

Qué impresión:

**** PageContentViewController
pageIndex equals 0 ****

**** PageContentViewController
pageIndex equals 0 ****

---- PageContentViewController
pageIndex equals 0 ----

Nota: si tiene una clase personalizada que no hereda de ninguna clase incluido en UIKit o Fundación bibliotecas, a continuación, hacer que hereda de la NSObjectclase o que sea conforme con CustomStringConvertibley CustomDebugStringConvertibleprotocolos.


la función debe declararse como pública
Karsten

35

Solo usa CustomStringConvertibleyvar description: String { return "Some string" }

funciona en Xcode 7.0 beta

class MyClass: CustomStringConvertible {
  var string: String?


  var description: String {
     //return "MyClass \(string)"
     return "\(self.dynamicType)"
  }
}

var myClass = MyClass()  // this line outputs MyClass nil

// and of course 
print("\(myClass)")

// Use this newer versions of Xcode
var description: String {
    //return "MyClass \(string)"
    return "\(type(of: self))"
}

20

Las respuestas relacionadas CustomStringConvertibleson el camino a seguir. Personalmente, para mantener la definición de clase (o estructura) lo más limpia posible, también separaría el código de descripción en una extensión separada:

class foo {
    // Just the basic foo class stuff.
    var bar = "Humbug!"
}

extension foo: CustomStringConvertible {
    var description: String {
        return bar
    }
}

let xmas = foo()
print(xmas)  // Prints "Humbug!"

8
class SomeBaseClass: CustomStringConvertible {

    //private var string: String = "SomeBaseClass"

    var description: String {
        return "\(self.dynamicType)"
    }

    // Use this in newer versions of Xcode
    var description: String {
        return "\(type(of: self))"
    }

}

class SomeSubClass: SomeBaseClass {
    // If needed one can override description here

}


var mySomeBaseClass = SomeBaseClass()
// Outputs SomeBaseClass
var mySomeSubClass = SomeSubClass()
// Outputs SomeSubClass
var myOtherBaseClass = SomeSubClass()
// Outputs SomeSubClass

6

Como se describe aquí , también puede usar las capacidades de reflexión de Swift para hacer que sus clases generen su propia descripción mediante el uso de esta extensión:

extension CustomStringConvertible {
    var description : String {
        var description: String = "\(type(of: self)){ "
        let selfMirror = Mirror(reflecting: self)
        for child in selfMirror.children {
            if let propertyName = child.label {
                description += "\(propertyName): \(child.value), "
            }
        }
        description = String(description.dropLast(2))
        description += " }"
        return description
    }
}

4
struct WorldPeace: CustomStringConvertible {
    let yearStart: Int
    let yearStop: Int

    var description: String {
        return "\(yearStart)-\(yearStop)"
    }
}

let wp = WorldPeace(yearStart: 2020, yearStop: 2040)
print("world peace: \(wp)")

// outputs:
// world peace: 2020-2040
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.