Chciałbym globalnie zignorować wszystkie println()
wywołania w moim kodzie Swift, jeśli nie jestem w kompilacji debugowania. Nie mogę znaleźć żadnych szczegółowych instrukcji krok po kroku i byłbym wdzięczny za wskazówki. czy jest jakiś sposób, aby to zrobić globalnie, albo muszę otaczać każdy println()
ze #IF DEBUG/#ENDIF
stwierdzeniami?
84
Odpowiedzi:
Najprostszym sposobem jest umieszczenie własnej funkcji globalnej przed Swift
println
:func println(object: Any) { Swift.println(object) }
Kiedy nadejdzie czas, aby zakończyć rejestrowanie, po prostu zakomentuj treść tej funkcji:
func println(object: Any) { // Swift.println(object) }
Lub możesz zrobić to automatycznie, używając warunku:
func println(object: Any) { #if DEBUG Swift.println(object) #endif }
EDYCJA W Swift 2.0
println
zostaje zmieniona naprint
. Niestety ma teraz zmienny pierwszy parametr; to jest fajne, ale oznacza to, że nie można go łatwo zastąpić, ponieważ Swift nie ma operatora „splat”, więc nie można przekazać kodu wariadycznego (można go utworzyć tylko dosłownie). Ale możesz stworzyć zredukowaną wersję, która działa, jeśli, jak to zwykle bywa, drukujesz tylko jedną wartość:func print(items: Any..., separator: String = " ", terminator: String = "\n") { Swift.print(items[0], separator:separator, terminator: terminator) }
W Swift 3 musisz pominąć zewnętrzną etykietę pierwszego parametru:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { Swift.print(items[0], separator:separator, terminator: terminator) }
źródło
println()
nie jest wykonywany w trybie wydania.println
zmienioną naprint
. Powodem, dla którego to nie działa, jest to, że Twojaprint
definicja nie pasuje do języka Swift, więc jej nie zastępujesz. Występuje niewielki problem, ponieważ, jak wielokrotnie zauważano, Swift nie ma operatora splat, więc nie możesz przejść wariadyki. Ale działa dobrze w przypadku jednego elementu, który możesz przekazać jakoitems[0]
.Zaktualizowano dla Swift 4.x:
Ponieważ Swift 2.0 / 3.0 i Xcode 7/8 są teraz poza wersją beta, nastąpiły pewne zmiany w sposobie wyłączania funkcji drukowania w kompilacjach wydań.
Jest kilka ważnych punktów wymienionych powyżej przez @matt i @Nate Birkholz, które są nadal aktualne.
println()
Funkcja została zastąpiona przezprint()
Aby użyć
#if DEBUG
makra, musisz zdefiniować "Swift Compiler - Custom Flags-Other Flags", aby zawierał wartość-D DEBUG
Zalecałbym przesłonięcie
Swift.print()
funkcji w zakresie globalnym, aby można było używaćprint()
funkcji w normalny sposób w kodzie, ale spowoduje to usunięcie danych wyjściowych dla kompilacji bez debugowania. Oto podpis funkcji, który możesz dodać w zakresie globalnym, aby zrobić to w Swift 2.0 / 3.0:func print(items: Any..., separator: String = " ", terminator: String = "\n") { #if DEBUG var idx = items.startIndex let endIdx = items.endIndex repeat { Swift.print(items[idx], separator: separator, terminator: idx == (endIdx - 1) ? terminator : separator) idx += 1 } while idx < endIdx #endif }
Uwaga: Domyślnym separatorem jest tutaj spacja, a domyślnym terminatorem jest znak nowej linii. Jeśli chcesz, możesz skonfigurować to inaczej w swoim projekcie.
Mam nadzieję że to pomoże.
Aktualizacja:
Zwykle lepiej jest umieścić tę funkcję w zakresie globalnym, tak aby znajdowała się przed
print
funkcją Swift . Uważam, że najlepszym sposobem na zorganizowanie tego jest dodanie pliku narzędziowego do projektu (takiego jak DebugOptions.Swift), w którym można umieścić tę funkcję w zakresie globalnym.Od wersji Swift 3
++
operator zostanie wycofany. Zaktualizowałem powyższy fragment, aby odzwierciedlić tę zmianę.źródło
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
Problem z wszystkimi tymi podejściami, w tym moim, polega na tym, że nie usuwają one narzutu związanego z oceną
print
argumentów. Bez względu na to, którego z nich używasz, będzie to drogie:print(myExpensiveFunction())
Jedynym przyzwoitym rozwiązaniem jest zawinięcie rzeczywistego wywołania print w kompilację warunkową (załóżmy, że
DEBUG
jest to zdefiniowane tylko dla kompilacji debugowania):#if DEBUG print(myExpensiveFunction()) #endif
To i tylko to zapobiega
myExpensiveFunction
wywoływaniu w kompilacji wydania.Możesz jednak cofnąć ocenę o jeden poziom, używając funkcji automatycznego zamykania . W ten sposób możesz przepisać moje rozwiązanie (to jest Swift 3) w ten sposób:
func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") { #if DEBUG Swift.print(item(), separator: separator, terminator: terminator) #endif }
To rozwiązuje problem tylko w przypadku, gdy drukujesz tylko jedną rzecz, co zwykle jest prawdą. Dzieje się tak, ponieważ
item()
nie jest wywoływana w trybie wydania.print(myExpensiveFunction())
w ten sposób przestaje być drogie, ponieważ wywołanie jest zapakowane w zamknięcie bez oceny, aw trybie zwolnienia w ogóle nie będzie oceniane.źródło
@autoclosure
?print
oświadczenia w kodzie wysyłki, ale różni się to od tego, czego dotyczy moja odpowiedź. Daneprint
wyjściowe instrukcji nie są wysyłane do konsoli w kompilacji wydania niezależnego od Xcode, ale nadal są oceniane , więc warto wiedzieć, jak zablokować tę ocenę na wypadek, gdyby była kosztowna lub ma niepożądane skutki uboczne.Jak już wspomniałem, jestem studentem i potrzebuję trochę jaśniejszego zdefiniowania rzeczy, aby dalej postępować. Po wielu badaniach sekwencja, którą musiałem śledzić, to:
Kliknij nazwę projektu u góry Nawigatora plików po lewej stronie okna projektu Xcode. To jest wiersz zawierający nazwę projektu, liczbę celów kompilacji i wersję zestawu SDK systemu iOS.
Wybierz kartę Ustawienia kompilacji i przewiń w dół do sekcji „ Swift Compiler - Flagi niestandardowe ” u dołu. Kliknij strzałkę w dół obok pozycji Inne flagi, aby rozwinąć sekcję.
Kliknij wiersz Debug , aby go zaznaczyć. Umieść kursor myszy po prawej stronie linii i kliknij dwukrotnie. Pojawi się lista. Kliknij przycisk + w lewym dolnym rogu widoku listy, aby dodać wartość. Pole tekstowe stanie się aktywne.
W polu tekstowym wprowadź tekst
-D DEBUG
i naciśnij klawisz Return, aby zatwierdzić wiersz.Dodaj nowy plik Swift do swojego projektu. Będziesz chciał utworzyć niestandardową klasę dla pliku, więc wprowadź tekst w następujących wierszach:
class Log { var intFor : Int init() { intFor = 42 } func DLog(message: String, function: String = __FUNCTION__) { #if DEBUG println("\(function): \(message)") #endif } }
Miałem dziś problem z zaakceptowaniem klasy przez Xcode, więc init może być nieco cięższy niż to konieczne.
Teraz będziesz musiał odwołać się do swojej niestandardowej klasy w dowolnej klasie, w której zamierzasz użyć nowej funkcji niestandardowej zamiast
println()
Dodaj to jako właściwość w każdej odpowiedniej klasie:let logFor = Log()
Teraz można zastąpić wszystkie wystąpienia
println()
zlogFor.DLog()
. Dane wyjściowe zawierają również nazwę funkcji, w której wywołano wiersz.Zauważ, że wewnątrz funkcji klasowych nie mogłem wywołać funkcji, chyba że utworzyłem kopię funkcji jako funkcja klasy w tej klasie, a
println()
także jest nieco bardziej elastyczny z danymi wejściowymi, więc nie mogłem tego użyć w każdym przypadku w mój kod.źródło
Szybki 5
Po prostu utwórz nowy plik w swoim projekcie i wklej ten kod w:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { #if DEBUG items.forEach { Swift.print($0, separator: separator, terminator: terminator) } #endif }
Ta sygnatura funkcji jest zgodna z domyślną sygnaturą Swift, więc „zastępuje” funkcję w projekcie. W razie potrzeby nadal możesz uzyskać dostęp do oryginału za pomocą
Swift.print()
.Po dodaniu powyższego kodu używaj
print()
dalej jak zwykle, a będzie on drukowany tylko w kompilacjach debugowania.Uwaga: Wykonanie polecenia,
forEach
aby wydrukować każdy element, usuwa irytujące nawiasy tablicowe wokół instrukcji print, które pojawiają się, jeśli przejdzieszitems
bezpośrednio doSwift.print()
.Dla każdego stosunkowo nowego w Swifcie możesz się zastanawiać, co to
$0
jest do cholery . Po prostu reprezentuje pierwszy argument przekazany doforEach
bloku.forEach
Oświadczenie można także zapisać w ten sposób:items.forEach { item in Swift.print(item, separator: separator, terminator: terminator) }
Na koniec, jeśli jesteś zainteresowany, deklaracja Swift
print
wygląda następująco:public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
Moja odpowiedź powyżej odzwierciedla dokładną implementację Swift - chociaż nigdy nie drukuję więcej niż jednej rzeczy ani nie zmieniam separatora / terminatorów. Ale kto wie, może chcesz.
źródło
Oto funkcja, której używam, która działa doskonale w Swift 3:
func gLog<T>( _ object: @autoclosure() -> T, _ file: String = #file, _ function: String = #function, _ line: Int = #line) { #if DEBUG let value = object() let stringRepresentation: String if let value = value as? CustomDebugStringConvertible { stringRepresentation = value.debugDescription } else if let value = value as? CustomStringConvertible { stringRepresentation = value.description } else { fatalError("gLog only works for values that conform to CustomDebugStringConvertible or CustomStringConvertible") } let fileURL = NSURL(string: file)?.lastPathComponent ?? "Unknown file" let queue = Thread.isMainThread ? "UI" : "BG" let gFormatter = DateFormatter() gFormatter.dateFormat = "HH:mm:ss:SSS" let timestamp = gFormatter.string(from: Date()) print("✅ \(timestamp) {\(queue)} \(fileURL) > \(function)[\(line)]: " + stringRepresentation + "\n") #endif }
Oto przykład generowanego wyniku:
Wyjaśnienie:
zielony znacznik wyboru służy do szybkiego przeglądania wiadomości drukowanych (gLog) w konsoli, gdzie czasami mogą one zgubić się w morzu innych wiadomości
znacznik czasu / daty
wątek, na którym jest uruchamiany - w moim przypadku jest to albo MainThread (który nazywam UI), albo nie MainThread (który nazywam BG, dla wątku w tle)
nazwa pliku, w którym znajduje się wiadomość gLog
funkcja w pliku, w którym znajduje się wiadomość gLog
numer wiersza komunikatu gLog
aktualna wiadomość gLog, którą chcesz wydrukować
Mam nadzieję, że przyda się to komuś innemu!
źródło
Przetestowano za pomocą Swift 2.1 i Xcode 7.1.1
Istnieje łatwy sposób na wykluczenie wszystkich instrukcji print z wydanych wersji, gdy wiesz, że puste funkcje są usuwane przez kompilator Swift .
Uwaga dodatkowa: w erze Objective-C istniał wstępny parser, którego można było użyć do usunięcia instrukcji NSLog przed uruchomieniem kompilatora, jak opisano w mojej odpowiedzi tutaj . Ale ponieważ Swift nie ma już pre-parsera, to podejście nie jest już aktualne.
Oto, czego używam dzisiaj jako zaawansowanej i łatwo konfigurowalnej funkcji dziennika, bez martwienia się o jej usunięcie w kompilacjach wydań. Ponadto, ustawiając różne flagi kompilatora, możesz dostosować informacje, które są rejestrowane w razie potrzeby.
W razie potrzeby możesz dostosować tę funkcję, wszelkie sugestie dotyczące jej ulepszenia są mile widziane!
// Gobal log() function // // note that empty functions are removed by the Swift compiler -> use #if $endif to enclose all the code inside the log() // these log() statements therefore do not need to be removed in the release build ! // // to enable logging // // Project -> Build Settings -> Swift Compiler - Custom flags -> Other Swift flags -> Debug // add one of these 3 possible combinations : // // -D kLOG_ENABLE // -D kLOG_ENABLE -D kLOG_DETAILS // -D kLOG_ENABLE -D kLOG_DETAILS -D kLOG_THREADS // // you can just call log() anywhere in the code, or add a message like log("hello") // func log(message: String = "", filePath: String = #file, line: Int = #line, function: String = #function) { #if kLOG_ENABLE #if kLOG_DETAILS var threadName = "" #if kLOG_THREADS threadName = NSThread.currentThread().isMainThread ? "MAIN THREAD" : (NSThread.currentThread().name ?? "UNKNOWN THREAD") threadName = "[" + threadName + "] " #endif let fileName = NSURL(fileURLWithPath: filePath).URLByDeletingPathExtension?.lastPathComponent ?? "???" var msg = "" if message != "" { msg = " - \(message)" } NSLog("-- " + threadName + fileName + "(\(line))" + " -> " + function + msg) #else NSLog(message) #endif #endif }
Oto miejsce, w którym ustawiasz flagi kompilatora:
Przykładowe wyjście ze wszystkimi flagami wygląda następująco:
2016-01-13 23:48:38.026 FoodTracker[48735:4147607] -- [MAIN THREAD] ViewController(19) -> viewDidLoad() - hello
Kod z log () wygląda następująco:
override func viewDidLoad() { log("hello") super.viewDidLoad() // Handle the text field's user input through delegate callbacks nameTextField.delegate = self }
źródło
Jeszcze prostsze, po upewnieniu się, że
-D DEBUG
są ustawione dlaOTHER_SWIFT_FLAGS
ustawień kompilacji debugowania:#if !DEBUG func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { } #endif
źródło
XCode 8 wprowadził kilka nowych ustawień kompilacji .
W szczególności jeden, o którym mowa,
Active Compilation Conditions
działa w podobny sposób, co ustawienia innych flag .Zgodnie z XCode 8 (testowane w 8.3.2) otrzymasz to domyślnie:
Więc bez żadnej konfiguracji możesz napisać:
#if DEBUG print("⚠️ Something weird happened") #endif
Zdecydowanie zalecam, aby w przypadku intensywnego stosowania tego podejścia utworzyć klasę / strukturę / funkcję, która otacza tę logikę rejestrowania. Możesz chcieć przedłużyć to dalej w dół drogi.
źródło
Jak dotąd Varun Naharia ma lepsze rozwiązanie. Połączyłbym jego odpowiedź z odpowiedzią Rivery ...
-D DEBUG
flagę na dyrektywach kompilatora, ustawienia kompilacji.następnie dodaj ten kod:
#if !DEBUG public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { } #endif
Ten kod zamieni wszystko
print
w nic do wydania.źródło
Swift 4 Xcode 10.0.0
może mógłbyś tego użyć
func dPrint(_ message: @autoclosure () -> Any) { #if DEBUG print(message()) #endif }
Powodem użycia
@autoclosure
jest to, że jeśli przekażesz funkcję jako parametr wiadomości, funkcja zostanie wywołana tylko w trybie debugowania, spowoduje to spadek wydajności.w przeciwieństwie do
Swift.print(_ items: Any..., separator: String = default, terminator: String = default)
funkcji, moje rozwiązanie ma tylko jeden parametr, ponieważ w większości przypadków nie przekazujemy wielu parametrów, ponieważ funkcja print wyświetla informacje tylko w konsoli, możemy po prostu przekonwertować parametry na String:,"\(param1)"+"\(param2)"
prawda? mam nadzieję, że podoba ci się moje rozwiązanieźródło
Możesz także użyć punktu przerwania, ustawić go tak, aby był kontynuowany po ocenie i napisać komunikat drukowania w punkcie przerwania!
źródło
Możesz określić,
debug_println
czyja zawartość byłaby z grubsza:#if DEBUG println() #endif
źródło
Moje rozwiązanie używa tego kodu w AppDelegate przed klasą
// Disable console log in live app #if !arch(x86_64) && !arch(i386) public func debugPrint(items: Any..., separator: String = " ", terminator: String = "\n") { } public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { } #endif class AppDelegate: UIResponder, UIApplicationDelegate { // App Delegate Code }
źródło
dla mojego rozwiązania upraszczam
import UIKit class DLog: NSObject { init(title:String, log:Any) { #if DEBUG print(title, log) #endif } }
następnie, aby to pokazać, po prostu zadzwoń
_ = DLog(title:"any title", log:Any)
źródło
Skończyło się na tym:
#if DEBUG func dLog(_ item: @autoclosure () -> Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) { print("\(Date()) [\((file as NSString).lastPathComponent):\(line) \(function)] \(item())") } #else func dLog(_ item: @autoclosure () -> Any) {} #endif
Jest dość kompaktowy, wyświetla przydatne informacje (znacznik czasu, szybka nazwa pliku, linia kodu, nazwa funkcji) i przynajmniej w moich testach nie mogłem znaleźć żadnych zarejestrowanych ciągów w pliku binarnym aplikacji po otwarciu w edytorze szesnastkowym.
źródło
Jeszcze prostsze: wykorzystaj fakt, że potwierdzenia są usuwane z kompilacji wydania i dopiero stamtąd wywołują print. Spowoduje to usunięcie wszystkich wywołań dziennika (tak, nawet wywołań Log.da), ponieważ są one puste podczas budowania do wydania.
Słyszałem też, że wydruki są usuwane z kompilacji wydania, ale nie mogłem znaleźć tego na piśmie. Na razie używam czegoś takiego
Log
poniżej. Mam bardziej mięsistą wersję na GitHub z emoji (dla czytelności) i tematami dziennika (dla spójności):https://github.com/Gatada/JBits/blob/master/Project/Utility/Log.swift
public enum Log { /// A date formatter used to create the timestamp in the log. /// /// This formatter is only created if it is actually used, reducing the /// overhead to zero. static var formatter: DateFormatter? // MARK: - API /// Call to print message in debug area. /// /// Asserts are removed in release builds, which make /// the function body empty, which caused all calls to /// be removed as well. /// /// Result is zero overhead for release builds. public static func da(_ message: String) { assert(debugAreaPrint(message)) } // MARK: - Helpers /// The function that actually does the printing. It returns `true` to /// prevent the assert from kicking in on debug builds. private static func debugAreaPrint(_ message: String) -> Bool { print("\(timestamp) - \(message)") return true } /// Creates a timestamp used as part of the temporary logging in the debug area. static private var timestamp: String { if formatter == nil { formatter = DateFormatter() formatter!.dateFormat = "HH:mm:ss.SSS" } let date = Date() return formatter!.string(from: date) } }
W kodzie:
Log.da("This is only handled in a debug build.")
Widziane w obszarze debugowania Xcode tylko podczas uruchamiania kompilacji debugowania:
źródło
Mój projekt został opracowany w Objective C, ale od zeszłego roku zacząłem scalać nowy kod w Swift, więc w Swift poniższe rozwiązanie zadziałało, dodałem ten kod w pliku stałym My Swift:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { #if DEBUG items.forEach { Swift.print($0, separator: separator, terminator: terminator) } #endif }
źródło
To działa dla mnie (dodaj to jako funkcję globalną w projekcie)
func print(_ items: Any...) { #if DEBUG Swift.print(items[0]) #endif }
źródło