Правда ли, что нельзя использовать NSLog () в производственном коде? - PullRequest
153 голосов
/ 19 ноября 2008

Мне несколько раз говорили об этом на этом сайте, но я хотел убедиться, что это действительно так.

Я ожидал, что смогу распределить вызовы функций NSLog по всему коду и что Xcode / gcc автоматически удалит эти вызовы при сборке моих сборок Release / Distribution.

Стоит ли избегать этого? Если да, то какие альтернативы наиболее распространены среди опытных программистов Objective-C?

Ответы [ 12 ]

196 голосов
/ 19 ноября 2008

Макросы препроцессора действительно хороши для отладки. В NSLog () нет ничего плохого, но просто определить собственную функцию ведения журнала с лучшей функциональностью. Вот тот, который я использую, он включает имя файла и номер строки, чтобы упростить отслеживание операторов журнала.

#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif

Мне было проще поместить весь этот оператор в заголовок префикса, а не в его собственный файл. При желании вы можете создать более сложную систему ведения журнала, взаимодействуя с DebugLog с обычными объектами Objective-C. Например, у вас может быть класс ведения журнала, который записывает в свой собственный файл журнала (или базу данных) и содержит аргумент «приоритет», который вы можете установить во время выполнения, поэтому сообщения отладки не отображаются в вашей версии выпуска, а сообщения об ошибках ( если вы сделали это, вы можете сделать DebugLog (), WarningLog () и т. д.).

Да, и имейте в виду, #define DEBUG_MODE может быть повторно использовано в разных местах в вашем приложении. Например, в моем приложении я использую его, чтобы отключить проверку лицензионного ключа и разрешить запуск приложения только в том случае, если это происходит до определенной даты. Это позволяет мне распространять ограниченную по времени полнофункциональную бета-копию с минимальными усилиями с моей стороны.

77 голосов
/ 20 июня 2013

Поместите эти 3 строки в конец файла -prefix.pch:

#ifndef DEBUG
  #define NSLog(...) /* suppress NSLog when in release mode */
#endif

Вам не нужно ничего определять в своем проекте, потому что DEBUG определяется в настройках вашей сборки по умолчанию при создании проекта.

25 голосов
/ 19 ноября 2008

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

Приложения, которые засоряют системный журнал, раздражают и кажутся непрофессиональными.

23 голосов
/ 28 сентября 2009

Я не могу комментировать Ответ Марка Шарбонно , поэтому я опубликую это как ответ.

Помимо добавления макроса в предварительно скомпилированный заголовок, вы можете использовать конфигурации сборки Target для управления определением (или отсутствием определения) DEBUG_MODE.

Если вы выберете активную конфигурацию " Debug ", будет определено DEBUG_MODE, и макрос развернется до полного определения NSLog.

Выбор активной конфигурации " Release " не будет определять DEBUG_MODE, и ваш NSLog ging опущен в сборке выпуска.

Шаги:

  • Цель> Получить информацию
  • Вкладка Build
  • Поиск «Макросы препроцессора» (или GCC_PREPROCESSOR_DEFINITIONS)
  • Выберите конфигурацию: Отладка
  • Редактировать определение на этом уровне
  • Добавить DEBUG_MODE=1
  • Выберите конфигурацию: Выпуск
  • подтвердить DEBUG_MODE не установлено в GCC_PREPROCESSOR_DEFINITIONS

если вы опустите символ '=' в определении, вы получите ошибку от препроцессора

Также вставьте этот комментарий (показанный ниже) над определением макроса, чтобы напомнить вам, откуда исходит определение DEBUG_MACRO;)

// Target > Get Info > Build > GCC_PREPROCESSOR_DEFINITIONS
// Configuration = Release: <empty>
//               = Debug:   DEBUG_MODE=1
11 голосов
/ 19 ноября 2008

РЕДАКТИРОВАТЬ: Метод , опубликованный Marc Charbonneau и доведенный до моего сведения sho , намного лучше, чем этот .

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


Для достижения автоматического (и ожидаемого) поведения в Xcode:

В настройках проекта перейдите на вкладку «Сборка» и выберите конфигурацию «Отладка». Найдите раздел «Макросы препроцессора» и добавьте макрос с именем DEBUG_MODE.

...

РЕДАКТИРОВАТЬ: См. Ответ Марка Шарбонно о том, как правильно включить и отключить ведение журнала с помощью макроса DEBUG_MODE.

7 голосов
/ 19 ноября 2008

Я согласен с Мэтью. Нет ничего плохого в NSLog в рабочем коде. На самом деле, это может быть полезно для пользователя. Тем не менее, если единственная причина, по которой вы используете NSLog, заключается в том, чтобы помочь отладке, то да, это следует удалить перед выпуском.

Кроме того, поскольку вы пометили это как вопрос iPhone, NSLog забирает ресурсы, а это то, чего у iPhone очень мало. Если вы NSLogging что-нибудь на iPhone, это отнимает процессорное время из вашего приложения. Используйте это с умом.

4 голосов
/ 19 июля 2013

Простая правда в том, что NSLog просто медленный.

Но почему? Чтобы ответить на этот вопрос, давайте выясним, что делает NSLog, а затем, как он это делает.

Что конкретно делает NSLog?

NSLog делает 2 вещи:

Он записывает сообщения журнала в систему Apple System Logging (asl). Это позволяет сообщениям журнала показываться в Console.app. Он также проверяет, идет ли поток приложения stderr на терминал (например, когда приложение запускается через XCode). Если это так, он записывает сообщение журнала в stderr (чтобы оно отображалось в консоли Xcode).

Запись в STDERR не кажется сложной. Это можно сделать с помощью fprintf и ссылки на дескриптор файла stderr. Но что насчет asl?

Лучшая документация, которую я нашел об ASL, - это блог из Питера Хоси из 10 частей: ссылка

Не вдаваясь в подробности, основной момент (с точки зрения производительности) таков:

Чтобы отправить сообщение журнала в средство ASL, вы в основном открываете клиентское соединение с демоном ASL и отправляете сообщение. НО - каждый поток должен использовать отдельное клиентское соединение. Таким образом, для обеспечения безопасности потоков каждый раз, когда вызывается NSLog, он открывает новое клиентское соединение asl, отправляет сообщение, а затем закрывает соединение.

Ресурсы можно найти здесь & здесь .

2 голосов
/ 16 февраля 2013

С точки зрения безопасности, это зависит от того, что регистрируется. Если NSLog (или другие регистраторы) записывают конфиденциальную информацию, то вы должны удалить регистратор в рабочем коде.

С точки зрения аудита, аудитор не хочет рассматривать каждое использование NSLog, чтобы убедиться, что он не регистрирует конфиденциальную информацию. Он / она просто скажет вам удалить регистратор.

Я работаю с обеими группами. Мы проводим аудит кода, пишем руководства по кодированию и т. Д. Наше руководство требует, чтобы ведение журнала было отключено в рабочем коде. Так что внутренние команды знают, что не стоит пробовать;)

Мы также отклоним внешнее приложение, которое входит в систему, поскольку мы не хотим принимать на себя риск, связанный со случайной утечкой конфиденциальной информации. Нам все равно, что говорит нам разработчик. Его просто не стоит тратить время на расследование.

И помните, мы определяем «чувствительный», а не разработчик;)

Я также воспринимаю приложение, которое выполняет много журналирования, как приложение, готовое взорваться. Есть причина, по которой так много журналирования выполняется / необходимо, и это обычно не стабильность. Это прямо там с «сторожевыми» потоками, которые перезапускают зависшие сервисы.

Если вы никогда не проходили обзор архитектуры безопасности (SecArch), мы рассмотрим такие вещи.

2 голосов
/ 09 октября 2012

Как отмечено в других ответах, вы можете использовать #define, чтобы изменить, используется ли NSLog или нет во время компиляции.

Однако более гибкий способ - использовать библиотеку журналов, такую ​​как Cocoa Lumberjack , которая позволяет вам изменять, регистрируется ли что-либо также во время выполнения.

В вашем коде замените NSLog на DDLogVerbose или DDLogError и т. Д., Добавьте #import для определений макросов и т. Д. И настройте регистраторы, часто в методе applicationDidFinishLaunching.

Чтобы иметь тот же эффект, что и NSLog, код конфигурации:

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];
1 голос
/ 17 декабря 2013

Имейте в виду, что NSLogs может замедлять интерфейс / основной поток. Лучше всего удалить их из сборок релиза, если в этом нет крайней необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...