Objective c управление памятью
На самом деле, для многих программистов под Си/++ достаточно трудным этапом является восприятие по работе и организации объектов в памяти на языке от Apple. Хотя есть множество различных статей в интернете по поводу управления памятью в программах на Obj-C, при написании этой статьи я решил сделать упор на примеры, где объяснялись бы такие важные отличия методов работы с памятью, таких как: alloc, copy, assign, init, retain, release, autorelease от таких привычных нам функций выделения и уничтожения объектов/памяти как malloc(), free() и их производные в Си и new/delete в C++.
Для начала определю некоторые ключевые моменты:
1. Программа-пример на Objective-C выполнялись в ОС MacOs 10.7 в среде XCode 4.2;
2. Программа-пример на C/C++ выполнялась в ОС Windows XP в среде C++ Builder 2009.
Средства управления памятью в XCode:
В данной среде разработки под MAC в последней (XCode 4.2) версии поддерживаются, грубо говоря, 3 вида настроек компиляции для управления памятью в программе.
Классическое управление, связанное с ручным выделением памяти, ручным или полуавтоматическим удалением объектов из памяти;
«Модернизированное» классическое управление. По сути это тоже самое, что и Классическое управление, но к нему добавлен такой механизм, как автоматический подсчет ссылок (Automatic references count - ARC). Этот метод доступен только начиная с XCode 4.2 и только при использовании компилятора LLVM;
Garbage Collector (GB) - сборщик мусора.
А теперь о всех по порядку.
Классическое управление
Этот способ давнишнего управления памятью используется и по сей день, наиболее часто. Суть управления заключается как и в C/C++, выделил память - освободи. Вот только средства выделения немного разные. Разберемся в чем суть.
Поскольку Obj-C является объектно-ориентированным языком, то классы и объекты - это то, на чем держиться модель памяти в данном языке. Вы вполне можете выделять память и освобождать обычными классическими Си-функциями, такими как malloc(. ) и free(), но к объекто-ориентированному подходу это не имеет никакого отношения.
Теперь более подробно, как выделяется память в Objective-C.
Выделение памяти
Для выделения памяти объектам есть такие методы, как alloc, new, init.
Давайте сравним их с сишными вариантами:
alloc - выделяет память для объектов аналогично оператору new в C++;
init - по сути это метод инициализации, он сравним с конструктором в C++, но запускается вручную. Его можно переопределить, так же как и конструктор в C++;
new - посути это тоже самое, что alloc и init в одном флаконе.
А теперь, как выделить память для какого либо объекта класса? Возьмем к примеру строковой класс NSMutableString .
Способ 1 - NSMutableString *str = [[ NSMutableString alloc] init];
Способ 2 - NSMutableString *str = [ NSMutableString new];
Проведем аналогию, по сравнению с выделением памяти (созданием объекта) в C++. Используем стандартную библиотеку классов;
string *str = new string(); - по сути похоже на второй способ в Objective-C.
Освобождение памяти
Конечно же память надо освободить после использования. Какие есть методы для освобождения памяти? Продолжим пример, написанный выше.
Способ 1 - [str release]; - моментальное освобождение памяти. Если указатель хотите использовать далее, то рекомендуется после этого выполнить следующую конструкцию:
Способ 2 - [str autorelease]; - а вот этот способ требует более детального рассмотрения. Для каждого потока формируется определенный пул, куда записываются методы, память которых вы не хотите освобождать моментально. То есть после выполнения этого оператора, объект какое то время будет доступен.
А теперь как узнать, какое это время? Тут есть два варианта:
1) пока не выполниться команда [pool drain] ( в версиях XCode < 4), или пока не выйдете из блока @autorelease pool< . >;
К сожалению ко второму случаю, аналогию в чистом языке C++ со стандартной библиотекой я привести не могу, так как такого пула нет, но скорее всего есть какие-либо сторонние библиотеки, которые реализуют подобный механизм.
А вот аналог первого варианта в C++ всем известен:
Так же при создании объекта, можно сделать его изначально как autorelease .
Например можно создать объект следующей строкой:
NSMutableStirng *str1 = [[[NSMutableStirng alloc] init] autorelease]; // Такой способ выделит память под новый объект, инициализирует его, а затем изначально сообщит в пул, что удаление объекта кладется на плечи autorelease пула, после чего передаст указатель на объект переменной str1 .
В итоге, объект, находящийся по адрессу, записанному в str1 автоматический уничтожится по условию освобождения пула.
Кстати, после такой записи, если вызвать [str1 release]; - то объект сразу же будет уничтожен, но при попытке освободить пул, получим исключение, и последующий вылет из программы с ошибкой удаления несуществующего объекта.
Так же есть такой стандартизированный способ создания авторелизнутых объектов классов как применение Convenience конструкторов.
Еще хотелось бы порекомендовать, по возможности всегда использовать release вместо autorelease при программировании под IOs . Конечно, проще было бы создать объект изначально авторелизнутым и забыть про удаление объекта, эдакий почти сборщик мусора). Но не забывайте, что если пул очищается очень редко, то при программировании для мобильных устройств, где количество памяти ограничено, можно столкнутся с проблемой нехватки памяти еще до то того, как пул будет очищен.
Cчетчик указателей на объект
Еще одно отличие классов C++ от классов в Objective-C в том, что каждый объект класса имеет счетчик ссылок указателей, которые ссылаются на данный объект. И если мы создаем объект, счетчик увеличивается на еденицу, если мы присваиваем объект еще какому то указателю, используя retain , то счетчик ссылок еще увеличивается на +1, но тут есть строгое правило. Объект не будет удален (при вызовах release или autorelease ) до тех пор, пока счетчик ссылок не равен 0. Такой механизм наследуется от супер-класса NSObject ( загляните в его заголовочный файл ).
В C++ такого механизма нет, если даже 10 указателей ссылаются на объект, то после вызова delete объект будет уничтожен, а попытки обратиться к нему, грозят вызовом исключения.
Но на самом деле не все так просто. Давайте разберем эту особенность по подробнее.
Существуют два способа работы с чужим объектом (по другому это можно назвать - легкая копия ( shallow copy ):
Взять объект во владение ( retain );
Получить просто адресс объекта ( assign ).
Что из себя это представляет? Первый способ отличается от второго только тем, что увеличивает счетчик ссылок указателей на объект. Второй способо ничего такого не делает и просто присваивает указателю адресс на объект.
NSMutableString * str1 = [[NSMutableString alloc]init]; // Создаем объект типа NSMutableString и присваиваем указатель на него переменной str1.
NSMutableString * str2 = [str1 retain]; // Присваиваем указатель на существующий объект переменной str2 и увеличиваем счетчик указателей в объекте на +1;
NSMutableString * str3 = [str1 assign]; // Присваиваем указатель на существующий объект переменной str 3.
В итоге получается, чтобы объект был уничтожен, нужно вызвать следующие методы:
// Счетчик объекта = 2;
[str1 release]; // Отнимаем еденицу от счетчика = -1;
[str2 release]; // Отнимаем еще еденицу от счетчика = -1;
// В итоге счетчик == 0, объект немедленно уничтожается.
[str 3 release];//Приведет к ошибке, так как счетчик при (assign) не увеличивался.
А теперь приведем аналогию относительно C++:
NSMutableString * str1 = [[NSMutableString alloc]init] - аналогично string *str1 = new string();
NSMutableString * str2 = [str1 retain]; - в C++ такое возможно только при использовании стороннего механизма;
NSMutableString *str3 = [str1 assign]; - аналогично string *str3 = str1; Кстати в Objective-C возможно присвоить указатель и таким образом NSMutableString *str3 = str1; так как этот язык всетаки является расширением обычного Си, и все средства Си вполне доступны.
На счет метода assign сделаю небольшое поясленние. Далеко не все классы реализуют этот метод, так как по сути это обычное присвоение указателя другой переменной, поэтому очень часто проще сделать NSMutableString *str3 = str1; .
Копирование объектов
Метод копирования объектов или по другому - полная копия (deep copy), во многом похож, на копирование объектов в C++ , но существуют некоторые особенности.
Метод копирования, как и другие методы управления памятью, описан в супер-классе NSObject как протокол NSCopying , но реализацию этого протокола каждый класс, наследуемый от NSObject должен выполнять самостоятельно. Большинство стандартных классов в библиотеках от Apple поддерживают методы копирования объектов внутри себя. В случае же, если вы хотите дать возможность копирования объектов своего класса (пользовательского), то необходимо самостоятельно реализовать метод копирования по протоколу NSCopying . Реализация очень похожа на C++, так как для копирования пользовательских объектов класса так же реализацию необходимо прописывать самому.
Метод копирования синтаксический записывается как copy .
Пример копирования объекта строки в Objective-C:
NSMutableString *str1 = [[NSMutableString alloc]init]; // Создаем объект от класса NSMutableString . Присваиваем указатель на объект переменной str1 .
NSMutableString *str2 = [str1 copy]; // В результате этой строчки, в объекте str1 запуститься уже реализованный метод copy ( он реализован в классе NSMutableString ), который создаст совершенно новый объект типа NSMutableString в памяти, затем перепишет содержимое объекта из str1 в новый объект, а затем уже передаст указатель на объект переменной str2 . В итоге str1 и str2 будут совершенно двумя разными объектами в памяти, но имеющими одни данные, то есть, str2 будет в прямом смысле слова копией str1 .
Соответственно такие объекты необходимо будет в конце удалить.
[str1 release];
[str2 release];
А теперь хочу обратить Ваше внимание на еще одну отличительную вещь реализации копирования в стандартных фреймворках от Apple. Существуют такие понятия как mutable и immutable объекты. Из названий наверняка понятно, что одни объекты являются изменяемыые, другие нет. Только имеется ввиду изменяемость не самих объектов, а изменяемость данных, которые содержаться в этих объектах.
Например, возьмем два стандартных строковых класса - NSMutableString, я его часто использовал в примерах и NSString . Из названия понятно, что NSMutableString - позволяет изменять данные внутри (имеет методы для этого), а NSString является статичным по отношению к данным. А теперь, к чему я это?
Очень важным фактором является то, что обычно, если идет копирование не изменяемых объектов, то в реализации копирования идет следующая строчка:
return [self retain];
Это говорит о том, что полное копирование замещают легким копированием, с захватом владения объектом. Наверное в этом есть смысл. Поэтому операция copy от retain в данном случае не отличается, для неизменяемых объектов. Лучше всего конечно смотреть документацию, чтобы убедиться в способе копирования, реализуемым классом.
Ниже в примере, я покажу на практике эти отличия.
Модернизированное классическое управление
Эта новая фишка появилась в среде программирования XCode относительно недавно, начиная с версии XCode 4.2. В большинстве случаев она включается автоматический при создании нового проекта.
Изначально разрабатывалась для того, чтобы новички программирования под Objective-C забыли про то, что память нужно удалять ( вспомним ту же Java).
Главная особенность использования ARC состоит в том, что мы по прежнему создаем объекты сами в ручную, вызывая alloc - init или new , но мы теперь не можем использовать такие методы как release , autorelease или переопределять -(void)dealloc . ARC это будет делать за нас. Механизм конечно не такой как в сборщике мусора, но результаты похожие, хотя даже не знаю, стоит ли их сравнивать. В общем, основываясь на том, сколько вы создали объектов, сколько раз скопировали их, сколько раз вызывали retain , счетчик все это считает, и в коде за нас сам прописывает где нужно освобождение памяти, когда объектами уже никто не пользуется.
По идеи разработчиков, такой метод должен облегчить программирование, и уменьшить ошибки, связанные с утечкой памяти, но по факту, по скольку контроль за уничтожение объектов отдается полностью на откуп компилятору, мне трудно представить, как он понимает, где нужно подставить строчки по осовобождению памяти, так как подставляются они условно.
А что же делать, если код ранее был написан в предыдущих версиях среды? Он же теперь не будет компилироваться? Но для этого, разработчики предусмотрели специальную опцию меню, которая облегчит переход с не- ARC, на ARC -ориентированную программу.
Насколько я понимаю, так же ARC можно применять, чуть ли не пофайлово, что дает плавный переход каждого файла исходных текстов программы с промежуточным тестированием. Так же такой подход позволяет использовать ARC в своем проекте, и использовать не- ARC и сходники каких либо сторонних библиотек, например API - Cocos2D.
Относительно же C/C++, в стандартных библиотеках и средах таких механизмов я не наблюдал, но возможно сторонние библиотеки позволяют использовать аналогичные механизмы. Поэтому аналогию тут провести не смогу.
Вообще, конечно, начиная с Mac OS X 10.5 управление памятью стало намного проще, поскольку добавилась поддержка сборки мусора (GC). Соответственно, можно её просто включить по умолчанию в вашем проекте и забыть об этом, как о страшном сне.
Тем не менее, если вы внимательно почитаете описание, то там будет написано следующее: «All Objective-C code linked or loaded by this application must also be GC capable». То есть, если вы используете сторонние фреймворки в закрытом виде, которые были скомпилированы без поддержки GC — придется либо от них отказаться, либо использовать более старые механизмы управления памятью, в виде retain/release. Также, поддержка GC отсутствует в iPhone, так что если вы решили написать своё приложение под iPhone OS, то в любом случае вам придется с этим столкнуться.
Я буду предполагать, что у вас все-таки какие-то начальные знания об Objective C есть, к примеру, вы уже читали этот топик.
В Objective C используется стандартный механизм подсчета ссылок: у каждого создаваемого объекта, являющегося наследником NSObject/NSProxy есть внутренний счётчик ссылок, который после создания устанавливается равным в единицу. Чтобы увеличить счётчик ссылок на объект, нужно вызвать retain, для уменьшения счётчика ссылок — release. После того, как счётчик ссылок достигнет 0, объект освобождается.
NSString *someString = [[NSString alloc] initWithFormat: @"usage: %s first-arg second-arg\n" , argv[0]]; // счетчик ссылок равен 1
// . сделать что-нибудь с этой строкой
[someString release]; // освобождаем память
NSString *someString = [NSString stringWithFormat: @"usage: %s first-arg second-arg\n" , argv[0]]; // счётчик ссылок всё равно равен 1, НО объект вручную освобождать НЕ НУЖНО, если только вы не сами не вызывали retain (в этом случае число вызовов release должно соответствовать количеству вызовов retain)
// . сделать что-нибудь с этой строкой
// всё! объект освободится сам после окончания обработки события или ручного опустошения пула
Если ваш код выполняется в отдельной нити, то нужно создать NSAutoreleasePool в этой нити вручную, иначе вся память от autorelease объектов будет утекать (о чём вы сможете легко догадаться из-за огромного количества записей в консоли).
В XCode есть отличные утилиты для отладки и анализа работы с памятью, например можно выбрать пункт Build → Build and Analyze, и тогда ваш код будет проанализирован статическим анализатором Clang, который может вам указать на типичные ошибки при управлении памятью (и не только). Также, есть возможность запустить приложение с целью обнаружить утечки, для этого запустите Run → Run with Performance Tool → Leaks в вашем проекте. Она отслеживает утечки памяти прямо в процессе работы приложения, причём она может находить утечки не только в коде Objective C, но и в обычном C (с malloc/free), и даже внутри закрытых фреймворков (в том числе и от Apple)!
Работа с Keychain
Функции для работы с Keychain достаточно низкоуровневые (в отличие от большинства фреймворков, которые работают с пользовательским интерфейсом), и используют API на языке C. В документации от Apple есть очень объемное руководство по всем вызовам, которые поддерживаются подсистемой Keychain Services, но я бы хотел показать, насколько просто можно делать базовые вещи.
При работе с вызовами на языке C, Apple в основном использует CoreFoundation. CoreFoundation использует и поддерживает практически те же самые типы данных, которые используются в Objective C с фреймворком Cocoa, и даже поддерживает прозрачное приведение типов CoreFoundation <-> Cocoa. Все вызовы CoreFoundation имеют префикс CF (ср. с NS), а имена типов получаются с помощью замены NS на CF и звездочки [*] на суффикс Ref (reference, ссылка) в конец (к примеру, NSString* <-> CFStringRef, NSArray* <-> CFArrayRef). Для работы с памятью используются CFRelease(CFTypeRef) / CFRetain(CFTypeRef), о назначении и способе использования которых можете догадаться сами.
Итак, чтобы начать работу с Keychain, нужно добавить пару заголовков и подключить фреймворк Security.framework:
Чтобы добавить интернет-пароль для вашего приложения в связку ключей по умолчанию, используйте вызов SecKeychainAddInternetPassword. У него очень много аргументов, большАя из которых не является обязательной. Пример использования находится ниже:
const char *serverName = "habrahabr.ru" ;
int serverNameLength = strlen(serverName);
const char *accountName = "youROCK" ;
int accountNameLength = strlen(accountName);
char *path = "/" ;
int pathLength = strlen(path);
const char *passwordData = "myExtremelySecretPassword" ;
int passwordLength = strlen(passwordData);
/* конечно, протокол может быть другим (не SSH :)), см. описании этой функции в руководстве, в нем можно найти ссылку на список всех поддерживаемых протоколов */
SecKeychainAddInternetPassword(NULL, serverNameLength, serverName, 0, NULL, accountNameLength, accountName, pathLength, path, port, kSecProtocolTypeSSH, kSecAuthenticationTypeDefault, passwordLength, passwordData, NULL);
* This source code was highlighted with Source Code Highlighter .
Для получения интернет-пароля из Keychain, мы используем вызов SecKeychainFindInternetPassword, пример работы с которым можно увидеть ниже:
const char *serverName = "habrahabr.ru" ;
int serverNameLength = strlen(serverName);
const char *accountName = "youROCK" ;
int accountNameLength = strlen(accountName);
char *path = "/" ;
int pathLength = strlen(path);
UInt32 passwordLength;
void *passwordData;
if ( (retVal = SecKeychainFindInternetPassword(NULL, serverNameLength, serverName, 0, NULL, accountNameLength, accountName, pathLength, path, port, kSecProtocolTypeSSH, kSecAuthenticationTypeDefault, &passwordLength, &passwordData, NULL)) == 0)
// дело в том, что passwordData является (void *) и НЕ СОДЕРЖИТ нулевого символа в конце,
// поэтому мы используем следующий код, чтобы получить пригодную для использования строку с паролем
NSString *passValue = [[NSString alloc] initWithBytes:passwordData length:passwordLength encoding:NSUTF8StringEncoding];
// делаем, что хотим с полученным паролем
SecKeychainItemFreeContent(NULL, passwordData);
[passValue release];
> else
// обратите внимание на 2 вещи — во-первых, в названии функции есть copy, а значит нам нужно освободить память самим
// вторая вещь — мы используем обычное приведение типов (NSString*), чтобы получить указатель на NSString из CFStringRef
CFStringRef reason = SecCopyErrorMessageString(retVal, NULL);
NSLog( @"Could not fetch info from KeyChain, recieved code %d with following explanation: %@" , retVal, (NSString*) reason);
CFRelease(reason);
>
* This source code was highlighted with Source Code Highlighter .
Заметьте, что мы нигде не предполагали, на какой ОС этот код будет запущен, так что он будет работать и в Mac OS X и в iPhoneOS (причём в iPhoneOS для доступа к Keychain из приложения не требуется подтверждение пользователя).
GUI-Утилита для монтирования томов через SSH с использованием SSHFS
Я уже неоднократно писал на Хабре про свою утилиту, которая позволяет подключать удаленные ФС с помощью SSHFS и GUI, но хочу сообщить, что она наконец-то доросла до состояния, когда ей вполне можно пользоваться:
Управление памятью приложения, во время выполнения программы - процесс выделения памяти под объекты, и освобождение ее после использования. Хорошо написанная программа использует мало памяти, на сколько это возможно. В Objective-C, это также может рассматриваться как способ распределения ограниченного объема памяти между различными частями кода и данных. Данное руководство показывает как можно управлять памятью Вашего приложения, управляя жизненным циклом Ваших объектов, освобождая их, когда они больше не нужны.
С Первого Взгляда
Objective-C предоставляет три метода управления памятью:
- В методике, описанной в данном руководстве, называемой "manual retain-release" (ручное сохранение-освобождение) или MRR, вы явно управляете памятью, отслеживая объекты, которые у вас есть. Это реализуется с помощью модели, известной как подсчет ссылок, что Foundation класса NSObject обеспечивает совместно со средой выполнения.
- В Automatic Reference Counting (автоматическом подсчете ссылок), или ARC, система использует тот же подсчет ссылок, что и система MRR, но он вставляет соответствующий вызов метода управления памятью за вас во время компиляции. Вам настоятельно рекомендуется использовать ARC для новых проектов. Если вы используете ARC, обычно нет необходимости, понимать внутреннюю реализацию описанную в этом документе, хотя она может быть полезной в некоторых ситуациях.
- В garbage collection (сборе мусора), система автоматически отслеживает, какие объекты владеют другими объектами. Затем она автоматически освобождает (или собирает мусор) объекты, на которые больше не ссылаются. Метод использует другой механизм, нежели, чем у используемых в MRR и ARC, и поддерживается только в среде выполнения на Mac OS X, а не IOS.
Если вы планируете писать код для IOS, вы должны использовать явное управление памятью (предмет данного руководства). Кроме того, если вы планируете создавать библиотеки подпрограмм, плагины, или общий код, который может быть загружен в любом процессе со сборкой мусора или без сбора мусора, вы можете написать свой код с помощью методов управления памятью, описанных в этом руководстве. (Убедитесь, что вы затем проверите код в Xcode, со сборкой мусора выключенной и включенной.)
Практика Предотвращения Проблем при Работе с Памятью
Существуют две основные проблемы, возникающие в результате неправильного управления памятью:
-
Освобождение или перезапись данных, которые используются до сих пор
Это приводит к повреждению памяти, и как правило, приводит к сбою в своем приложении, или еще хуже, повреждает данные пользователя.
Утечка памяти это выделенная и не освобожденная память, которая больше не используется, что в свою очередь приводит к все большему выделению памяти и как результат - снижение производительности системы, а в случае с IOS и вылет приложения
Думаю способ управления памятью с точки зрения подсчета ссылок, может показаться, зачастую контрпродуктивным, потому что вы склонны рассматривать управление памятью с точки зрения деталей реализации, а не с точки зрения ваших фактических целей. Вместо этого, вы должны думать об управлении памятью с точки зрения владения объектом и объектом графов.
Объект граф (Object graph)
В объектно-ориентированных программах, группы объектов образуют сеть через их отношения друг с другом, либо через прямую ссылку на другой объект, либо через цепочку промежуточных ссылок. Эти группы объектов, называются графами объектов. Объекты графы могут быть маленькими или большими, простыми или сложными. Объект массив , который содержит один объект строку представляет маленький, простой граф объекта. Группа объектов, содержащих объект приложения, со ссылками на окна, меню, виды, и другие вспомогательные объекты, может представлять большой, сложный объект графов.
Cocoa использует прямую конвертацию имен, чтобы показать, когда метод возвращает объект.
Хотя основная политика проста, есть некоторые практические шаги которые можно предпринять, чтобы сделать управление памятью легче, для содействия обеспечения надежности вашей программы и в то же время сводя к минимуму потребности в ресурсах.
Используйте Инструменты Анализа для Отладки Проблем с Памятью
Для выявления проблем с вашим кодом во время компиляции, вы можете использовать Clang Static Analyzer (Статический анализатора кода), который встроен в Xcode.
Работа статического анализатора кода
Xcode статический анализатор разбирает исходный код проекта и определяет типы проблем:
- Логические ошибки, такие как доступ к неинициализированным переменным и разыменование нулевых указателей
- Недостатки управления памятью , такие как утечка выделенной памяти
- Неиспользуемые переменные
- Недостатки, используемого API, не следующего политике используемых Frameworks и библиотек
Заметим, что если статический анализатор сообщает об отсутствии проблем, это не означает, что их нет.Инструмент не может обнаружить все недостатки в исходном коде.
Если проблемы управления памятью все-таки возникают, есть другие средства и методы, которые можно использовать для выявления и диагностики проблем.
Вы можете использовать инструменты для отслеживания подсчета ссылок событий и искать утечки памяти. См. "Просмотр и анализ данных трассировки".
Структура класса и процесс загрузки
Objective-C - это объектно-ориентированный язык программирования, который расширяет C. Одним из различий между языками Objective-C и C является введение «объектно-ориентированного» мышления, которое может гибко использовать классы и объекты для программирования. Поэтому понимание структуры и характера классов очень важно для изучения Objective-C.
- Классовая структура
Класс в Objective-C сам по себе Class Тип объекта, сокращенноОбъект класса, и Class На самом деле точка objc_class Указатель на структуру.
Просмотрев objc/runtime.h Файл, узнал objc_class Структура композиции, которая включает в себя много информации для класса.
- Получить объект класса в памяти
- полезным class метод
[Прототип функции] + (Class)class
[Описание метода] Метод класса, вызовите этот метод через определенный класс, чтобы вернуть Class Тип объекта класса.
- Использование объектов экземпляра класса class метод
[Прототип функции] - (Class)class
[Описание метода] Метод объекта, вызывающий метод через объект экземпляра определенного класса, возвращает Class Тип объекта класса.
годный к употреблению objectP Создание объекта класса Person Экземпляр объекта класса и подтверждается печатью classP да Person Виды.
Распечатать classP 、 classP 1 Адрес памяти находит то же значение, указывая, что в памяти есть только одна копия класса.
- Загрузка классов и инициализация
- Класс загрузки
[Прототип функции] + (void)load
[Описание функции] Когда программа запущена, она вызывается, когда классы и категории (независимо от того, используются они или нет) в опции «Скомпилировать источники» XCode, сначала вызывают родительский класс, а затем вызывают подкласс Вызывается один раз в класс. - Инициализация класса
[Прототип функции] + (void)initialize
[Описание функции] Вызывается, когда этот класс используется впервые. Приоритетный звонок в этой категории initialize Метод, когда подкласс сначала вызывает родительский класс initialize Метод для инициализации родительского класса.
【различия】: load Вызываются и классы методов, и классификации, потому что они загружаются отдельно, а порядок загрузки классификации связан с порядком компиляции; initialize Метод вызывается, когда класс используется впервые, и вызывающая последовательность должна вызвать класс initialize , А затем назвать родительскую категорию initialize ; Если нет такого рода initialize , А затем вызвать родительский класс initialize 。
Создание объекта
Чтобы создать объект в Objective-C, сначала вызовите класс alloc Метод возвращает объект, а затем вызывает объект init или initWithSomething Метод возвращает сам себя или напрямую вызывает класс new метод. Например, следующий код:
Поочередно представьте следующие три метода:
- + (instancetype) alloc
Когда этот метод вызывается, система сначала выделяет подходящий объем памяти в области кучи для хранения объекта (см.C языком - основы управления памятью-C язык манипулирует функциональной частью памяти кучи) и возвращает неинициализированный объект этого типа, а также выполняет следующие три вещи: (цитируется изОб двухэтапной модели структуры ObjC)
1. Установите для счетчика сохранения нового объекта значение 1.
2. Укажите переменную-член isa нового объекта для его объекта класса.
3. Установите значение всех других переменных-членов нового объекта на ноль. (В зависимости от типа переменной-члена ноль может означать ноль или ноль или 0.0)
- (instancetype) init
Значение переменной-члена объекта фактически инициализируется в соответствии с типом конкретной переменной-члена объекта.
+ (instancetype) new
можно просто понимать как alloc 、 init Объединить в одну операцию.
- Хранение объектов
Создано выше p1 、 p2 Код можно интерпретировать как:
- больше деталей
Для получения более подробной информации об объектах ObjC, пожалуйста, обратитесь к следующей статье:
Тан Цяо:Об двухэтапной модели структуры ObjC
Мороз:Объекты в этой жизни
Matt Gallagher:What is a meta-class in Objective-C?
Некоторые из приведенных выше описаний взяты из первого и уже объяснены.
Владение объектом и подсчет ссылок
Стратегия владенияЛюбой объект, созданный самим собой, принадлежит самому себе и имеет одного или нескольких владельцев. Пока объект имеет хотя бы одного владельца, объект не будет уничтожен, а занимаемое им пространство памяти всегда будет существовать и не будет освобождено (если только Вся программа вышла). Аналогично бутылке минеральной воды может потребляться один или несколько человек, если одному человеку необходимо пить минеральную воду, она не будет утилизироваться санитарными работниками.
через протокол NSObject retainCount Свойство может получить текущее значение счетчика ссылок объекта.
Счетчик ссылок: Какао использует механизм подсчета ссылок, чтобы привязать целое число типа NSUInteger к каждому объекту, чтобы указать, сколько раз объект ссылается в данный момент (то есть, сколько владельцев в настоящее время ссылаются на объект), который называется ссылкой объекта счетчик. Каждый объект ObjC имеет свой собственный счетчик ссылок (занимает 8 байтов в 64-битной среде компилятора). Аналогично целому числу, которое записывает, сколько людей в настоящее время выпивают бутылку минеральной воды, когда минеральная вода не пить, она будет переработана работниками санитарии.
Роль счетчика ссылок: Когда объект только что создан, его счетчик ссылок по умолчанию равен 1. Когда значение счетчика ссылок объекта становится равным 0 (то есть ни один владелец не ссылается на объект), система уничтожает объект, освобождает и повторно использует объект и размещает его в области кучи. Пространство для хранения. Система определяет, нужно ли ей уничтожать объект и освобождать занимаемое им пространство памяти, определяя, равно ли значение счетчика ссылок объекта 0.
Одно исключение: если значение объекта nil Когда счетчик ссылок равен 0, система не освобождает пространство, потому что система не выделила пространство для объекта.
- Операции, связанные с подсчетом ссылок
- - (instancetype)retain : Сделать значение счетчика ссылок объекта +1
- - (oneway void)release : Сделать значение счетчика ссылок объекта -1 (не означает уничтожение объекта)
- - (NSUInteger)retainCount : Получить текущее значение счетчика ссылок объекта
- - (instancetype)autorelease : Для очистки позже в "пуле авто-релизов" @autoreleasepool ), затем уменьшите счетчик ссылок на объект
Стоит отметить, что: retain Операция не может сделать значение счетчика ссылок объекта, который был освобожден +1, что также хорошо понятно, retain Невозможно вернуть мертвого человека к жизни, так как санитарный работник переработал вашу бутылку с минеральной водой, и вы больше не сможете держать бутылку с водой.
В платформе подсчета ссылок OC автоматическое освобождение пула является важной функцией. Вызов release немедленно уменьшит счетчик ссылок объекта (и весьма вероятно, что система восстановит объект). Однако иногда нет необходимости вызывать его, а вместо этого вызывать autorelease. Этот метод Счетчик будет уменьшен позже, обычно в следующем «четном цикле» (четном цикле), но он может быть выполнен раньше. Эта функция очень полезна, особенно при возврате объектов в методы.
В книге также приведены такие примеры:
Вернулся в это время str Значение счетчика объектов +1 к ожидаемому значению, потому что метод вызывается один раз внутри alloc При работе без соответствующей операции сброса счетчик +1 означает, что вызывающий абонент отвечает за обработку дополнительной операции резервирования. Но не в - (NSString *)stringValue Метод освобождается изнутри, в противном случае система вернет объект до возврата метода. На этот раз его следует использовать autorelease Он может продлить жизненный цикл объекта и убедиться, что объект должен быть действительным после того, как метод вернется и будет выпущен в нужное время.
[Примечание]: об объекте retainCount Это разумно в большинстве случаев, но иногда его значение непостижимо. Например, официальные документы Apple объясняют это следующим образом:
Общее содержание документа: этот метод не очень полезен при отладке проблем управления памятью. Поскольку любой объект каркаса может ссылаться на объект, а пул автоматического выпуска имеет эффект отложенного выпуска для объекта, невозможно получить полезную информацию путем вызова этого метода.
Уничтожение объекта
- Когда объект уничтожен: когда значение счетчика ссылок объекта равно 0, оно будет уничтожено системой, и занимаемое им пространство памяти будет освобождено.
- - (void)dealloc : Когда объект уничтожается, система вызывает этот метод, переписывает метод и высвобождает в нем соответствующие ресурсы, такие как уведомление об удалении (аналогично последним словам), после перезаписи. dealloc Метод должен вызывать метод [super dealloc] в конце блока кода. Метод [super dealloc] вызывается для super Освободите соответствующие ресурсы, чтобы освободить любые унаследованные объекты, кроме того, мы не должны напрямую вызывать объект delloc метод.
Например, следующий код:
Консоль выводит информацию
Область управления памятью
С учетом вышеизложенного, предвещающего знание объектов Objc, мы можем хорошо представить концепцию управления памятью.
ассортимент:Управляйте памятью любого объекта, унаследованного от типа NSObject. Это недопустимо для других основных типов данных, таких как int, char, double, структура, переменные типа перечисления.
Основная причина: объекты ObjC и другие базовые типы переменных имеют разные места хранения в памяти.Замять памяти, занимаемая объектами, динамически выделяется системой в области кучи и должна быть вручную освобождена программистом, в то время как другие базовые типы данных (локальные переменные) обычно выделяются Область стека автоматически освобождается системой.
Метод управления памятью
-
Objective-C предоставляет нам два (ранее три) способа управления памятью:
- Сбор мусора (механизм сбора мусора)
После Objective-C 2.0 существует механизм сборки мусора. Механизм сборки мусора контролирует весь граф отношений объектов, ищет объекты, которые больше не имеют указателей в области видимости, и автоматически освобождает эти объекты.
Кроме того, в книге «Эффективные Objective-C 2.0 Написание высококачественных способов iOS и OS X Code 52 Эффективных способов» механизм сбора мусора описывается следующим образом: - Пока объект все еще используется, занимаемое им пространство памяти не должно быть освобождено, необходимо использовать объект, сделать счетчик ссылок на объект +1; использовать объект, сделать счетчик ссылок на объект -1.
- Кто создал, кто выпустил
- Кто сохранит, кто освободит
- Дикий указатель
- Определенная переменная-указатель не инициализирована.
- Указатель на область памяти кучи, которая была освобождена.
Mannul Reference Counting (MRC)
Ручное управление памятью относится к retain 、 release 、 autorelease Программист управляет памятью вручную, используя операцию счетчика ссылок. Все коды в этой статье, которые включают три вышеуказанных метода, являются практикой MRC. Самая большая проблема с MRC - это время удержания и отпускания предметов, неподходящих retain с участием release Это может вызвать проблемы с памятью, такие как «дикий указатель» и «утечка памяти» (подробности описаны ниже).
Automatic Reference Counting (ARC)
Автоматическое управление памятью, ARC, как новая функция компилятора LLVM 3.0 после WWDC2011 и iOS5, значительно освобождает руки разработчиков iOS (рекомендуется Apple). Разработчикам больше не нужно писать что-либо, содержащее retain 、 release 、 autorelease Компилятор автоматически вставит вышеуказанный код в соответствующее место для управления памятью. Фактически, большая часть кода теперь использует ARC для управления памятью, но, как новичок, это может быть «ежедневное использование и невежество», без осознания глубинных отношений, и написание кода с операциями подсчета ссылок в среде ARC Невозможно скомпилировать. Например:
Для получения дополнительной информации о механизмах MAC и ARC, пожалуйста, обратитесь к следующему блогу:
Вэй Ван (@onevcat):Научите ARC-iOS / Mac разработки ARC ввода и использования
HIT-Alibaba:Распределение памяти в Objective-C
Apple:Transitioning to ARC Release Notes
Начиная с Mac OS X 10.8, «сборщик мусора» формально устарел и больше не должен использоваться при написании программ для Mac OS X в коде Objective-C, а iOS никогда не поддерживала сборку мусора.
Поскольку я собрал относительно мало информации о сборке мусора в Objective-C, я видел только соответствующие введения в книге «Objective-C для обучения», и этот механизм никогда не использовался в разработке для iOS, поэтому я думаю, что здесь пока В Objective-C было три метода управления памятью, но я не могу рассказать о них подробнее. Заинтересованные студенты могут проверить книгу.
Принципы управления памятью
Конкретно при использовании alloc 、 new 、 copy (Создать копию принятого объекта) Когда объект создан, его значение счетчика ссылок устанавливается в 1, и это нужно сделать один раз, когда объект не нужен release Операция, когда используется retain Когда операция удерживает объект, его значение счетчика ссылок равно +1, и это нужно сделать один раз, когда объект не нужно использовать release Операция, одно творение соответствует одному release ,один раз retain Соответствует один раз release , Так что объект может иметь начало и конец.
Проблемы, вызванные неправильным управлением памятью
Приведенный выше код находится в yellowDog Когда значение счетчика ссылок равно 0, память объекта Dog, выделенного в области кучи, была освобождена, а затем вызвать NSLog по yellowDog Доступ указателя к объектам в области кучи может вызвать проблемы.
В книге «Эффективный Objective-C 2.0 52 эффективных способа написания высококачественного кода для iOS и OS X» слово «вероятно» объясняется следующим образом:
Причина, по которой я говорю «вероятно», не говорит наверняка, состоит в том, что память, занятая объектом в области кучи, помещается обратно в «пул доступной памяти» только после «освобождения», если память объекта не была перезаписана при выполнении NSLog в это время , Тогда объект все еще действителен, и программа не будет аварийно завершена. Видно, что ошибки, вызванные слишком ранним выпуском объектов, трудно отлаживать.
Когда мы включаем функцию обнаружения объектов зомби в Xcode, происходит сбой программы, и на консоль выводится следующая информация:
Как и в коде выше, когда мы создаем Dog Когда объект находится в области стека, система husky Переменная память, в области кучи для husky Указал на Dog Объект выделяет память за счет переменной husky Область видимости - это локальная переменная в фигурных скобках, когда выполняется блок кода, стек husky Переменная освобождается, но в данный момент нет прав в блоке кода husky Пространство для хранения объектов области кучи, на которую указывает переменная, освобождается, поэтому мы говоримСоответствующее место для хранения в области кучи протекает.
Сводка такова: основные типы данных обычно хранятся в области стека из-за занимаемой фиксированной памяти. Поскольку локальные переменные в основном хранятся в стеке, пространство памяти, занимаемое локальными переменными, автоматически восстанавливается, когда заканчивается блок кода или функция, и также восстанавливается указатель на объект. Этот процесс не требует управления программистом. Однако после создания объекта он сохраняется в области кучи. Поскольку указатель на объект был повторно использован в это время, но объект все еще находится в памяти, это приведет к утечке памяти (запрошенное пространство больше не используется, но не освобождается своевременно и разумным образом. Капля).
Конец статьи
Выше приведено все содержание базового понимания автора об управлении памятью Objective-C. Часть описания цитируется из книг и других статей блога. Однако все концепции, которые я считаю важными, прикреплены к другим ссылкам блога для расширения соответствующих знаний.
Кроме того: Код MRC, участвующий в этой статье, требует следующих настроек в XCode для компиляции: В Настройках сборки найдите параметр «Автоматический подсчет ссылок Objective-C» и измените его значение на «НЕТ».
Читайте также: