Нативная разработка мобильных приложений


Экранированные и неэкранированные замыкания



Download 3,69 Mb.
Pdf ko'rish
bet68/228
Sana21.07.2022
Hajmi3,69 Mb.
#834838
1   ...   64   65   66   67   68   69   70   71   ...   228
Bog'liq
Нативная разработка мобильных приложений

Экранированные и неэкранированные замыкания
Одним из более сложных аспектов замыканий, которые важно понимать, яв
-
ляется управление памятью. Всякий раз, когда создается замыкание, перемен
-
ные и экземпляры, содержащиеся в нем, «замыкаются», и создаются сильные 
ссылки на эти объекты. Поэтому объекты остаются в памяти даже после выхода 
из области видимости кода, благодаря чему могут использоваться замыкани
-
ем. Например:
class Incrementor {
var count = 0
func increment() {
count += 1
print(count)


100

Передача сообщений
}
}
let incrementor = Incrementor()
let closure = {
incrementor.increment() // 1
incrementor.increment() // 2
}
closure() // Выведет: "1\n2"
Здесь сначала определяется класс, увеличивающий значение своего счетчи
-
ка на единицу при каждом вызове метода 
increment
. Затем создается экземпляр 
этого класса. Потом этот объект передается в замыкание, в результате чего соз
-
дается сильная ссылка на него. Это позволяет объекту оставаться в текущем 
состоянии после выхода из области видимости. В конечном итоге, когда вы
-
зывается метод 
closure()
, объект все еще хранится в памяти и может увеличить 
свой счетчик на единицу при вызове метода 
increment
(в данном случае метод 
вызывается дважды).
Актуальность объекта обеспечивается особенностями механизма управ
-
ления памятью. Swift и его предшественник Objective-C подвержены целому 
классу ошибок, известных как циклические ссылки, когда объект нельзя уда
-
лить после выхода из области видимости, потому что другие объекты удержи
-
вают ссылку на него. Поскольку замыкания создают сильную ссылку на храни
-
мые в них объекты, они фактически добавляют «метку» к объектам, которыми 
владеют. Она говорит компилятору, что тот не должен удалять такие объекты 
из памяти, потому что они могут продолжать использоваться другими объек
-
тами. В нашем примере это пригодилось в замыкании, потому что 
incrementor
продолжает оставаться в области видимости и может вызываться позже, при 
обращении к замыканию.
Замыкание, которое передается в параметре и не живет дольше вызываемой 
функции, называют 
неэкранированным
замыканием. Замыкания этого типа ис
-
пользуются по умолчанию, когда передаются в качестве аргумента. Вот пример 
неэкранированного замыкания:
class Incrementor {
var count = 0
func increment(with closure: () ­> ()) {
count += 1
closure()
}
}
let incrementor = Incrementor()
let printCount = {
print(incrementor.count)
}
incrementor.increment(with: printCount) // 1
incrementor.increment(with: printCount) // 2
Мы изменили метод 
increment()
в нашем классе 
Incrementor
, чтобы он при
-
нимал замыкание в параметре, поэтому теперь сигнатура метода имеет вид 


iOS 

101
increment(with:)
. Это замыкание вызывается сразу после увеличения пере
-
менной 
count
на единицу. В нашем примере мы передаем замыкание, которое 
просто выводит значение 
count
, вызывая его непосредственно из объекта 
in­
crementor
. Мы обращаемся к 
incrementor
непосредственно, а это означает, что 
компилятор создаст сильную ссылку и 
incrementor
будет оставаться в памяти до 
завершения приложения.
В нашем примере аргумент 
closure
, передаваемый в 
increment(with:)
, явля
-
ется неэкранированным замыканием. Оно никогда не живет дольше вызывае
-
мой функции. Давайте теперь рассмотрим экранированное замыкание, чтобы 
узнать, как оно выглядит:
class Incrementor {
var count = 0
var printingMethod: (() ­> ())?
func increment(with closure: () ­> ()) {
if printingMethod == nil {
printingMethod = closure
}
count += 1
printingMethod?()
}
}
let incrementor = Incrementor()
let printCount = {
print(incrementor.count)
}
incrementor.increment(with: printCount)
incrementor.increment(with: printCount)
Как видите, мы добавили новое свойство 
printingMethod
для сохранения по
-
лученного замыкания. Далее, в 
increment(with:)
, мы записываем полученное 
замыкание в переменную 
printingMethod
. Попробовав скомпилировать этот 
код, вы получите ошибку компиляции в строке, где выполняется присваивание 
переменной 
printingMethod
, потому что мы не указали, что это замыкание яв
-
ляется экранированным. Эта проблема решается просто: достаточно добавить 
ключевое слово 
escape
в объявление параметра 
closure
в метод 
increment(with:)

например:
func increment(with closure: @escaping () ­> ()) {
if printingMethod == nil {
printingMethod = closure
}
count += 1
printingMethod?()
}
Полный пример теперь выглядит так:
class Incrementor {
var count = 0
var printingMethod: (() ­> ())?


102

Передача сообщений
func increment(with closure: @escaping () ­> ()) {
if printingMethod == nil {
printingMethod = closure
}
count += 1
printingMethod?()
}
}
let incrementor = Incrementor()
let printCount = {
print(incrementor.count)
}
incrementor.increment(with: printCount)
incrementor.increment(with: printCount)
Это объявление предотвращает ошибку компиляции. Но мы создали дру
-
гую досадную ошибку, известную как циклическая ссылка. Такие ошибки легко 
создаются, но с трудом выявляются. Это не очень заметно в нашем небольшом 
примере, но если у вас имеется большой объект или объект, который создается 
много раз, вы можете быстро исчерпать доступную память или столкнуться 
с нежелательными и неожиданными побочными эффектами. Итак, в чем же 
наша ошибка?
Выше мы упоминали, что при создании замыкания создается сильная ссыл
-
ка, мешающая удалению замыкания из памяти, пока не исчезнут все ссылки на 
него. В нашем примере мы создаем сильную ссылку на 
incrementor
в замыкании 
printCount
. Но эта сильная ссылка на замыкание создается при сохранении его 
в 
printingMethod
внутри метода 
increment(with:)
. То есть объект хранит сильную 
ссылку на самого себя, из-за чего он никогда не выйдет из области видимости 
и не будет удален из памяти!
К счастью, в Swift есть способ превратить сильную ссылку в слабую. Давайте 
внутри замыкания объявим 
incrementor
как слабую ссылку, например:
let printCount = { [weak incrementor] in
guard let incrementor = incrementor else { return }
print(incrementor.count)
}
Обратите внимание: мы использовали объявление 
[weak
incrementor]
, чтобы 
подсказать компилятору, что 
incrementor
является слабой ссылкой. Мы также 
добавили спецификатор 
guard
в объявление 
incrementor
, чтобы гарантировать 
невозможность обращения к ссылке, если она равна 
nil
. Так мы разорвали цикл, 
потому что теперь 
Incrementor
больше не сохраняет сильную ссылку на себя при 
сохранении замыкания в 
printingMethod
внутри метода 
increment(with:)
; он со
-
храняет слабую ссылку. То есть после последнего обращения к 
incrementor
ссыл
-
ка обнулится, и объект будет благополучно удален из памяти.
В настоящее время замыкания, безусловно, являются наиболее современ
-
ным способом передачи сообщений между объектами, но иногда их возмож
-
ности избыточны. Кроме того, использование замыканий чревато ошибками, 
как только что было показано. Как оказывается, в Cocoa Touch имеются другие 
способы передачи сообщений между объектами. Давайте теперь обратим наше 


iOS 


Download 3,69 Mb.

Do'stlaringiz bilan baham:
1   ...   64   65   66   67   68   69   70   71   ...   228




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©hozir.org 2024
ma'muriyatiga murojaat qiling

kiriting | ro'yxatdan o'tish
    Bosh sahifa
юртда тантана
Боғда битган
Бугун юртда
Эшитганлар жилманглар
Эшитмадим деманглар
битган бодомлар
Yangiariq tumani
qitish marakazi
Raqamli texnologiyalar
ilishida muhokamadan
tasdiqqa tavsiya
tavsiya etilgan
iqtisodiyot kafedrasi
steiermarkischen landesregierung
asarlaringizni yuboring
o'zingizning asarlaringizni
Iltimos faqat
faqat o'zingizning
steierm rkischen
landesregierung fachabteilung
rkischen landesregierung
hamshira loyihasi
loyihasi mavsum
faolyatining oqibatlari
asosiy adabiyotlar
fakulteti ahborot
ahborot havfsizligi
havfsizligi kafedrasi
fanidan bo’yicha
fakulteti iqtisodiyot
boshqaruv fakulteti
chiqarishda boshqaruv
ishlab chiqarishda
iqtisodiyot fakultet
multiservis tarmoqlari
fanidan asosiy
Uzbek fanidan
mavzulari potok
asosidagi multiservis
'aliyyil a'ziym
billahil 'aliyyil
illaa billahil
quvvata illaa
falah' deganida
Kompyuter savodxonligi
bo’yicha mustaqil
'alal falah'
Hayya 'alal
'alas soloh
Hayya 'alas
mavsum boyicha


yuklab olish