Я хотел бы глобально игнорировать все println()
вызовы в моем коде Swift, если я не нахожусь в сборке отладки. Я не могу найти никаких подробных пошаговых инструкций для этого и был бы признателен за руководство. есть способ сделать это глобально, или мне нужно сделать , чтобы окружать каждый println()
с #IF DEBUG/#ENDIF
утверждениями?
84
Ответы:
Самый простой способ - поставить свою глобальную функцию перед Swift
println
:func println(object: Any) { Swift.println(object) }
Когда пришло время прекратить ведение журнала, просто закомментируйте тело этой функции:
func println(object: Any) { // Swift.println(object) }
Или вы можете сделать это автоматически, используя условное выражение:
func println(object: Any) { #if DEBUG Swift.println(object) #endif }
РЕДАКТИРОВАТЬ В Swift 2.0
println
изменено наprint
. К сожалению, теперь у него есть переменный первый параметр; это круто, но это означает, что вы не можете легко переопределить его, потому что Swift не имеет оператора splat, поэтому вы не можете передавать вариативный код в коде (его можно создать только буквально). Но вы можете сделать сокращенную версию, которая будет работать, если, как обычно бывает, вы печатаете только одно значение:func print(items: Any..., separator: String = " ", terminator: String = "\n") { Swift.print(items[0], separator:separator, terminator: terminator) }
В Swift 3 вам нужно подавить внешнюю метку первого параметра:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { Swift.print(items[0], separator:separator, terminator: terminator) }
источник
println()
не выполняется в режиме выпуска.println
изменил его наprint
. Причина, по которой это не работает для вас, заключается в том, что вашеprint
определение не соответствует Swift, поэтому вы не отменяете его. Есть небольшая проблема, потому что, как уже много раз отмечалось, Swift не имеет оператора splat, поэтому вы не можете передать вариативность. Но он отлично работает для одного элемента, который можно передать какitems[0]
.Обновлено для Swift 4.x:
Поскольку Swift 2.0 / 3.0 и Xcode 7/8 вышли из стадии бета-тестирования, были внесены некоторые изменения в способ отключения функции печати в сборках выпуска.
Есть некоторые важные моменты, упомянутые выше @matt и @Nate Birkholz, которые все еще актуальны.
println()
Функция была замененаprint()
Чтобы использовать
#if DEBUG
макрос, вы должны определить «Swift Compiler - Custom Flags - Other Flags», чтобы содержать значение-D DEBUG
Я бы порекомендовал переопределить
Swift.print()
функцию в глобальной области, чтобы вы могли использовать ееprint()
как обычно в своем коде, но она удалит вывод для сборок без отладки. Вот сигнатура функции, которую вы можете добавить в глобальную область видимости, чтобы сделать это в 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 }
Примечание. Мы установили здесь разделителем по умолчанию как пробел, а в качестве терминатора по умолчанию - новую строку. Вы можете настроить это по-другому в своем проекте, если хотите.
Надеюсь это поможет.
Обновить:
Обычно предпочтительнее поместить эту функцию в глобальную область видимости, чтобы она располагалась перед
print
функцией Swift . Я считаю, что лучший способ организовать это - добавить в проект служебный файл (например, DebugOptions.Swift), в котором вы можете разместить эту функцию в глобальной области видимости.Начиная с Swift 3,
++
оператор устарел. Я обновил приведенный выше фрагмент, чтобы отразить это изменение.источник
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
Проблема всех этих подходов, включая мой, заключается в том, что они не устраняют накладные расходы на оценку
print
аргументов. Независимо от того, какой из них вы используете, это будет дорого:print(myExpensiveFunction())
Единственное достойное решение - обернуть фактический вызов печати в условную компиляцию (предположим, что
DEBUG
это определено только для отладочных сборок):#if DEBUG print(myExpensiveFunction()) #endif
Это и только это предотвращает
myExpensiveFunction
вызов в сборке выпуска.Однако вы можете отодвинуть оценку на один уровень, используя автоматическое закрытие . Таким образом, вы можете переписать мое решение (это Swift 3) следующим образом:
func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") { #if DEBUG Swift.print(item(), separator: separator, terminator: terminator) #endif }
Это решает проблему только в том случае, если вы печатаете только одну вещь, что обычно верно. Это потому, что
item()
не вызывается в режиме выпуска.print(myExpensiveFunction())
таким образом перестает быть дорогостоящим, потому что вызов завершается закрытием без оценки, а в режиме выпуска он вообще не будет оцениваться.источник
@autoclosure
?print
утверждения в коде доставки, но это отличается от того, о чем здесь мой ответ.print
Оператор вывода не отправляется на консоль в вашем Xcode-независимой сборки релиза, но она по - прежнему оценивается , так что остается полезно знать , как подавить эту оценку только в случае , если это дорого или имеет нежелательные побочные эффекты.Как уже отмечалось, я студент, и мне нужно более четко определить вещи, чтобы следовать им. После долгих исследований мне нужно было следовать следующей последовательности:
Щелкните имя проекта в верхней части файлового навигатора слева от окна проекта Xcode. Это строка с названием проекта, количеством целей сборки и версией iOS SDK.
Выберите вкладку « Параметры сборки » и прокрутите вниз до раздела « Компилятор Swift - Пользовательские флаги ». Щелкните стрелку вниз рядом с надписью «Другие флаги», чтобы развернуть раздел.
Щелкните строку Debug, чтобы выбрать ее. Наведите курсор мыши на правую часть строки и дважды щелкните. Появится список. Нажмите кнопку + в нижнем левом углу списка, чтобы добавить значение. Текстовое поле станет активным.
В текстовое поле введите текст
-D DEBUG
и нажмите Return, чтобы зафиксировать строку.Добавьте в проект новый файл Swift. Вам нужно создать собственный класс для файла, поэтому введите текст в следующих строках:
class Log { var intFor : Int init() { intFor = 42 } func DLog(message: String, function: String = __FUNCTION__) { #if DEBUG println("\(function): \(message)") #endif } }
У меня были проблемы с тем, чтобы класс был принят Xcode сегодня, поэтому init может быть немного тяжелее, чем необходимо.
Теперь вам нужно будет ссылаться на свой настраиваемый класс в любом классе, в котором вы собираетесь использовать новую настраиваемую функцию вместо
println()
Добавить это как свойство в каждом применимом классе:let logFor = Log()
Теперь вы можете заменить все экземпляры
println()
сlogFor.DLog()
. Вывод также включает имя функции, в которой была вызвана строка.Обратите внимание, что внутри функций класса я не мог вызвать функцию, если я не сделал копию функции как функцию класса в этом классе, а
println()
также немного более гибок с вводом, поэтому я не мог использовать это в каждом экземпляре в мой код.источник
Swift 5
Просто создайте новый файл в своем проекте и вставьте этот код в:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { #if DEBUG items.forEach { Swift.print($0, separator: separator, terminator: terminator) } #endif }
Эта сигнатура функции соответствует сигнатуре Swift по умолчанию, поэтому она «перезаписывает» функцию в вашем проекте. При необходимости вы все равно можете получить доступ к оригиналу, используя
Swift.print()
.После того, как вы добавили приведенный выше код, продолжайте использовать
print()
как обычно, и он будет печататься только в отладочных сборках.Примечание: выполнение
forEach
для печати каждого элемента избавляет от надоедливых скобок массива вокруг операторов печати, которые появляются, если вы просто переходитеitems
прямо вSwift.print()
.Для любого относительно новичка в Swift вы можете задаться вопросом, что это за хрень
$0
. Он просто представляет первый аргумент, переданный вforEach
блок.forEach
Заявление также может быть записано следующим образом:items.forEach { item in Swift.print(item, separator: separator, terminator: terminator) }
Наконец, если вам интересно, объявление Swift
print
выглядит следующим образом:public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
Мой ответ выше отражает точную реализацию Swift - хотя я никогда не печатаю более одного элемента и не меняю разделители / терминаторы. Но кто знает, возможно, вы захотите.
источник
Вот функция, которую я использую, которая отлично работает в 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 }
Вот пример вывода, который он генерирует:
Пояснение:
зеленая галочка используется для того, чтобы вы могли быстро увидеть свои сообщения печати (gLog) в консоли, где они могут иногда теряться в море других сообщений
отметка времени / даты
поток, в котором он выполняется - в моем случае это либо MainThread (который я называю UI), либо не MainThread (который я называю BG для фонового потока)
имя файла, в котором находится сообщение gLog
функция в файле, в котором находится сообщение gLog
номер строки сообщения gLog
фактическое сообщение gLog, которое вы хотите распечатать
Надеюсь, это будет полезно кому-то еще!
источник
Протестировано с Swift 2.1 и Xcode 7.1.1
Есть простой способ исключить все операторы печати из версий выпуска, если вы знаете, что пустые функции удаляются компилятором Swift .
Боковое примечание: в эпоху Objective-C существовал предварительный синтаксический анализатор, который можно было использовать для удаления операторов NSLog до запуска компилятора, как описано в моем ответе здесь . Но поскольку в Swift больше нет препарсера, этот подход больше не работает.
Вот то, что я использую сегодня как расширенную и легко настраиваемую функцию журнала, не беспокоясь об ее удалении в сборках выпуска. Также, устанавливая различные флаги компилятора, вы можете настроить информацию, которая регистрируется по мере необходимости.
Вы можете настроить функцию по мере необходимости, любые предложения по ее улучшению приветствуются!
// 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 }
Здесь вы устанавливаете флаги компилятора:
Пример вывода со всеми включенными флагами выглядит так:
2016-01-13 23:48:38.026 FoodTracker[48735:4147607] -- [MAIN THREAD] ViewController(19) -> viewDidLoad() - hello
Код с log () выглядит так:
override func viewDidLoad() { log("hello") super.viewDidLoad() // Handle the text field's user input through delegate callbacks nameTextField.delegate = self }
источник
Еще проще, убедившись, что
-D DEBUG
установленыOTHER_SWIFT_FLAGS
настройки сборки отладки:#if !DEBUG func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { } #endif
источник
XCode 8 представил несколько новых настроек сборки .
В частности, упомянутый
Active Compilation Conditions
делает то же самое, что и настройки Other Flags .Согласно XCode 8 (проверено в 8.3.2) вы получите это по умолчанию:
Итак, без какой-либо конфигурации вы можете написать следующее:
#if DEBUG print("⚠️ Something weird happened") #endif
Я настоятельно рекомендую вам, если вы широко используете этот подход, создать класс / структуру / функцию, которая обертывает эту логику ведения журнала. Возможно, вы захотите продлить это дальше по дороге.
источник
Пока что у Варуна Нахарии есть лучшее решение. Я бы совмещал его ответ с ответом Риверы ...
-D DEBUG
флаг на директивы компилятора, настройки сборки.затем добавьте этот код:
#if !DEBUG public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { } #endif
Этот код преобразует все
print
в ничто для выпуска.источник
Swift 4 Xcode 10.0
может ты мог бы использовать это
func dPrint(_ message: @autoclosure () -> Any) { #if DEBUG print(message()) #endif }
Причина использования
@autoclosure
заключается в том, что если вы передадите функцию в качестве параметра сообщения, функция будет вызываться только в режиме отладки, что приведет к снижению производительности.в отличие от
Swift.print(_ items: Any..., separator: String = default, terminator: String = default)
функции, у моего решения только один параметр, потому что в большинстве случаев мы не передаем несколько параметров, так как функция печати показывает информацию только в консоли, мы можем просто преобразовать параметры в String:,"\(param1)"+"\(param2)"
верно? надеюсь, тебе нравится мое решениеисточник
Вы также можете использовать точку останова, установить ее для продолжения после оценки и написать сообщение печати в точке останова!
источник
Вы можете определить,
debug_println
чье содержимое будет примерно таким:#if DEBUG println() #endif
источник
Мое решение - использовать этот код в AppDelegate перед классом
// 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 }
источник
для своего решения я делаю это просто
import UIKit class DLog: NSObject { init(title:String, log:Any) { #if DEBUG print(title, log) #endif } }
затем, чтобы показать это, просто позвоните
_ = DLog(title:"any title", log:Any)
источник
В итоге я использовал это:
#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
Он довольно компактен, печатает некоторую полезную информацию (метку времени, быстрое имя файла, строку кода, имя функции), и, по крайней мере, в моих тестах я не смог найти никаких записанных строк в двоичном файле приложения при открытии в шестнадцатеричном редакторе.
источник
Еще проще: воспользуйтесь тем, что утверждения удаляются из сборок выпуска и только оттуда вызывают печать. Это удаляет все вызовы журнала (да, даже вызовы Log.da), поскольку они пусты при сборке для выпуска.
Но я также слышал, что отпечатки удаляются для выпускных сборок, но не смог найти это в письменной форме. Итак, сейчас я использую что-то вроде этого
Log
ниже. У меня есть более содержательная версия на GitHub с эмодзи (для удобства чтения) и темами журнала (для согласованности):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) } }
В коде:
Log.da("This is only handled in a debug build.")
Видно в области отладки Xcode только при запуске отладочной сборки:
источник
Мой проект был разработан на Objective C, но с прошлого года я начал объединять новый код в Swift, поэтому в Swift ниже решение сработало для меня, я добавил этот код в файл констант My Swift:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { #if DEBUG items.forEach { Swift.print($0, separator: separator, terminator: terminator) } #endif }
источник
Это работает для меня (добавьте это как глобальную функцию в проект)
func print(_ items: Any...) { #if DEBUG Swift.print(items[0]) #endif }
источник