Статьи

Разработка приложений для iPhone — Управление памятью

Управление памятью стало настолько простым и легким для разработчиков с появлением iOS5 Automated Reference Counting. Мы поймем основные понятия управления памятью и значение ARC в этом уроке.

До iOS5 управление памятью осуществлялось с помощью ручного подсчета ссылок.

  • Если вы создаете объект, то вы являетесь владельцем объекта, и вы несете ответственность за освобождение памяти объекта, когда вам больше не нужен этот объект.
  • Вы также можете получить право собственности на объект, отправив ему сообщение «retain». Сохраненные объекты также должны быть выпущены его владельцами.
  • Память объекта можно освободить, отправив объекту сообщение «release» или «autorelease».
  • Автоматическое освобождение сообщения может быть отправлено, если мы хотим восстановить память объекта в будущем. Когда объект автоматически освобожден, он добавляется в пул автоматического выпуска. Когда пул истощится, объекты в пуле будут освобождены.
  • Количество ссылок или владельцев объекта отслеживается с помощью счетчика ссылок.
  • Всякий раз, когда объект инициализируется или сохраняется, его счетчик ссылок увеличивается на 1, а когда он освобождается, он уменьшается на 1.
  • Когда счетчик ссылок объекта обнуляется, память объекта восстанавливается. Другими словами, это распределяется.

Образец 1:

Проверьте счетчик ссылок в примере ниже:

NSString *str1 = [[NSString alloc] init]; //Reference count is 1 [str1 retain]; // Reference count increments to 2 [str1 release]; Reference count decrements to 1 [str1 release]; Reference count becomes 0 and memory gets reclaimed 

Образец 2:

 -(NSString *)sample{ NSString *str = [[[NSString alloc] initWithString:@"Test"] autorelease]; return str; } 

В примере 2, если мы вызовем метод release для объекта str , объект будет отменен, прежде чем он будет возвращен. Это не будет служить цели функции. В таких случаях мы будем использовать методы автоматического выпуска или удобства.

Удобные методы заботятся об инициализации и освобождении объектов сами по себе. В приведенном ниже примере stringWithString является stringWithString методом.

 -(NSString *)sample{ NSString *str = [NSString stringWithString:@"Test"]; return str; } 

Когда у объекта нет владельцев, вызывается его метод dealloc. Поэтому мы должны реализовать метод dealloc в пользовательских классах, которые мы создаем, и очистить все его ресурсы хранения.

В приведенном ниже примере все переменные свойства высвобождаются в методе dealloc.

 @interface Customclass : NSObject @property (nonatomic,retain) NSString *str1; @property (nonatomic,retain) NSString *str2; @end @implementation Customclass @synthesize str1,str2; -(void) dealloc { [str1 release]; [str2 release]; [super dealloc]; } @end 

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

В противном случае могут возникнуть следующие угрозы

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

Благодаря iOS5, кошмар балансировки релиза с сохранением полностью решен с помощью автоматического подсчета ссылок.

Концепции те же, но компилятор выполняет утомительную задачу по балансировке релиза с запасами для разработчиков.

Мы не должны использовать методы retain, release или autorelease. Чтобы компилятор знал, когда выпускать или сохранять объект, мы должны использовать квалификаторы владения со свойствами в заголовочных файлах или ссылочных переменных.

Переменные пожизненные квалификаторы

__strong переменные-указатели помогают сохранять объект до тех пор, пока на него указывает указатель. Классификатор по умолчанию.

 NSArray * __strong arrSample; 

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

 NSString * __weak strSample = [[NSString alloc] initWithString:@”hello”]; NSLog(@”%@”,strSample); 

В приведенном выше примере вывод будет ноль. Это потому, что слабая переменная strSample не имеет других сильных ссылок на нее.

__unsafe_unretained похож на слабый квалификатор, за исключением того, что объект не имеет значение nil и остается в качестве висячего указателя. Этот классификатор доступен только для поддержки iOS4, где слабые классификаторы недоступны. Не используйте этот классификатор, так как он может привести к аварийным ситуациям из-за его висячих указателей.

__autoreleasing используется для аргументов, которые передаются как ссылки.

Свойство пожизненной квалификации

Все перечисленные выше квалификаторы, кроме autoreleasing могут использоваться для свойств в заголовочных файлах. Это поможет компилятору добавить соответствующие вызовы релиза в методе dealloc.

Некоторые другие классификаторы приведены ниже.

Nonatomic свойства не являются потокобезопасными. Atomic — это его противоположность.

Свойство Assign используется для присвоения значения переменной и, следовательно, оно используется для примитивных типов данных.

Свойство Copy используется, когда вы чувствуете, что объект изменчив. Это используется, когда вы не хотите, чтобы значение было изменено другими владельцами объекта.

Сильные и слабые временные классификаторы помогают избежать сильных контрольных или циклических контрольных циклов. В отношениях родитель-ребенок родитель должен иметь сильную ссылку на ребенка, а ребенок должен иметь слабую ссылку, чтобы избежать циклов сильной ссылки.

ParentClass.h

 #import "ChildClass.h" @interface ParentClass : NSObject<SampleDelegate> @property(nonatomic,strong) ChildClass *child; @end 

ParentClass.m

 @implementation ParentClass @synthesize child; -(void) sampleMethod { child = [[ChildClass alloc] init]; child.delegate = self; } 

ChildClass.h

 @protocol SampleDelegate<NSObject> @end @interface ChildClass : NSObject @property(nonatomic,weak) id<SampleDelegate> delegate; @end 

ChildClass.m

 @implementation ChildClass @synthesize delegate; @end 

Шаблон делегата является хорошим примером цикла сохранения. ParentClass имеет строгую ссылку на объект ChildClass . Делегатом ChildClass является сам ParentClass . Это приводит к циклической ссылке. Если делегат ChildClass является сильным, то ни родитель, ни ребенок не могут быть освобождены. Это в свою очередь приведет к утечке памяти. Поэтому квалификатор времени жизни делегата должен быть установлен как слабый.

Даже если ARC помогает удерживать и освобождать объекты для вас, могут быть обстоятельства, когда вы должны установить указатели на ноль. Если этого не сделать, вашему приложению может не хватить памяти. Если вы продолжаете использовать сильные ссылки на объекты, которые вы создаете, ARC не сможет их освободить. Поэтому всегда старайтесь понять сферу действия объекта и, если требуется, установите для него значение nil.

Утечки памяти могут быть обнаружены, а производительность может быть оптимизирована с помощью инструментов Instruments, поставляемых с XCode. Изучите инструменты и не забудьте проконтролировать производительность вашего приложения, прежде чем отправлять его в Appstore.

Вывод

Я надеюсь, что вы приобрели некоторое понимание того, как осуществляется управление памятью в iOS. Экспертиза в управлении памятью может быть достигнута только через опыт. Пусть ваше исследование начинается здесь.