Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Что необходимо знать об ARC

Anna | 2.07.2014 | нет комментариев
Автоматический подсчет ссылок (Automatic Reference Counting, ARC) для языка Objective-C был представлен компанией Apple еще в 2011 году для iOS 4.0 и выше, Mac OS X 10.6 и выше, с применением xCode 4.2 и выше. И, правда всё огромнее библиотек, фреймворков и планов сегодня применяют ARC, до сих пор среди программистов встречается либо неприятие этой спецтехнологии, либо eё неполное осознавание. Опытные программисты, привыкшие к retain/release, изредка считают, что они отменнее и результативней справяться с подсчетом ссылок, чем это за них сделает компилятор, а новички, начищающие сразу применять ARC, предполагают, что им не нужно вообще думать об управлении памятью, и всё сделается магическим образом само.

ARC — не сборщик мусора

В отличае от сборщика мусора, ARC не занимается механическим освобождением памяти от отработанных объектов и не запускает никаких фоновых процессов. Всё что он делает — это при сборке приложения анализирует и расставляет retain/release в компилируемый код за программиста.
Вы не можете напрямую вызвать retain, release, retainCount, autorelease.
Дозволено переопределить dealloc, но вызывать [super dealloc] не нужно, за вас это сделает ARC, а так же освободит при этом все property и instance variables, которые в этом нуждаются. Переопределять deallocбывает нужно для того, Дабы, скажем, отписаться от нотификаций, удалить объект из иных служб и администраторов, на которые он подписан, инвалидировать таймеры, а так же Дабы освободить не Objective-C объекты.

Код без ARC:
- (void)dealloc
{
	[_someIvar release];
	[_anotherIvar release];
	[[NSNotificationCenter defaultCenter] removeObserver:self];
        [super dealloc];
} 
Код с ARC:
- (void)dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:self];
} 

Мощные и слабые ссылки

Взамен признаков retain и assign для properties в ARC применяются признаки strong и weak (__strong и __weakдля локальных переменных и ivars). Это не вовсе их аналоги, но об этом позднее. Признак strong применяется по умолчанию, так что указывать его очевидно не непременно.

@property  AnyClass *strongReference; //(atomic, strong) по умолчанию
@property (nonatomic, weak) id delegate;

Properties, переменные которых ссылаются на объекты «вверх» по иерархии и делегаты обязаны быть слабыми, Дабы избежать циклических ссылок.

От того что локальные переменные по умолчанию мощные (__strong), допустимы такие конструкции в коде:

NSDate *originalDate = self.lastModificationDate; 
self.lastModificationDate = [NSDate date];
NSLog(@"Last modification date changed from %@ to %@", originalDate, self.lastModificationDate);

Объект, сохраненный в переменной originalDate будет жив до тех пор, пока ARC не обнаружит последнюю строку, где применяется эта переменная, а после этого здесь же ее освободит (подставит release).

Для создания слабой ссылки применяется признак __weak:

NSDate * __weak originalDate = self.lastModificationDate;
self.lastModificationDate = [NSDate date];

В данном примере originalDate может перестать существовать теснее на 2-й строчке, если на данный объект огромнее нет крепких ссылок.

Значимая и пригодная специфика ARC: сразу позже деаллокации слабые и крепкие ссылки обнуляются, то есть становятся равными nil.

А от того что в Objective-C nil может принимать всякие сообщения, задачи с EXC_BAD_ACCESS уходят в прошлое. Это и есть различие от признаков retain/assign. Сходственное происходит и при объявлении объектов: они неявно инициализируются nil’ом. Пригодным приёмом является кэширование слабых property в крепких локальных переменных, Дабы быть уверенным, что объект будет жив нужное время:

- (void)someMethod {
    NSObject *cachedObject = self.weakProperty;
    [cachedObject doSomething];
    [cachedObject doSomethingElse];
}

По этой же причине при проверке слабых property на nil, скажем, делегатов, необходимо их кэшировать:

id cachedObject = self.delegate;
if (cachedObject) {
	[self.delegate doSomeTask];
}
cachedObject = nil;

Присваивание кэшированному объекту nil убирает мощную ссылку, и если на него огромнее нет других крепких ссылок, объект будет деаллоцирован.

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

@property (unsafe_unretained) NSObject *someProperty;	
NSObject *__unsafe_unretained someReference;

Autorelease

Объекты, сделанные с поддержкой статических способов (скажем: [NSData data...], [NSArray array...] и т. д.) и литералов (@«string», @42, @[], @{} ) огромнее не авторелизные. Время жизни таких обьектов задается только мощными ссылками на них.
Существует признак __autoreleasing, тот, что, согласно документации, рекомендуется применять для двойных указателей (* id) в случае, если нужно передать итог в параметр.

NSError *__autoreleasing error;
BOOL ok = [database save:&error];
if (!ok) {
	//обрабатываем ошибку
}

Tогда способ save должен иметь следующую сигнатуру:

- (BOOL)save:(NSError * __autoreleasing *) error;

Это гарантирует сохранность сделанного внутри способа объекта. Если объявить переменную без этого признака, то компилятор сделает временную авторелизную переменную, которую передаст в способ:

NSError * error;
NSError *__autoreleasing tmp;
BOOL ok = [database save:&tmp];
error = tmp;

NSAutoreleasePool сейчас недостижим для прямого применения. Взамен него предлагается применять директиву@autoreleasepool {}. При входе в такой блок состояние пула сохраняется, при выходе восстанавливается, освобождая все объекты, сделанные внутри. Рекомендуется применять @autoreleasepool в циклах, если создается огромное число временных обьектов.

for (id object in hugeArray) {
	@autoreleasepool {
		//использование временных объектов
	}
} 

Блоки

Блоки по-прежднему нужно копировать.

//property
@property (nonatomic, copy) SomeBlockType someBlock;

//локальная переменная
someBlockType someBlock = ^{NSLog(@"hi");}; 
[someArray addObject:[someBlock copy]];

О циклических ссылках компилятор предупредит:

warning: capturing ‘self’ strongly in this block is likely to lead to a retain cycle
[-Warc-retain-cycles,4]

SomeBlockType someBlock = ^{
    	[self someMethod];
};

Когда блок будет скопирован, он также захватит self, если в нем применяются instance varisbles.

Дабы избежать таких случаев, необходимо сделать слабую ссылку на self:

__weak SomeObjectClass *weakSelf = self;
SomeBlockType someBlock = ^{
    SomeObjectClass *strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf someMethod];
    }
};

Нужно заботиться о всяких объектах в блоках, объявленных снаружи, применяя признак __weak.

Строим мосты

Как же быть с объектами CoreFondation? Для них ручной подсчет ссылок никто не отменял. Прямой каст сейчас не работает, для этого есть несколько особых ключевых слов.

id my_id;
CFStringRef my_cfref;
NSString   *a = (__bridge NSString*)my_cfref;     // пустой каст
CFStringRef b = (__bridge CFStringRef)my_id;      // пустой каст
NSString   *c = (__bridge_transfer NSString*)my_cfref; // -1 к числу ссылок CFRef
CFStringRef d = (__bridge_retained CFStringRef)my_id;  // вернет CFRef  1
  • __bridge не делает ничего с числом ссылок, легко приведет объект к надобному типу.
  • __bridge_transfer необходим для смены типа объекта с CF на Objective-C. ARC декрементирует счетчик ссылок CF, так что удостоверитесь, что он огромнее нуля.
  • __bridge_retained необходим для смены типа объекта с Objective-C на CF. Он вернет CF-объект c счетчиком ссылок 1. Не позабудьте освободить объект вызовом CFRelease().

Завершение

Используйте ARC. Это проще, неопаснее и сэкономит вам время и нервы.

Ссылки

Раздел документации Clang об ARC
Статья о быстродействии ARC

 

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB