Статьи

Совет: настройте NSLog для упрощения отладки

В этом кратком совете мы узнаем, как настроить вывод, генерируемый NSLog, для более эффективной отладки программ. Читай дальше!


По умолчанию NSLog отображает вывод в следующем формате:

1
Date Time OurApp[<processid>] NSLog output

Пример из реальной жизни может выглядеть так:

1
20130803 00:35:53.038 TestApp[460:c07] Value of result = 20

Вывод по умолчанию хорош, но оставляет желать лучшего. В большинстве случаев мы хотим видеть следующее в операторе журнала:

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

Короче говоря, мы бы хотели, чтобы NSLog был таким:

1
(ClassName MethodName) (SourceFileName:LineNumber) NSLog output

Давайте сначала посмотрим, как работает NSLog без изменений. NSLog — это просто функция C, встроенная в базовый каркас Cocoa, и она ведет себя так же, как любая другая переменная функция C. В частности, NSLog отправляет сообщения об ошибках в средство Apple System Log. Он делает это просто, передавая свои аргументы функции NSLogv .

Поскольку NSLog — это просто оболочка для NSLogv, мы можем переопределить NSLog с помощью нашего собственного пользовательского вызова NSLogv. Это именно то, что я покажу вам, как это сделать в этом уроке.


Создайте новый проект iOS в Xcode с шаблоном Empty Application . Назовите это ExtendNSLog . Установите флажок «Автоматический подсчет ссылок», но снимите флажки «Основные данные» и «Юнит тесты».

Создайте проект iOS с шаблоном «Пустое приложение»
Название продукта должно быть «ExtendNSLog»

Теперь создайте заголовочный файл вместе с проектом. Выберите « Новый файл»> «Класс Objective-C» . Установите имя класса в ExtendNSLogFunctionality. который будет подклассом NSObject.

Создать шаблон класса Objective-C
Установите имя класса в ExtendNSLogFunctionality

Откройте ExtendNSLogFunctionality.h и поместите следующий код в заголовок:

1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
 
#ifdef DEBUG
#define NSLog(args…) ExtendNSLog(__FILE__,__LINE__,__PRETTY_FUNCTION__,args);
#else
#define NSLog(x…)
#endif
 
void ExtendNSLog(const char *file, int lineNumber, const char *functionName, NSString *format, …);

Приведенное выше условие будет определять оператор NSLog только когда определено DEBUG. Когда DEBUG не определен, оператор NSLog ничего не сделает. Возникает вопрос: как вы управляете, когда определяется DEBUG? Это можно сделать, назначив DEBUG = 1 в настройках препроцессора для вашего проекта.

Для этого щелкните цель приложения и выберите вкладку «Настройки сборки». Далее убедитесь, что выбраны опции «Все» и «Комбинированные». Выполните поиск «предварительной обработки» и найдите раздел «Макросы препроцессора». Затем просто добавьте «DEBUG = 1» в раздел «Отладка».

Добавьте флаг DEBUG = 1 в настройки препроцессора.

Обратите внимание, что в более поздних шаблонах проектов Xcode уже будет макрос DEBUG = 1, определенный для конфигурации сборки Debug в разделе Макросы препроцессора. Для получения дополнительной информации обратитесь к этому сообщению на StackOverflow .

С заданным макросом отладки наша следующая задача — написать собственную версию NSLog. Откройте ExtendNSLogFunctionality.m и добавьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#import «ExtendNSLogFunctionality.h»
 
void ExtendNSLog(const char *file, int lineNumber, const char *functionName, NSString *format, …)
{
    // Type to hold information about variable arguments.
    va_list ap;
 
    // Initialize a variable argument list.
    va_start (ap, format);
     
    // NSLog only adds a newline to the end of the NSLog format if
    // one is not already there.
    // Here we are utilizing this feature of NSLog()
    if (![format hasSuffix: @»\n»])
    {
        format = [format stringByAppendingString: @»\n»];
    }
     
    NSString *body = [[NSString alloc] initWithFormat:format arguments:ap];
     
    // End using variable argument list.
    va_end (ap);
     
    NSString *fileName = [[NSString stringWithUTF8String:file] lastPathComponent];
    fprintf(stderr, «(%s) (%s:%d) %s»,
            functionName, [fileName UTF8String],
            lineNumber, [body UTF8String]);
}

Теперь добавьте файл ExtendNSLogFunctionality.h в заголовочный файл префикса Prefix.pch в разделе #ifdef __OBJC__.

1
2
3
4
5
#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import «ExtendNSLogFunctionality.h»
#endif

Чтобы лучше понять заголовки префиксов, взгляните на эту запись в Википедии . Относительно лучших практик заголовка префикса, прочитайте этот пост StackOverflow .


Теперь добавьте NSLog в любом месте вашего кода проекта. В моем случае я решил добавить один в метод AppDelegate.m -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions .

1
2
int result = 20;
NSLog(@»Value of result : %d», result);

Если вы соберете и запустите проект с конфигурацией Debug сейчас, вы должны увидеть что-то вроде этого:

1
([AppDelegate application:didFinishLaunchingWithOptions:]) (AppDelegate.m:21) Value of result : 20

Ура! Этот вывод гораздо полезнее, чем реализация по умолчанию. Надеюсь, вы обнаружите, что эта техника сэкономит вам много времени при отладке ваших собственных программ!