println()
デバッグビルドを使用していない場合、Swiftコード内のすべての呼び出しをグローバルに無視したいと思います。このための強力なステップバイステップの説明が見つかりません。ガイダンスをいただければ幸いです。これをグローバルに行う方法はありますか、それともすべてprintln()
を#IF DEBUG/#ENDIF
ステートメントで囲む必要がありますか?
println()
デバッグビルドを使用していない場合、Swiftコード内のすべての呼び出しをグローバルに無視したいと思います。このための強力なステップバイステップの説明が見つかりません。ガイダンスをいただければ幸いです。これをグローバルに行う方法はありますか、それともすべてprintln()
を#IF DEBUG/#ENDIF
ステートメントで囲む必要がありますか?
回答:
最も簡単な方法は、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
}
EDITでスウィフト2.0println
に変更されますprint
。残念ながら、現在は可変個引数の最初のパラメーターがあります。これはすばらしいですが、Swiftには「スプラット」演算子がないため、コードで可変個引数を渡すことができないため、簡単にオーバーライドできないことを意味します(文字通りにのみ作成できます)。ただし、通常の場合のように、1つの値のみを出力する場合に機能する縮小バージョンを作成できます。
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にはスプラット演算子がないため、可変個引数を渡すことができないため、わずかな問題があります。ただし、1つのアイテムに対しては正常に機能し、として渡すことができますitems[0]
。
Swift 4.x用に更新:
Swift 2.0 /3.0およびXcode7 / 8のベータ版が終了したため、リリースビルドで印刷機能を無効にする方法にいくつかの変更が加えられました。
上記の@mattと@NateBirkholzによって言及された、まだ有効ないくつかの重要なポイントがあります。
println()
関数が置き換えられていますprint()
#if DEBUG
マクロを使用 するには、「Swiftコンパイラ-カスタムフラグ-その他のフラグ」を定義して値を含める必要があります-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
}
注:ここでは、デフォルトの区切り文字をスペースに設定し、デフォルトのターミネーターを改行に設定しています。必要に応じて、プロジェクトでこれを別の方法で構成できます。
お役に立てれば。
更新:
通常、この関数をグローバルスコープに配置して、Swiftの前に配置することをお勧めします。 print
関数のます。これを整理する最良の方法は、この関数をグローバルスコープに配置できるユーティリティファイル(DebugOptions.Swiftなど)をプロジェクトに追加することです。
Swift 3以降、++
オペレーターは非推奨になります。この変更を反映するために、上記のスニペットを更新しました。
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
私を含むこれらすべてのアプローチの問題は、print
引数を評価するオーバーヘッドが取り除かれないことです。どちらを使用しても、これは高額になります。
print(myExpensiveFunction())
唯一の適切な解決策は、実際の印刷呼び出しを条件付きコンパイルでラップすることです(DEBUG
デバッグビルドに対してのみ定義されていると仮定しましょう)。
#if DEBUG
print(myExpensiveFunction())
#endif
それだけmyExpensiveFunction
で、リリースビルドで呼び出されるのを防ぎます。
ただし、自動閉鎖を使用すると、評価を1レベル遅らせることができます。したがって、私のソリューション(これはSwift 3です)を次のように書き直すことができます。
func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
#if DEBUG
Swift.print(item(), separator: separator, terminator: terminator)
#endif
}
これにより、1つだけ印刷する場合に問題が解決します。これは、通常は当てはまります。これitem()
は、リリースモードでは呼び出されないためです。print(myExpensiveFunction())
したがって、コールは評価されずにクロージャにラップされ、リリースモードではまったく評価されないため、コストがかからなくなります。
@autoclosure
?
print
出荷コードにステートメントを残す傾向がありますが、それはここでの私の答えとは異なります。print
文の出力は、あなたのXcodeに依存しないリリースビルドでコンソールに送信されていませんが、それはまだされて評価され、それはそれは高価であるか、望ましくない副作用を持っているだけの場合にはその評価を抑制する方法を知っておくと便利残っているので、。
すでに述べたように、私は学生であり、従うためにもう少し明確に定義されたものが必要です。多くの調査の後、私が従う必要のある順序は次のとおりです。
Xcodeプロジェクトウィンドウの左側にあるファイルナビゲータの上部にあるプロジェクト名をクリックします。これは、プロジェクトの名前、ビルドターゲットの数、およびiOSSDKのバージョンを含む行です。
[ビルド設定]タブを選択し、下部にある[ Swiftコンパイラ-カスタムフラグ]セクションまで下にスクロールします。その他のフラグの横にある下矢印をクリックします、セクションを展開します。
デバッグ行をクリックして選択します。線の右側にマウスカーソルを置き、ダブルクリックします。リストビューが表示されます。+をクリックしますリストビューの左下にある ]ボタンを、値を追加します。テキストフィールドがアクティブになります。
テキストフィールドにテキスト-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()
、入力に対してもう少し柔軟性があるため、のすべてのインスタンスでこれを使用できなかったことに注意してください。私のコード。
スウィフト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
まっすぐに渡した場合に表示されるprintステートメントの周りの煩わしい配列括弧が削除されます。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の実装を反映していますが、複数のものを印刷したり、区切り文字/ターミネーターを変更したりすることはありません。しかし、誰が知っているか、あなたはしたいかもしれません。
これが私が使っている関数で、Swift3で完全に機能します。
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メッセージ
これが他の誰かに役立つことを願っています!
でテストスウィフト2.1&Xcodeの7.1.1
空の関数がSwiftコンパイラによって削除されることがわかったら、リリースバージョンからすべてのprintステートメントを除外する簡単な方法があります。
補足: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
れているものは、他のフラグ設定が行ったのと同様の方法で行います。
「アクティブコンパイル条件」は、条件付きコンパイルフラグをSwiftコンパイラに渡すための新しいビルド設定です。
XCode 8(8.3.2でテスト済み)に従って、デフォルトでこれを取得します。
したがって、設定なしで次のように書くことができます。
#if DEBUG
print("⚠️ Something weird happened")
#endif
このアプローチを広範囲に使用する場合は、このロギングロジックをラップするクラス/構造体/関数を作成することを強くお勧めします。これをさらに拡張することをお勧めします。
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)
関数とは異なり、私のソリューションにはパラメーターが1つしかありません。ほとんどの場合、print関数はコンソールに情報のみを表示するため、複数のパラメーターを渡さず、パラメーターを文字列に変換するだけ"\(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
それは非常にコンパクトで、いくつかの有用な情報(タイムスタンプ、迅速なファイル名、コード行、関数名)を出力し、少なくとも私のテストでは、16進エディターで開いたときにアプリケーションのバイナリファイルにログに記録された文字列を見つけることができませんでした。
さらに簡単:アサーションがリリースビルドから削除され、そこからのみプリントを呼び出すという事実を利用してください。これにより、リリース用にビルドするときに空であるため、すべてのログ呼び出し(はい、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デバッグ領域に表示されます。
13:36:15.047-これはデバッグビルドでのみ処理されます。
私のプロジェクトはObjectiveCで開発されましたが、昨年からSwiftで新しいコードのマージを開始したので、以下のソリューションでSwiftが機能し、そのコードをMySwift定数ファイルに追加しました。
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
#if DEBUG
items.forEach {
Swift.print($0, separator: separator, terminator: terminator)
}
#endif
}