web-dev-qa-db-ja.com

リリースバージョンiOSのprintln()を削除しますSwift

デバッグビルドでない場合、Swiftコード内のすべてのprintln()呼び出しをグローバルに無視したいと思います。このための手順ごとの堅牢な手順が見つからないため、ガイダンスをいただければ幸いです。これをグローバルに行う方法はありますか、またはすべてのprintln()#IF DEBUG/#ENDIFステートメントで囲む必要がありますか?

76
Nate Birkholz

前述のように、私は学生であり、従うにはもう少し明確に定義されたものが必要です。多くの研究を経て、私が従う必要があるシーケンスは次のとおりです。

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()も入力に対してもう少し柔軟なので、使用できませんでした。これは私のコードのすべてのインスタンスで。

16
Nate Birkholz

最も簡単な方法は、独自のグローバル関数を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 printlnprintに変更されました。残念ながら、現在は可変引数の最初のパラメーターがあります。これはクールですが、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)
}
97
matt

Swift 4.x向けに更新:

Swift 2.0/3.0およびXcode 7/8がベータ版ではなくなったため、リリースビルドで印刷機能を無効にする方法にいくつかの変更が加えられました。

上記の@mattと@Nate Birkholzによって言及されたいくつかの重要なポイントがまだ有効です。

  1. println()関数はprint()に置き換えられました

  2. #if DEBUGマクロを使用するには、「Swift Compiler-Custom Flags -Other Flags」を定義して、値-D DEBUGを含める必要があります

  3. コードで通常どおり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以降、++演算子は非推奨になります。この変更を反映するために、上記のスニペットを更新しました。

46
Glavid

私の方法を含むこれらすべてのアプローチの問題は、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())は、評価されずにクロージャーにラップされ、リリースモードではまったく評価されないため、高価になりません。

32
matt

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
    }

次に、生成される出力の例を示します。

screenshot of output

説明:

  • 緑色のチェックマークを使用して、コンソールで印刷(gLog)メッセージをすばやく確認できるようにします。このメッセージは、他のメッセージの海で時々失われる可能性があります

  • 時刻/日付スタンプ

  • 実行されているスレッド-私の場合、MainThread(UIと呼ばれる)か、MainThread(バックグラウンドスレッドのBGと呼ばれる)ではありません

  • gLogメッセージが存在するファイルの名前

  • gLogメッセージが存在するファイル内の関数

  • gLogメッセージの行番号

  • 印刷したい実際のgLogメッセージ

これが他の誰かに役立つことを願っています!

9
Gene Loparco

Swift 2.1Xcode 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
}

コンパイラフラグを設定する場所は次のとおりです。

enter image description here

すべてのフラグをオンにした出力例は次のようになります。

   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
}
8
Ronny Webers

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")のようにまったく同じ方法で使用できます(セパレーターまたはターミネーターパラメーターを入力する必要はありません)。また、このように各アイテムを印刷すると、itemsSwift.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実装を反映しています複数の項目を印刷したり、セパレーター/ターミネーターを変更したりすることはありません。しかし、誰が知っている、あなたがしたいかもしれません。

6
Trev14

XCode 8では、いくつかの 新しいビルド設定 が導入されました。
特にActive Compilation Conditionsと呼ばれるものは、Other Flags設定が行ったことと同様の方法で行います。

「アクティブなコンパイル条件」は、条件付きコンパイルフラグをSwiftコンパイラに渡すための新しいビルド設定です。

XCode 8(8.3.2でテスト済み)に従って、デフォルトでこれを取得します:

enter image description here

したがって、設定なしで次のように記述できます。

#if DEBUG
    print("⚠️ Something weird happened")
#endif

このアプローチを広範に使用する場合は、このロギングロジックをラップするクラス/構造体/関数を作成することを強くお勧めします。これを今後さらに拡張することもできます。

6
Javier Cadiz

さらに簡単に、-D DEBUGOTHER_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)
4
Rivera

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)"私の解決策が好きだ

2
Jiangshi Fresh

私のソリューションは、クラスの前に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 

}
0
Varun Naharia

内容がだいたいdebug_printlnを定義できます:

#if DEBUG
  println()
#endif
0
Ian MacDonald

私のソリューションのために私はそれを簡単にします

import UIKit

class DLog: NSObject {

   init(title:String, log:Any) {
       #if DEBUG
           print(title, log)
       #endif

   }

}

それからそれを表示するには

_ = DLog(title:"any title", log:Any)