Qual é o equivalente Swift de - [NSObject description]?

163

No Objective-C, pode-se adicionar um descriptionmétodo à sua classe para ajudar na depuração:

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

Em seguida, no depurador, você pode:

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

Qual é o equivalente em Swift? A saída REPL da Swift pode ser útil:

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

Mas eu gostaria de substituir esse comportamento para imprimir no console:

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

Existe uma maneira de limpar essa printlnsaída? Eu vi o 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 }
}

Imaginei que isso seria automaticamente "visto" por, printlnmas não parece ser o 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)

E, em vez disso, tenho que chamar explicitamente a descrição:

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

Existe uma maneira melhor?

Jason
fonte

Respostas:

124

Para implementar isso em um tipo Swift, você deve implementar o CustomStringConvertibleprotocolo e também implementar uma propriedade de cadeia de caracteres chamada description.

Por exemplo:

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)obtém o tipo das instâncias atuais em vez de escrever explicitamente 'MyClass'.

drewag
fonte
3
Ótima descoberta! Vou arquivar um radar - a saída println de "swift -i sample.swift" e "swift sample.swift && sample" diferem.
Jason
Obrigado pela informação sobre isso. Eu estava experimentando o Printable em um parquinho e, de fato, não funciona no momento. É bom ouvir que funciona em um aplicativo.
Tod Cunningham
O Printable imprimível funciona no playground, mas se a classe for descendente de NSObject
dar512
5
Em Swift 2,0 mudou para CustomStringConvertible e CustomDebugStringConvertible
Mike Vosseller
Além disso, não há nenhum problema usando CustomStringConvertible e CustomDebugStringConvertible no campo de jogos com o Xcode 7.2
Nicholas Credli
54

Exemplo de uso CustomStringConvertiblee CustomDebugStringConvertibleprotocolos no 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)
    }

          ...
}

Que imprimir:

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

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

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

Nota: se você tiver uma classe personalizada que não herdar de qualquer classe incluído no UIKit ou Fundação bibliotecas, em seguida, torná-lo herdar de NSObjectclasse ou torná-lo em conformidade com CustomStringConvertiblee CustomDebugStringConvertibleprotocolos.

O rei da bruxaria
fonte
a função deve ser declarada pública
Karsten
35

Basta usar CustomStringConvertibleevar description: String { return "Some string" }

funciona no 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
fonte
20

As respostas relacionadas CustomStringConvertiblesão o caminho a percorrer. Pessoalmente, para manter a definição de classe (ou estrutura) o mais limpa possível, eu também separaria o código de descrição em uma extensão 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
fonte
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
fonte
6

Conforme descrito aqui , você também pode usar os recursos de reflexão do Swift para fazer com que suas classes gerem sua própria descrição usando esta extensão:

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
fonte
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
fonte