Was ist das Swift-Äquivalent von - [NSObject description]?

163

In Objective-C können Sie descriptionihrer Klasse eine Methode hinzufügen , um das Debuggen zu erleichtern:

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

Dann können Sie im Debugger Folgendes tun:

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

Was ist das Äquivalent in Swift? Die REPL-Ausgabe von Swift kann hilfreich sein:

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

Aber ich möchte dieses Verhalten beim Drucken auf der Konsole überschreiben:

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

Gibt es eine Möglichkeit, diese printlnAusgabe zu bereinigen ? Ich habe das PrintableProtokoll gesehen:

/// 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 }
}

Ich dachte, dies würde automatisch von "gesehen" werden, printlnaber es scheint nicht der Fall zu sein:

  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)

Und stattdessen muss ich die Beschreibung explizit aufrufen:

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

Gibt es einen besseren Weg?

Jason
quelle

Antworten:

124

Um dies auf einem Swift-Typ zu implementieren, müssen Sie das CustomStringConvertibleProtokoll implementieren und anschließend auch eine aufgerufene Zeichenfolgeeigenschaft implementieren description.

Beispielsweise:

class MyClass: CustomStringConvertible {
    let foo = 42

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

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

Hinweis: type(of: self)Ruft den Typ der aktuellen Instanzen ab, anstatt explizit 'MyClass' zu schreiben.

Drawag
quelle
3
Toller Fund! Ich werde ein Radar einreichen - Druckausgabe von "swift -i sample.swift" und "swift sample.swift && sample" unterscheiden sich.
Jason
Danke für die Infos dazu. Ich habe Printable auf einem Spielplatz ausprobiert und tatsächlich funktioniert es momentan nicht. Gut, dass es in einer App funktioniert.
Tod Cunningham
Printable funktioniert auf dem Spielplatz, aber wenn die Klasse von NSObject abstammt
dar512
5
In Swift 2.0 wurde es in CustomStringConvertible und CustomDebugStringConvertible geändert
Mike Vosseller
Es gibt auch kein Problem bei der Verwendung von CustomStringConvertible und CustomDebugStringConvertible auf dem Spielplatz mit Xcode 7.2
Nicholas Credli
54

Anwendungsbeispiel CustomStringConvertibleund CustomDebugStringConvertibleProtokolle in 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)
    }

          ...
}

Welcher Ausdruck:

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

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

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

Hinweis: Wenn Sie eine benutzerdefinierte Klasse haben, die von keiner in UIKit- oder Foundation- Bibliotheken enthaltenen NSObjectKlasse erbt, erben Sie sie von der Klasse oder passen Sie sie an CustomStringConvertibleund CustomDebugStringConvertibleProtokolle an.

König-Zauberer
quelle
Die Funktion muss als öffentlich deklariert werden
Karsten
35

Verwenden Sie einfach CustomStringConvertibleundvar description: String { return "Some string" }

funktioniert in 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
quelle
20

Die Antworten in Bezug auf CustomStringConvertiblesind der richtige Weg. Um die Definition der Klasse (oder Struktur) so sauber wie möglich zu halten, würde ich den Beschreibungscode auch in eine separate Erweiterung aufteilen:

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
quelle
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
quelle
6

Wie hier beschrieben , können Sie auch die Reflexionsfunktionen von Swift verwenden, damit Ihre Klassen mithilfe dieser Erweiterung eine eigene Beschreibung generieren:

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