Me gustaría ignorar globalmente todas las println()
llamadas en mi código Swift si no estoy en una compilación de depuración. No puedo encontrar instrucciones sólidas paso a paso para esto y agradecería orientación. ¿Hay alguna manera de hacer esto a nivel mundial, o necesito rodear cada uno println()
con #IF DEBUG/#ENDIF
declaraciones?
84
Respuestas:
La forma más sencilla es poner su propia función global frente a la de Swift
println
:func println(object: Any) { Swift.println(object) }
Cuando sea el momento de detener el registro, simplemente comente el cuerpo de esa función:
func println(object: Any) { // Swift.println(object) }
O puede hacerlo automático usando un condicional:
func println(object: Any) { #if DEBUG Swift.println(object) #endif }
EDITAR En Swift 2.0
println
se cambia aprint
. Desafortunadamente, ahora tiene un primer parámetro variadic; esto es genial, pero significa que no puede anularlo fácilmente porque Swift no tiene un operador "splat", por lo que no puede pasar un código variable (solo se puede crear literalmente). Pero puede hacer una versión reducida que funcione si, como suele ser el caso, está imprimiendo solo un valor:func print(items: Any..., separator: String = " ", terminator: String = "\n") { Swift.print(items[0], separator:separator, terminator: terminator) }
En Swift 3, debe suprimir la etiqueta externa del primer parámetro:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { Swift.print(items[0], separator:separator, terminator: terminator) }
fuente
println()
no se ejecuta en modo de lanzamiento.println
cambiado aprint
. La razón por la que no funciona para usted es que suprint
definición no coincide con la de Swift, por lo que no la está anulando. Hay un pequeño problema porque, como se ha comentado muchas veces, Swift no tiene un operador de splat, por lo que no puede pasar la variadic. Pero funciona bien para un elemento, que puede pasar comoitems[0]
.Actualizado para Swift 4.x:
Con Swift 2.0 / 3.0 y Xcode 7/8 ahora fuera de la versión beta, ha habido algunos cambios en la forma en que deshabilita la función de impresión en las versiones de lanzamiento.
Hay algunos puntos importantes mencionados anteriormente por @matt y @Nate Birkholz que aún son válidos.
La
println()
función ha sido reemplazada porprint()
Para usar la
#if DEBUG
macro, debe definir el "Compilador rápido - Banderas personalizadas - Otras banderas" para contener el valor-D DEBUG
Recomendaría anular la
Swift.print()
función en el alcance global para que pueda usar laprint()
función normalmente en su código, pero eliminará la salida para compilaciones que no sean de depuración. Aquí hay una firma de función que puede agregar en el ámbito global para hacer esto en 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 }
Nota: Hemos configurado el separador predeterminado para que sea un espacio aquí, y el terminador predeterminado para ser una nueva línea. Puede configurar esto de manera diferente en su proyecto si lo desea.
Espero que esto ayude.
Actualizar:
Por lo general, es preferible poner esta función en el ámbito global, de modo que se ubique frente a la
print
función de Swift . Creo que la mejor manera de organizar esto es agregar un archivo de utilidad a su proyecto (como DebugOptions.Swift) donde puede colocar esta función en el ámbito global.A partir de Swift 3, el
++
operador quedará obsoleto. Actualicé el fragmento anterior para reflejar este cambio.fuente
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
El problema con todos estos enfoques, incluido el mío, es que no eliminan la sobrecarga de evaluar los
print
argumentos. No importa cuál de ellos use, esto será costoso:print(myExpensiveFunction())
La única solución decente es envolver la llamada de impresión real en compilación condicional (supongamos que
DEBUG
está definida solo para compilaciones de depuración):#if DEBUG print(myExpensiveFunction()) #endif
Eso, y solo eso, evita que
myExpensiveFunction
se llame en una versión de lanzamiento.Sin embargo, puede retrasar la evaluación un nivel utilizando el cierre automático . Por lo tanto, podría reescribir mi solución (esto es Swift 3) de esta manera:
func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") { #if DEBUG Swift.print(item(), separator: separator, terminator: terminator) #endif }
Esto resuelve el problema solo en el caso de que esté imprimiendo solo una cosa, lo que suele ser cierto. Eso es porque
item()
no se llama en modo de lanzamiento.print(myExpensiveFunction())
así deja de ser caro, porque la llamada se envuelve en un cierre sin ser evaluada, y en modo de liberación, no se evaluará en absoluto.fuente
@autoclosure
?print
declaraciones en mi código de envío, pero eso es diferente de lo que trata mi respuesta aquí. Laprint
salida de una declaración no se envía a la consola en su versión de versión independiente de Xcode, pero aún se evalúa , por lo que sigue siendo útil saber cómo suprimir esa evaluación en caso de que sea costosa o tenga efectos secundarios no deseados.Como se señaló, soy un estudiante y necesito que las cosas se definan un poco más claramente para seguirlas. Después de mucha investigación, la secuencia que tenía que seguir es:
Haga clic en el nombre del proyecto en la parte superior del Navegador de archivos a la izquierda de la ventana del proyecto Xcode. Esta es la línea que tiene el nombre del proyecto, cuántos destinos de compilación hay y la versión del SDK de iOS.
Elija la pestaña Build Settings y desplácese hacia abajo hasta la sección " Swift Compiler - Custom Flags " cerca de la parte inferior. Haga clic en la flecha hacia abajo junto a Otras banderas para expandir la sección.
Haga clic en la línea Debug para seleccionarla. Coloque el cursor del mouse sobre el lado derecho de la línea y haga doble clic. Aparecerá una vista de lista. Haga clic en el botón + en la parte inferior izquierda de la vista de lista para agregar un valor. Se activará un campo de texto.
En el campo de texto, ingrese el texto
-D DEBUG
y presione Retorno para confirmar la línea.Agregue un nuevo archivo Swift a su proyecto. Querrá crear una clase personalizada para el archivo, así que ingrese el texto siguiendo las líneas de lo siguiente:
class Log { var intFor : Int init() { intFor = 42 } func DLog(message: String, function: String = __FUNCTION__) { #if DEBUG println("\(function): \(message)") #endif } }
Hoy estaba teniendo problemas para que Xcode aceptara la clase, por lo que el init puede ser un poco más pesado de lo necesario.
Ahora deberá hacer referencia a su clase personalizada en cualquier clase en la que desee utilizar la nueva función personalizada en lugar de
println()
Agregar esto como una propiedad en cada clase aplicable:let logFor = Log()
Ahora puede reemplazar cualquier instancia de
println()
conlogFor.DLog()
. La salida también incluye el nombre de la función en la que se llamó a la línea.Tenga en cuenta que dentro de las funciones de clase no podría llamar a la función a menos que hiciera una copia de la función como una función de clase en esa clase, y
println()
también es un poco más flexible con la entrada, por lo que no podría usar esto en cada instancia en mi código.fuente
Rápido 5
Simplemente cree un nuevo archivo en su proyecto y pegue este código en:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { #if DEBUG items.forEach { Swift.print($0, separator: separator, terminator: terminator) } #endif }
La firma de esta función coincide con la de Swift predeterminada, por lo que "sobrescribe" la función en su proyecto. Si es necesario, aún puede acceder al original utilizando
Swift.print()
.Una vez que haya agregado el código anterior, siga usando
print()
el como de costumbre y solo se imprimirá en compilaciones de depuración.Nota: Hacer el
forEach
para imprimir cada elemento elimina los molestos paréntesis de matriz alrededor de las declaraciones de impresión que aparecen si simplemente pasaitems
directamente aSwift.print()
.Para cualquiera que sea relativamente nuevo en Swift, puede que se pregunte qué diablos
$0
es. Simplemente representa el primer argumento que se pasa alforEach
bloque. LaforEach
declaración también podría escribirse así:items.forEach { item in Swift.print(item, separator: separator, terminator: terminator) }
Por último, si está interesado, la declaración de Swift se
print
ve así:public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
Mi respuesta anterior refleja la implementación exacta de Swift, aunque nunca imprimo más de una cosa ni cambio separadores / terminadores. Pero quién sabe, es posible que desee.
fuente
Aquí hay una función que uso, que funciona perfectamente en 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 }
Aquí hay un ejemplo de la salida que genera:
Explicación:
la marca de verificación verde se usa para permitirle ver rápidamente sus mensajes impresos (gLog) en la consola, donde a veces pueden perderse en un mar de otros mensajes
el sello de hora / fecha
el hilo en el que se está ejecutando; en mi caso, es MainThread (que llamo UI) o no MainThread (que llamo BG, para el hilo de fondo)
el nombre del archivo en el que reside el mensaje gLog
la función dentro del archivo en el que reside el mensaje gLog
el número de línea del mensaje gLog
el mensaje de gLog real que le gustaría imprimir
¡Espero que esto sea útil para alguien más!
fuente
Probado con Swift 2.1 y Xcode 7.1.1
Existe una manera fácil de excluir todas las declaraciones de impresión de las versiones de lanzamiento, una vez que sepa que el compilador de Swift elimina las funciones vacías .
Nota al margen: en la era de Objective-C, había un pre-analizador que podía usarse para eliminar declaraciones NSLog antes de que el compilador se activara, como se describe en mi respuesta aquí . Pero como Swift ya no tiene un analizador previo, este enfoque ya no es válido.
Esto es lo que uso hoy en día como una función de registro avanzada y fácilmente configurable, sin tener que preocuparme por eliminarlo en las versiones de lanzamiento. Además, al configurar diferentes indicadores del compilador, puede modificar la información que se registra según sea necesario.
Puede modificar la función según sea necesario, ¡cualquier sugerencia para mejorarla es bienvenida!
// 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 }
Aquí es donde configura las banderas del compilador:
Un ejemplo de salida con todas las banderas encendidas se ve así:
2016-01-13 23:48:38.026 FoodTracker[48735:4147607] -- [MAIN THREAD] ViewController(19) -> viewDidLoad() - hello
El código con log () se ve así:
override func viewDidLoad() { log("hello") super.viewDidLoad() // Handle the text field's user input through delegate callbacks nameTextField.delegate = self }
fuente
Aún más simple, después de asegurarse de que
-D DEBUG
esté configurado para laOTHER_SWIFT_FLAGS
configuración de compilación de depuración:#if !DEBUG func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { } #endif
fuente
XCode 8 introdujo algunas configuraciones de compilación nuevas .
En particular, uno al que se
Active Compilation Conditions
hace referencia hace de manera similar a lo que hizo la configuración de Otras banderas .Según XCode 8 (probado en 8.3.2) obtendrá esto por defecto:
Entonces, sin ninguna configuración, puede escribir lo siguiente:
#if DEBUG print("⚠️ Something weird happened") #endif
Le recomiendo encarecidamente que si usa este enfoque de manera extensa, cree una clase / estructura / función que envuelva esta lógica de registro. Es posible que desee ampliar esto más adelante.
fuente
Varun Naharia tiene la mejor solución hasta ahora. Combinaría su respuesta con la de Rivera ...
-D DEBUG
bandera en las directivas del compilador, construir configuraciones.luego agregue este código:
#if !DEBUG public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { } #endif
Este código convertirá todo
print
en nada para su lanzamiento.fuente
Swift 4 Xcode 10.0
tal vez podrías usar esto
func dPrint(_ message: @autoclosure () -> Any) { #if DEBUG print(message()) #endif }
La razón de su uso
@autoclosure
es que si pasa una función como parámetro de mensaje, la función se llamará solo en modo de depuración, lo que provocará un impacto en el rendimiento.a diferencia de la
Swift.print(_ items: Any..., separator: String = default, terminator: String = default)
función, mi solución tiene solo un parámetro, porque en la mayoría de los casos, no pasamos múltiples parámetros ya que la función de impresión solo muestra información en la consola, podemos simplemente convertir los parámetros a String:,"\(param1)"+"\(param2)"
¿verdad? espero que te guste mi soluciónfuente
También puede usar un punto de interrupción, configurarlo para que continúe después de la evaluación y escribir el mensaje de impresión en el punto de interrupción.
fuente
Podrías definir
debug_println
cuyo contenido sería aproximadamente:#if DEBUG println() #endif
fuente
Mi solución es usar este código en AppDelegate antes de la clase
// 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 }
fuente
para mi solución lo hago simple
import UIKit class DLog: NSObject { init(title:String, log:Any) { #if DEBUG print(title, log) #endif } }
luego para mostrarlo solo llama
_ = DLog(title:"any title", log:Any)
fuente
Terminé usando esto:
#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
Es bastante compacto, imprime información útil (marca de tiempo, nombre de archivo rápido, línea de código, nombre de función) y al menos en mis pruebas no pude encontrar ninguna cadena registrada en el archivo binario de la aplicación cuando se abrió en el editor hexadecimal.
fuente
Aún más simple: aproveche el hecho de que las afirmaciones se eliminan de las versiones de lanzamiento y solo desde allí se llaman a la impresión. Esto elimina todas las llamadas de registro (sí, incluso las llamadas a Log.da) ya que están vacías cuando se compilan para su lanzamiento.
Pero también escuché que las impresiones se eliminan para las versiones de lanzamiento, pero no pude encontrarlas por escrito. Entonces, por ahora, estoy usando algo como esto a
Log
continuación. Tengo una versión más carnosa en GitHub con emojis (para legibilidad) y temas de registro (para coherencia):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) } }
En codigo:
Log.da("This is only handled in a debug build.")
Visto en el área de depuración de Xcode solo cuando se ejecuta una compilación de depuración:
fuente
Mi proyecto se desarrolló en Objective C, pero desde el año pasado comencé a fusionar un nuevo código en Swift, por lo que en Swift la siguiente solución funcionó para mí, agregué ese código en Mi archivo constante de Swift:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") { #if DEBUG items.forEach { Swift.print($0, separator: separator, terminator: terminator) } #endif }
fuente
Esto funciona para mí (agregue esto como una función global en el proyecto)
func print(_ items: Any...) { #if DEBUG Swift.print(items[0]) #endif }
fuente