Jaki jest odpowiednik - [NSObject description] w języku Swift?

163

W Objective-C można dodać descriptionmetodę do swojej klasy, aby ułatwić debugowanie:

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

Następnie w debugerze możesz wykonać:

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

Jaki jest odpowiednik w Swift? Wyjście REPL w Swift może być pomocne:

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

Ale chciałbym zmienić to zachowanie przy drukowaniu na konsolę:

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

Czy istnieje sposób na wyczyszczenie tego printlnwyniku? Widziałem Printableprotokół:

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

Pomyślałem, że zostanie to automatycznie „zauważone”, printlnale nie wydaje się, aby tak było:

  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)

Zamiast tego muszę wyraźnie wywołać opis:

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

Czy jest lepszy sposób?

Jason
źródło

Odpowiedzi:

124

Aby zaimplementować to w typie Swift, należy zaimplementować CustomStringConvertibleprotokół, a następnie zaimplementować również właściwość ciągu o nazwie description.

Na przykład:

class MyClass: CustomStringConvertible {
    let foo = 42

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

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

Uwaga: type(of: self)pobiera typ bieżących instancji zamiast jawnie pisać „MyClass”.

drewag
źródło
3
Świetne znalezisko! Mam zamiar złożyć radar - println wyniki "swift -i sample.swift" i "swift sample.swift && sample" różnią się.
Jason
Dzięki za informację na ten temat. Próbowałem wydrukować na placu zabaw i rzeczywiście teraz nie działa. Dobrze, że działa w aplikacji.
Tod Cunningham
Druku działa na placu zabaw, ale wtedy i tylko wtedy klasa schodzi z NSObject
dar512
5
W Swift 2.0 zmieniono to na CustomStringConvertible i CustomDebugStringConvertible
Mike Vosseller
Ponadto nie ma problemu z używaniem CustomStringConvertible i CustomDebugStringConvertible w Playground z Xcode 7.2
Nicholas Credli,
54

Przykład użycia CustomStringConvertiblei CustomDebugStringConvertibleprotokołów w 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)
    }

          ...
}

Który wydruk:

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

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

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

Uwaga: jeśli masz niestandardową klasę, która nie dziedziczy z żadnej klasy zawartej w bibliotekach UIKit lub Foundation , spraw , aby dziedziczała po NSObjectklasie lub dostosuj ją do protokołów CustomStringConvertiblei CustomDebugStringConvertible.

King-Wizard
źródło
funkcja musi być zadeklarowana jako publiczna
Karsten
35

Po prostu użyj CustomStringConvertibleivar description: String { return "Some string" }

działa w 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
źródło
20

Odpowiedzi odnoszące się do tego CustomStringConvertiblesą drogą do zrobienia. Osobiście, aby zachować definicję klasy (lub struktury) tak czystą, jak to tylko możliwe, wydzieliłbym również kod opisu na osobne rozszerzenie:

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
źródło
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
źródło
6

Jak opisano tutaj , możesz również użyć funkcji odbicia Swift, aby Twoje klasy generowały własny opis za pomocą tego rozszerzenia:

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
źródło
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
źródło