Хотя я уже отмечал ответ как принятый и предлагал щедрость в то время, с тех пор я нашел истинный ответ, который я искал, и понял, что мой вопрос не очень ясен - прежде всего потому, что я не знал что спросить.
Как взаимодействовать с @autoreleasepool:
Когда компилятор встречает на @autoreleasepool { }
блок, он автоматически вставляет два вызова функции.При открытии блока он вставляет вызов функции C:
void *_objc_autoreleasePoolPush(void);
На закрытии @autoreleasepool
блока (в том числе, когда return
или break
встречается в пределах - но не тогда, когда исключение создается в соответствии с LLVM docs) компилятор вставляет вызов функции C:
_objc_autoreleasePoolPop(void *ctxt); // ctxt is Apple's label for the param
Я считаю, что параметр void *ctxt
является показателем того, где ток @autoreleasepool
начал (полезно только в реализации компании Apple - см here). В любом случае я нашел, что его можно легко игнорировать в пользовательских реализациях.
-autorelease
селектор:
По существу методика состоит в следующем:
- Создать (если наружный блок) или знак (если внутренний блок) объект autorelease бассейн (в C или C++) в
_objc_autoreleasePoolPush()
. Мне кажется, проще всего использовать структуру данных Stack для этого, но YMMV.
- Убедитесь, что этот объект находится в области действия в селекторе
-autorelease
, или у вас есть безопасный способ доступа к нему.
- Добавьте объект к объекту пула автоопределения (каким бы то ни было способом) внутри
-autorelease
.
- В
_objc_autoreleasePoolPop(void *)
отправьте сообщение -release
всем пунктам в объекте пула авторефератов до тех пор, пока маркер, установленный в _objc_autoreleasePoolPush()
, не достигнут (или вы не дойдете до дна пула). Затем выполните любую дополнительную необходимую очистку.
Конечные ноты на резьбе:
Objective-C @autoreleasepools
должны быть поточно-местный. То есть, каждый поток имеет свой собственный (если он требует один). В результате _objc_autoreleasePoolPush()
должен иметь некоторый способ определить, имеет ли текущий поток уже активный пул автозапуска и создает объект, если он этого не делает.
Точно так же, поэтому, при создании потока, поток должен сначала открыть @autoreleasepool { }
блок, прежде чем он делает что-нибудь еще (в Objective-C), и последнее, что он должен сделать, это близко, что @autoreleasepool
блок (неявно break
или return
или с явным провалом).
Три заключительных замечания:
- Если вы хотите реализовать свой собственный объект autorelease бассейна, вы будете нуждаться в некоторой форме локальной памяти потока для ее хранения.
- Этот последний вопрос о потоковом подключении - вот почему каждая программа Objective-C должна начинаться с блока
@autoreleasepool { }
в main()
.
- Вот почему Apple просят вас использовать класс
NSThread
и GCD
, а не резьбу DIY - они заботятся обо всем этом для вас!
Спасибо вам большое! (И для вашего комментария к предыдущему ответу - связанный с вами файл был чрезвычайно полезен). Кстати, причина, по которой мне, возможно, придется отказаться от механизма автоматического пула, состоит в том, что я могу в конечном итоге запустить такую систему в пользовательский класс NSObject, если я могу сделать ее достаточно эффективной (и довольно сложно получить более эффективную, чем компилятор!) – Ephemera
На самом деле, вы можете сделать хороший в C++, потому что деструкторы для распределенных по стеку переменных будут вызываться в точке выхода любой функции, в которой она используется. – CodaFi