У меня есть библиотека C ++, которую я хочу представить в качестве среды Objective-C, так что ее будет проще использовать разработчикам Objective-C.Оборачивая библиотеку C ++, я столкнулся с одной конкретной проблемой, связанной с объектами автоматического выпуска и потоковой обработкой.
Одна особенность библиотеки заключается в том, что разработчик может зарегистрировать «регистратор» для получения уведомительных сообщений в виде обратных вызовов отбиблиотека.Уведомление из библиотеки использует типы C ++ и получено из другого потока (POSIX), поэтому я создал закрытый класс-оболочку C ++ для обработки этого: он получает обратный вызов, превращает аргумент char * в NSString и передает егок экземпляру регистратора Objective-C, предоставленному пользователем.Все это работает очень хорошо и выглядит примерно так:
// Is called from the C++ library from another posix thread
void ObjCLoggerWrapper::LogMessage(const char *message)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Pass string to the user-provided Objective-C instance called "Logger"
[Logger logMessage:[[[NSString alloc] initWithUTF8String:message] autorelease]];
[pool release];
}
В качестве примера обратного вызова пользователя я написал этот простой метод для сбора всех записей в элементе NSString m_text в экземпляре пользовательского класса (дляиспользоваться в другом месте, но это не имеет значения).
-(void) logMessage: (NSString*)message
{
@synchronized(self)
{
m_text = [m_text stringByAppendingFormat:@"%04d: %@\r\n", m_lineno++, message];
}
}
Пока все хорошо.Или я так думал.Но вот претензия:
Все автоматически выпущенные объекты в методе обратного вызова пользователя будут принадлежать NSAutoreleasePool оболочки и, следовательно, будут освобождены после завершения обратного вызова.
Упс!Это означает, что моя строка m_text, созданная неявно как объект автоматического освобождения с помощью сообщения stringByAppendingFormat, будет освобождена после завершения logMessage и станет зомби.При доступе в следующий раз код будет аварийно завершен.Пользователь, конечно, наверняка и по праву не ожидает этого.Мне самому пришлось несколько раз почесать голову, прежде чем я понял, что происходит.
Поэтому мой вопрос: как мы должны обращаться с автоматически освобожденными объектами при выполнении обратных вызовов к пользовательскому коду из другого потока?
Я вижу несколько возможных вариантов.Ни одно из них не является идеальным, и Google не помогает (отсюда и этот вопрос).
- Скажите пользователю «не создавайте объекты автоматического освобождения в вашем коде обратного вызова».Не хорошо: такие объекты часто создаются невольно, например, с помощью stringByAppendingFormat и множества других методов инфраструктуры.Нет предупреждений, кроме более позднего, трудно отлаживаемого сбоя.
- Нет NSAutoreleasePool.Отсутствие одного приведет к появлению предупреждений, если пользователь попытается создать автоматически выпущенные объекты.Определенно не очень, но предупреждает пользователя о проблеме надежным способом.И пользователь может «просто» добавить свой NSAutoreleasePool, чтобы исправить ситуацию.Но опять же: не красиво.
- Нет NSAutoreleasePool и запустите обратный вызов в главном потоке, используя executeSelectorOnMainThread.Любые новые объекты автоматического выпуска будут попадать в пул основного потока.Я думаю, что это безопасно, но приветствуются комментарии - могут ли, например, обратные вызовы всегда выполняться в главном потоке?Этот подход требует более деликатного кодирования в оболочке, чтобы избежать тупиковых потоков и ждать результата, но пока это мой предпочтительный выбор.
Просто чтобы было ясно: переписывать мою собственную оболочку нетпроблема.Моим главным приоритетом является создание решения, которое будет работать гладко и без проблем для пользователя платформы Objective-C.Спасибо!