Ответ 1
Этот вопрос был открыт достаточно долго, и теперь я чувствую себя достаточно уверенно, чтобы ответить на него.
Различные уровни MM:
Аппаратная память
В Swift с ARC мы не можем очистить фактический аппаратный баран. Мы можем сделать так, чтобы ОС могла это сделать для нас. Одна часть использует правильный код (optionals
и weak
), другая часть создает время для выполнения этой задачи ОС.
Представьте, что у нас есть функция, которая работает на всех потоках бесконечно. Он делает одно, загружает изображение, конвертирует в черно-белый и сохраняет. Все изображения max на пару mbs и функция не создают утечек памяти программного обеспечения. Поскольку изображения не имеют заданного размера и могут иметь различное сжатие, они не имеют одинакового отпечатка. Эта функция всегда приведет к краху вашего приложения.
Этот "аппаратный" утечек памяти вызван тем, что функция всегда принимает следующий доступный слот памяти.
ОС не вступает в "фактическую очистку памяти", потому что нет времени простоя. Установка задержки между каждым проходом полностью исправляет это.
Значения по умолчанию MM
Кастинг
Некоторые операции не влияют на память, другие делают:
let myInt : Int = 1
Float(myInt) // this creates a new instance
Вместо этого попробуйте выполнить кастинг:
(myInt as Float) // this will not create a new instance.
Типы ссылок и типы значений | Классы vs Структуры
Оба имеют свои преимущества и свои опасности.
Структуры являются интенсивными, поскольку они Типы значений. Это означает, что они копируют их значения при назначении другому экземпляру, эффективно удваивая использование памяти. Для этого нет никаких проблем. Это то, что делает Structs Structs.
Классы не имеют такого поведения, поскольку они Reference Types. Они не копируются при назначении. Вместо этого они создают другую ссылку для того же объекта. ARC или Автоматический подсчет ссылок - это то, что отслеживает эти ссылки. У каждого объекта есть счетчик ссылок. Каждый раз, когда вы его назначаете, он увеличивается на один. Каждый раз, когда вы устанавливаете ссылку на nil, закрывающая функция заканчивается или включающий объект Deinits, счетчик уменьшается.
Когда счетчик обращается к 0, объект деинициализируется.
Существует способ предотвратить деиннициализацию экземпляра и, таким образом, создать утечку. Это называется Сильным ссылочным циклом.
class MyClass {
var otherClass : MyOtherClass?
deinit {
print("deinit") // never gets called
}
}
class MyOtherClass {
var myclass : MyClass?
deinit {
print("deinit") // never gets called
}
}
var classA : MyClass? = MyClass()
// sorry about the force unwrapping, don't do it like this
classA!.otherClass = MyOtherClass()
classA!.otherClass!.myclass = classA // this looks silly but in some form this happens a lot
classA = nil
// neither the MyClass nor the MyOtherClass deinitialised and we no longer have a reference we can acces. Immortalitiy reached they have.
установите одну ссылку на weak
class MyOtherClass {
weak var myclass : MyClass?
deinit {
print("deinit") // gets called
}
}
INOUT
Функции фиксируют переданные им значения. Но также можно отметить эти значения как inout. Это позволяет вам изменить Struct, переданный функции, без копирования Struct. Это может сэкономить память, в зависимости от того, что вы передадите и что вы делаете в этой функции.
Это также хороший способ иметь несколько возвращаемых значений без использования кортежей.
var myInt : Int = 0
// return with inout
func inoutTest(inout number: Int) {
number += 5
}
inoutTest(&myInt)
print(myInt) // prints 5
// basic function with return creates a new instance which takes up it own memory space
func addTest(number:Int) -> Int {
return number + 5
}
Функциональное программирование
Состояние - это значение со временем
Функциональное программирование является неотъемлемой частью объектно-ориентированного программирования. Функциональное программирование использует неизменяемое состояние.
Подробнее об этом здесь
Объектно-ориентированное программирование использует объекты, которые изменяют/изменяют состояния. Вместо создания нового значения обновляются старые значения.
Функциональное программирование может использовать больше памяти.
Optionals
Опционы позволяют устанавливать значение в нуль. Это уменьшит количество ссылок классов или деинициализированных структур. Установка нулей - это самый простой способ очистки памяти. Это идет рука об руку с ARC. После того, как вы установили все ссылки класса в ноль, он очистит и освободит память.
Если вы не создадите экземпляр как необязательный, данные останутся в памяти до тех пор, пока закрывающая функция не закончится или класс включения не будет деактивирован. Возможно, вы не знаете, когда это произойдет. Опционы дают вам контроль над тем, что остается в живых в течение долгого времени.
API MM
Многие "утечки памяти" являются причиной Frameworksкоторые имеют функцию "очистки", которую вы, возможно, не вызывали.
Хорошим примером является UIGraphicsEndImageContext()
Контекст останется в памяти до тех пор, пока эта функция не будет вызвана. Он не очищается, когда функция, создавшая контекст, заканчивается или когда задействованное изображение установлено на нуль.
Другим хорошим примером является удаление ViewControllers. Это может иметь смысл переходить к одному VC, а затем отступать назад, но segue фактически создает VC. Сессия назад не уничтожает ВК. Вызовите dismissViewControllerAnimated()
, чтобы удалить его из памяти.
Прочтите ссылки на классы и дважды проверьте, нет ли функций очистки.
Если вам нужны инструменты, чтобы найти утечку, проверьте другой ответ на этот вопрос.