¿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?

Jason
fuente

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'.

Drewag
fuente
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.

Rey Mago
fuente
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))"
}
Peter Ahlberg
fuente
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!"
Vince O'Sullivan
fuente
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
Peter Ahlberg
fuente
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
    }
}
Sir Codesalot
fuente
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
neoneye
fuente