2013-07-04 6 views
0

Основываясь на Bavarious's answer to this SO question, я убежден, что @autoreleasepool - это функция языка Objective-C, если вы создаете LLVM/clang.Как взаимодействовать с @autoreleasepool

В этом случае, как можно переписать следующий код, чтобы использовать @autoreleasepool вместо NSAutoreleasePool в среде, отличной от ARC?

[NSAutoreleasePool addObject:anObject]; // (*) 

фона: я принципиально хочу написать пользовательскую реализацию -autorelease, которая не никак взаимодействуют с NSAutoreleasePool класса:

@autoreleasepool { 
    SomeCls *obj = [[SomeCls alloc] init]; 
    [obj autorelease]; // Does not go through an NSAutoreleasePool object 
    // ... 
} 

ответ

3

@autoreleasepool { } - это новая языковая функция, предназначенная для устранения необходимости сливать текущий пул в каждой точке выхода функции. Теперь, вместо того, чтобы написать:

void f(void) { 
    //Make a new pool 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init]; 
    //Every inner scope that exits prematurely requires a drain 
    if (life_is_good) { 
     for (int i = 0; i < 1000; i++) { 
      NSObject *obj = [[[NSObject alloc]init]autorelease]; 
      //breaks out of the loop, and the function, so drain the pool 
      if (life_is_bad) { 
       [pool drain]; 
       return; 
      } 
     } 
     //Life gets bad below here, so return 
     [pool drain]; 
     return; 
    } 
    //end of function requires drain. 
    [pool drain]; 
} 

Вы можете обернуть всю функцию в @autoreleasepool { } директивы и компилятор вставит соответствующие водостоки, где функция возвращает:

void f(void) { 
    //Make a new pool 
    @autoreleasepool { 
     if (life_is_good) { 
      for (int i = 0; i < 1000; i++) { 
       NSObject *obj = [[[NSObject alloc]init]autorelease]; 
       //breaks out of the loop, and the function, so drain the pool 
       if (life_is_bad) { 
        //[pool drain]; auto-drain 
        return; 
       } 
      } 
      //[pool drain]; auto-drain 
      return; 
     } 
     //[pool drain]; auto-drain 
    } 
} 

Я понятия не имею, почему вы хотели бы отказаться от механизма автоматического пула, так как он обрабатывает огромное количество работы для вас (управление страницей, управление пул-потоками, выпуск объектов своевременным, но эффективным образом), но это не должно быть так много из хлопот, чтобы получить примитивный NSAutoreleasePool и запустить. Для любого autoreleasepool потребуется менеджер страниц (malloc() и realloc()), хранилище указателей (простой массив) и какой-либо способ определить, завершилась ли текущая область, в которой он используется, (-drain). Простой пул может быть просто потокобезопасным синглтоном, но будьте осторожны с тем, сколько объектов вы пытаетесь вставить и удалить за один раз с этой реализацией. Вы просто можете segfault, если попытаетесь выделить слишком много горячих страниц для слишком большого количества объектов.

+0

Спасибо вам большое! (И для вашего комментария к предыдущему ответу - связанный с вами файл был чрезвычайно полезен). Кстати, причина, по которой мне, возможно, придется отказаться от механизма автоматического пула, состоит в том, что я могу в конечном итоге запустить такую ​​систему в пользовательский класс NSObject, если я могу сделать ее достаточно эффективной (и довольно сложно получить более эффективную, чем компилятор!) – Ephemera

+0

На самом деле, вы можете сделать хороший в C++, потому что деструкторы для распределенных по стеку переменных будут вызываться в точке выхода любой функции, в которой она используется. – CodaFi

0

В конце @autoreleasepool блока , автореализованные объекты отправляются сообщением release.

Если вы хотите переобучить метод autorelease, возможно, вам не захочется напрямую отправить release сообщения в ваш NSObject подкласс. Если это так, переопределение release с некоторым особым поведением может привести к вашему бизнесу.

+0

Означает ли это, что компилятору (не-ARC) все равно, что на самом деле находится в реализации '-autorelease'?Он просто кладет все, что находится внутри него в конце '@ autoreleasepool'? – Ephemera

+0

Из того, что я понимаю, '@ autoreleasepool' - это всего лишь механизм, который заменяет' NSAutoreleasePool'. Он имеет тот же эффект, но это зависит от реализации компилятора, а не от того, что есть в Cocoa. Чтобы ответить на ваш комментарий, я думаю, вы все равно должны называть '[NSObject autorelease]' в своем пользовательском методе autorelease. – rems4e

0

фона: я принципиально хочу написать пользовательскую реализацию -autorelease, который не каким-либо образом взаимодействовать с классом NSAutoreleasePool:

Что заставляет вас думать, что -autorelease взаимодействует с NSAutoreleasePool?

Пока вы работаете на прошивке 5+ или Mac OS X 10.7+, как @autoreleasepool и -[NSObject autorelease] будет использовать функции управления во время выполнения autorelease пула, и не имеет ничего общего с NSAutoreleasePool на всех (и NSAutoreleasePool на этих ОСЕ версии - это просто оболочка вокруг функций времени исполнения).

1

Основываясь на ответе Бавария на этот вопрос, я убежден, что @autoreleasepool теперь является функцией языка Objective-C, если вы создаете LLVM/clang.

Исправить.

В этом случае, как можно переписать следующий код, чтобы использовать @autoreleasepool вместо NSAutoreleasePool в среде, отличной от ARC?

[NSAutoreleasePool addObject: anObject]; // (*)

Doc:Обычно не вызывать этот метод напрямую отправлять autorelease объекта вместо этого.

@autoreleasepool { 
id anObject = ...; 
[anObject autorelease]; 
} 

фона: я принципиально хочу написать пользовательскую реализацию -autorelease, не каким-либо образом взаимодействовать с классом NSAutoreleasePool:

Вы не можете даже подкласс NSAutoreleasePool больше (значимым образом). Если вы никогда не передадите объект другим API-интерфейсам, свободным от автообновления, ваши объекты попадут в текущий пул автозаполнения. Механика этого сегодня даже не основана на объектах.

Если это невозможно, я буду открыт для обходных предложений, не связанных с NSAutoreleasePool.

Не уверен, что проблема, которую вы пытаетесь решить, но вы можете продлить время жизни с помощью:

NSMutableArray * fauxAutoreleasePool = NSMutableArray.new; 
id anObject = ...; 
[fauxAutoreleasePool addObject:anObject]; 
... 
[fauxAutoreleasePool removeAllObjects]; // << explicitly drain, which also happens when the array is destroyed 

Конечно, вы можете написать свою собственную реализацию. Почему вы пишете свои собственные, по-прежнему остается загадкой, и не очень хорошая идея для производственного кода (@autoreleasepool работает хорошо).

0

Хотя я уже отмечал ответ как принятый и предлагал щедрость в то время, с тех пор я нашел истинный ответ, который я искал, и понял, что мой вопрос не очень ясен - прежде всего потому, что я не знал что спросить.

Как взаимодействовать с @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 или с явным провалом).

Три заключительных замечания:

  1. Если вы хотите реализовать свой собственный объект autorelease бассейна, вы будете нуждаться в некоторой форме локальной памяти потока для ее хранения.
  2. Этот последний вопрос о потоковом подключении - вот почему каждая программа Objective-C должна начинаться с блока @autoreleasepool { } в main().
  3. Вот почему Apple просят вас использовать класс NSThread и GCD, а не резьбу DIY - они заботятся обо всем этом для вас!