Quel est l'équivalent Swift de - [Description NSObject]?

163

En Objective-C, on peut ajouter une descriptionméthode à leur classe pour aider au débogage:

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

Ensuite, dans le débogueur, vous pouvez faire:

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

Quel est l'équivalent en Swift? La sortie REPL de Swift peut être utile:

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

Mais j'aimerais remplacer ce comportement pour l'impression sur la console:

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

Existe-t-il un moyen de nettoyer cette printlnsortie? J'ai vu le Printableprotocole:

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

J'ai pensé que cela serait automatiquement "vu" par printlnmais cela ne semble pas être le cas:

  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)

Et à la place, je dois appeler explicitement description:

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

Y a-t-il un meilleur moyen?

Jason
la source

Réponses:

124

Pour implémenter cela sur un type Swift, vous devez implémenter le CustomStringConvertibleprotocole, puis également implémenter une propriété de chaîne appelée description.

Par exemple:

class MyClass: CustomStringConvertible {
    let foo = 42

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

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

Remarque: type(of: self)obtient le type des instances actuelles au lieu d'écrire explicitement «MyClass».

dessiner
la source
3
Super trouvaille! Je vais déposer un radar - la sortie println de "swift -i sample.swift" et "swift sample.swift && sample" diffèrent.
Jason
Merci pour l'information à ce sujet. J'essayais Printable dans une cour de récréation et en effet cela ne fonctionne pas pour le moment. Bien entendu, cela fonctionne dans une application.
Tod Cunningham
Printable fonctionne dans la cour de récréation, mais si la classe descend de NSObject
dar512
5
Dans Swift 2.0, il est devenu CustomStringConvertible et CustomDebugStringConvertible
Mike Vosseller
En outre, il n'y a pas de problème avec CustomStringConvertible et CustomDebugStringConvertible dans Playground avec Xcode 7.2
Nicholas Credli
54

Exemple d'utilisation CustomStringConvertibleet de CustomDebugStringConvertibleprotocoles dans 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)
    }

          ...
}

Quelle impression:

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

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

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

Remarque: si vous avez une classe personnalisée qui n'hérite d'aucune classe incluse dans les bibliothèques UIKit ou Foundation , faites-la hériter de la NSObjectclasse ou rendez-la conforme aux protocoles CustomStringConvertibleet CustomDebugStringConvertible.

Roi-sorcier
la source
la fonction doit être déclarée publique
Karsten
35

Il suffit d'utiliser CustomStringConvertibleetvar description: String { return "Some string" }

fonctionne dans 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
la source
20

Les réponses concernant CustomStringConvertiblesont la voie à suivre. Personnellement, pour garder la définition de classe (ou struct) aussi propre que possible, je séparerais également le code de description dans une extension distincte:

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
la source
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
la source
6

Comme décrit ici , vous pouvez également utiliser les capacités de réflexion de Swift pour que vos classes génèrent leur propre description en utilisant cette extension:

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
la source
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
la source