Почему NSLog иногда печатает восьмеричные символы укода?

Я запускаю следующий код в функции viewDidLoad приложения одноразового просмотра iPad:

/*
 *  Print the string.  A lot.
 */
for (int i = 0; i < 300; i++) {
    NSLog(@"%d\n", i);
    NSLog(@"⊢ ⊣ ⊥ ⊻ ⊼ ⊂ ⊃ ⊑ ⊒ \n");
}

Результат выглядит следующим образом:

2013-02-04 20:17:49.718 testplay[59585:c07] 228
2013-02-04 20:17:49.718 testplay[59585:c07] ⊢ ⊣ ⊥ ⊻ ⊼ ⊂ ⊃ ⊑ ⊒ 
2013-02-04 20:17:49.719 testplay[59585:c07] 229
2013-02-04 20:17:49.719 testplay[59585:c07] ⊢ ⊣ ⊥ ⊻ ⊼ ⊂ ⊃ ⊑ ⊒ 
2013-02-04 20:17:49.719 testplay[59585:c07] 230
2013-02-04 20:17:49.720 testplay[59585:c07] ⊢ ⊣ ⊥ ⊻ ⊼ ⊂ ⊃ ⊑ ⊒ 
2013-02-04 20:17:49.720 testplay[59585:c07] 231
2013-02-04 20:17:49.720 testplay[59585:c07] ⊢ ⊣ ⊥ ⊻ ⊼ ⊂ ⊃ \342\212\221 ⊒ 
2013-02-04 20:17:49.723 testplay[59585:c07] 232
2013-02-04 20:17:49.724 testplay[59585:c07] ⊢ ⊣ ⊥ ⊻ ⊼ ⊂ ⊃ ⊑ ⊒ 

Октал почти всегда встречается на одном и том же символе, и икота происходит случайным образом примерно 3 раза за каждый ход.

Несмотря на то, что в NSLog() он относительно безвреден, это означает, что символы Unicode могут обрабатываться нерегулярно на некотором уровне. Если есть история этого поведения или какой-то ресурс, на который я мог бы смотреть, это было бы здорово.

[добавление: удалена ссылка на то, как я столкнулся с этой проблемой. Понимание того, почему и как NSLog получает поврежденное чтение символа юникода, я надеюсь здесь.]

Ответ 1

Краткая версия:

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

Если мое предположение верно, это только проблема вывода отладчика Xcode и не подразумевает никаких проблем с Unicode в приложении.

Длинная версия:

Если вы запустите свое приложение в симуляторе, lsof -p <pid_of_simulated_app> показывает, что стандартный ошибка (файловый дескриптор 2) перенаправляется на псевдотерминал:

# lsof -p 3251
...
testplay 3251 martin    2w     CHR               16,2     0t131     905 /dev/ttys002
...

И lsof -p <pid_of_Xcode> показывает, что Xcode имеет тот же псевдотерминал open:

# lsof -p 3202
...
Xcode   3202 martin   51u     CHR               16,2       0t0     905 /dev/ttys002
...

NSLog() записывается в стандартную ошибку. С помощью индикатора системного вызова "dtruss" можно видеть что Xcode считывает сообщение журнала с псевдотерминала. Для одного сообщения журнала

NSLog(@"⊢ ⊣ ⊥ ⊻ ⊼ ⊂ ⊃ ⊑ ⊒ \n");

он выглядит так:

# dtruss -n Xcode -t read_nocancel
 3202/0xe101:  read_nocancel(0x31, "2013-02-05 08:57:44.744 testplay[3251:11303] \342\212\242 \342\212\243 ... \342\212\222 \n\0", 0x8000)       = 82 0

Но для многих операторов NSLog(), следующих друг за другом быстро, иногда происходит следующее:

# dtruss -n Xcode -t read_nocancel
...
 3202/0xd828:  read_nocancel(0x33, "2013-02-05 08:39:51.156 ...", 0x8000) = 1024 0
 3202/0xd87b:  read_nocancel(0x33, "\212\273 \342\212\274 ...", 0x8000) = 24 0

Как вы можете видеть, Xcode прочитал 1024 байта из псевдотерминала, а следующее прочитанное начинается с неполной последовательности UTF-8. В этом случае Xcode "не видит", что последний байт первого считывания и первые два байта второго чтения являются частями такой же последовательности UTF-8. Я предполагаю, что Xcode обрабатывает все 3 байта как недопустимые последовательности UTF-8 и печатает их как восьмеричные числа.

Ответ 2

Обходной путь в вашей петле помещает "fflush (stderr)"; после второго заявления NSLog; Это заставит stderr фиксировать и записывать буфер перед продолжением.