デバッグビルドでない場合、Swiftコード内のすべてのprintln()
呼び出しをグローバルに無視したいと思います。このための手順ごとの堅牢な手順が見つからないため、ガイダンスをいただければ幸いです。これをグローバルに行う方法はありますか、またはすべてのprintln()
を#IF DEBUG/#ENDIF
ステートメントで囲む必要がありますか?
前述のように、私は学生であり、従うにはもう少し明確に定義されたものが必要です。多くの研究を経て、私が従う必要があるシーケンスは次のとおりです。
Xcodeプロジェクトウィンドウの左側にあるファイルナビゲータの上部にあるプロジェクト名をクリックします。これは、プロジェクトの名前、ビルドターゲットの数、iOS SDKバージョンを含む行です。
Build Settingsタブを選択し、下にある「Swift Compiler-Custom Flags」セクションまでスクロールダウンします。 その他のフラグの横の下矢印をクリックして、セクションを展開します。
デバッグ行をクリックして選択します。マウスカーソルを行の右側に合わせてダブルクリックします。リストビューが表示されます。リストビューの左下にある+ボタンをクリックして、値を追加します。テキストフィールドがアクティブになります。
テキストフィールドに-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に受け入れさせるのに苦労していたので、初期化は必要以上に重いかもしれません。
ここで、println()
の代わりに新しいカスタム関数を使用する予定のクラスでカスタムクラスを参照する必要があります。これを該当するすべてのクラスのプロパティとして追加します。
let logFor = Log()
これで、println()
のインスタンスをlogFor.DLog()
に置き換えることができます。出力には、行が呼び出された関数の名前も含まれます。
クラス関数内では、そのクラスのクラス関数として関数のコピーを作成しない限り、関数を呼び出すことができず、println()
も入力に対してもう少し柔軟なので、使用できませんでした。これは私のコードのすべてのインスタンスで。
最も簡単な方法は、独自のグローバル関数を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 In Swift 2.0 println
はprint
に変更されました。残念ながら、現在は可変引数の最初のパラメーターがあります。これはクールですが、Swiftには "splat"演算子がないため、コードで変数を渡すことができないため、簡単にオーバーライドできないことを意味します(文字列でのみ作成できます)。ただし、通常の場合と同様に、値を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)
}
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
}
注:ここでは、デフォルトのセパレータをスペースに設定し、デフォルトのターミネータを改行に設定しました。必要に応じて、プロジェクトでこれを別の方法で構成できます。
お役に立てれば。
更新:
通常、この関数をグローバルスコープに配置して、Swiftのprint
関数の前に配置することをお勧めします。これを整理する最良の方法は、ユーティリティファイルをプロジェクト(DebugOptions.Swiftなど)に追加して、この関数をグローバルスコープに配置することです。
Swift 3以降、++
演算子は非推奨になります。この変更を反映するために、上記のスニペットを更新しました。
私の方法を含むこれらすべてのアプローチの問題は、print
引数を評価するオーバーヘッドを除去しないことです。どちらを使用しても、これは高価になります。
print(myExpensiveFunction())
唯一の適切な解決策は、条件付きコンパイルで実際の印刷呼び出しをラップすることです(DEBUG
はデバッグビルドに対してのみ定義されていると仮定しましょう):
#if DEBUG
print(myExpensiveFunction())
#endif
それだけで、myExpensiveFunction
がリリースビルドで呼び出されるのを防ぎます。
ただし、autoclosureを使用して評価を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())
は、評価されずにクロージャーにラップされ、リリースモードではまったく評価されないため、高価になりません。
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ステートメントを削除するために使用できるプリパーサーがありました。これは私の答え here =。ただし、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
}
Swift 4.2
以下のコードは私にとって完璧に機能しています:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
#if DEBUG
items.forEach {
Swift.print($0, separator: separator, terminator: terminator)
}
#endif
}
この関数はデフォルトのSwift printをミラーリングするので、print("hello world")
のようにまったく同じ方法で使用できます(セパレーターまたはターミネーターパラメーターを入力する必要はありません)。また、このように各アイテムを印刷すると、items
をSwift.print()
に直接渡すだけで表示される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 = default, terminator: String = default)
また、ドキュメントでは、デフォルトのセパレータは単一スペース(" "
)であり、デフォルトのターミネータは改行("\n"
)であるため、上記の私の答えは正確なSwift実装を反映しています複数の項目を印刷したり、セパレーター/ターミネーターを変更したりすることはありません。しかし、誰が知っている、あなたがしたいかもしれません。
XCode 8では、いくつかの 新しいビルド設定 が導入されました。
特にActive Compilation Conditions
と呼ばれるものは、Other Flags設定が行ったことと同様の方法で行います。
「アクティブなコンパイル条件」は、条件付きコンパイルフラグをSwiftコンパイラに渡すための新しいビルド設定です。
XCode 8(8.3.2でテスト済み)に従って、デフォルトでこれを取得します:
したがって、設定なしで次のように記述できます。
#if DEBUG
print("⚠️ Something weird happened")
#endif
このアプローチを広範に使用する場合は、このロギングロジックをラップするクラス/構造体/関数を作成することを強くお勧めします。これを今後さらに拡張することもできます。
さらに簡単に、-D DEBUG
がOTHER_Swift_FLAGS
デバッグビルド設定に設定されていることを確認した後:
#if !DEBUG
func println(object: Any) {}
func print(object: Any){}
#endif
Swift 2/Xcode 7では、println
は不要/使用されなくなりましたが、次の行を追加することができます。
func print(_ items: Any..., separator separator: String = default, terminator terminator: String = default)
Swift 4 Xcode 10.
多分あなたはこれを使うことができます
func dPrint(_ message: @autoclosure () -> Any) {
#if DEBUG
print(message())
#endif
}
@autoclosure
を使用する理由は、関数をメッセージパラメーターとして渡すと、その関数はデバッグモードでのみ呼び出され、パフォーマンスが低下するためです。
Swift.print(_ items: Any..., separator: String = default, terminator: String = default)
関数とは異なり、私のソリューションにはパラメーターが1つしかありません。ほとんどの場合、印刷関数はコンソールに情報を表示するだけなので、複数のパラメーターを渡さず、パラメーターを文字列に変換できます:"\(param1)"+"\(param2)"
私の解決策が好きだ
私のソリューションは、クラスの前に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
}
内容がだいたいdebug_println
を定義できます:
#if DEBUG
println()
#endif
私のソリューションのために私はそれを簡単にします
import UIKit
class DLog: NSObject {
init(title:String, log:Any) {
#if DEBUG
print(title, log)
#endif
}
}
それからそれを表示するには
_ = DLog(title:"any title", log:Any)