Удалить println() для выпуска версии iOS Swift

Я хотел бы глобально игнорировать все вызовы println() в моем коде Swift, если я не в сборке Debug. Я не могу найти надежных пошаговых инструкций для этого и буду благодарен за руководство. есть ли способ сделать это глобально, или мне нужно окружить все println() операциями #IF DEBUG/#ENDIF?

Ответ 1

Как уже отмечалось, я студент и нуждаюсь в чём-то определенном, более понятном для понимания. После многих исследований последовательность, которую мне нужно было выполнить, следующая:

Нажмите на название проекта в верхней части Файлового навигатора слева от окна проекта Xcode. Это строка с именем проекта, количеством целей сборки и версией IOS SDK.

Выберите вкладку Настройки сборки и прокрутите вниз до раздела " Swift Compiler - Custom Flags" внизу. Нажмите стрелку вниз рядом с Другие флаги, чтобы развернуть раздел.

Нажмите на строку Отладка, чтобы выбрать ее. Наведите указатель мыши на правую сторону линии и дважды щелкните мышью. Появится список. Нажмите кнопку + в левом нижнем углу списка, чтобы добавить значение. Текстовое поле станет активным.

В текстовом поле введите текст -D DEBUG и нажмите Вернуть, чтобы зафиксировать строку.

Добавьте в проект новый файл 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() также немного более гибким с вводом, поэтому я не мог используйте это в каждом экземпляре в моем коде.

Ответ 2

Самый простой способ - поставить свою собственную глобальную функцию перед 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 В 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)
}

Ответ 3

Обновлено для 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() как обычно в своем коде, но она удалит выходные данные для сборок ebug не -D. Вот подпись функции, которую вы можете добавить в глобальной области видимости, чтобы сделать это в 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 оператор ++ устарел. Я обновил фрагмент выше, чтобы отразить это изменение.

Ответ 4

Проблема всех этих подходов, в том числе и моего, заключается в том, что они не снимают затраты на оценку аргументов 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()) таким образом, перестает быть дорогим, потому что вызов обернут в замыкание без оценки, а в режиме освобождения он вообще не будет оцениваться.

Ответ 5

Вот функция, которую я использую, которая отлично работает в 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) в консоли, где они могут иногда теряться в море других сообщений

  • timestamp/даты

  • поток, в котором он запускается - в моем случае это либо MainThread (который я называю UI), либо не MainThread (который я называю BG, для фонового потока)

  • имя файла, в котором находится сообщение gLog

  • функция в файле, в котором находится сообщение gLog

  • номер строки сообщения gLog

  • фактическое сообщение gLog, которое вы хотели бы распечатать

Надеюсь, что это полезно для кого-то еще!

Ответ 6

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("hello world") (не нужно вводить параметры разделителя или терминатора). Кроме того, печать каждого элемента таким образом избавляет от надоедливых скобок массива вокруг операторов печати, которые появляются, если вы просто передадите 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 = default, terminator: String = default)

В документах также говорится, что разделителем по умолчанию является один пробел (" "), а терминатором по умолчанию является символ новой строки ("\n"), поэтому мой ответ выше отражает точную реализацию Swift - хотя я никогда не печатаю более одной вещи или изменить разделитель/терминаторы. Но кто знает, вы можете захотеть.

Ответ 7

Протестировано с помощью 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 user input through delegate callbacks
   nameTextField.delegate = self
}

Ответ 8

XCode 8 представил несколько новых настроек сборки.
В частности, один из них, упомянутый в " Active Compilation Conditions выполняет те же действия, что и настройки других флагов.

"Условия активной компиляции" - это новая настройка сборки для передачи флагов условной компиляции компилятору Swift.

Согласно XCode 8 (протестировано в 8.3.2) вы получите это по умолчанию:

enter image description here

Так что без какой-либо конфигурации вы можете написать следующее:

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

Я настоятельно рекомендую вам, если вы используете этот подход, экстенсивно создайте класс/структуру/функцию, которая обернет эту логику регистрации. Вы можете расширить это дальше по дороге.

Ответ 9

Еще проще, убедившись, что -D DEBUG установлен для настроек сборки OTHER_SWIFT_FLAGS Debug:

#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)

Ответ 10

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), мое решение имеет только один параметр, поскольку в большинстве случаев мы не передаем несколько параметров в качестве печати Функция показывает только информацию в консоли, мы можем просто преобразовать параметры в строку: "\(param1)"+"\(param2)", верно? надеюсь, вам понравится мое решение

Ответ 11

Вы можете определить debug_println, содержимое которого будет примерно:

#if DEBUG
  println()
#endif

Ответ 12

Мое решение использует этот код в 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 

}

Ответ 13

для моего решения я делаю это простым

import UIKit

class DLog: NSObject {

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

   }

}

то для его отображения просто вызовите

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

Ответ 14

Было бы неплохо, если бы у Apple была настройка сборки "Подавить операторы печати" (отключена по умолчанию для отладочных сборок, включена по умолчанию для выпуска/архивирования), которая полностью закоротила бы всю логику в операторах печати, включая оценку аргументов. Я склонен использовать много печатных утверждений, и необходимость оборачивать их в условные выражения или постоянно комментировать/раскомментировать их обременительна и грязна.

Или я что-то пропустил? В настоящее время я использую Xcode 11.1 и не могу его найти.

Ответ 15

Выше все хорошие ответы. Вы могли бы просто использовать Swift.debugPrint() вместо этого отключить все отпечатки в режиме деблокирования.