20.09.2024

Swift циклы: Swift: Loops (Циклы) | Каморка сурового программиста

Swift | Циклы

Циклы

Последнее обновление: 29.12.2017

Цикл for-in

С помощью цикла for-in мы можем перебрать элементы коллекции (массивы, множества, словари) или последовательности. Он имеет следующую форму:


for объект_последовательности in последовательность {
	
	// действия
}

Например, переберем элементы массива:


for item in 1...5 {
    
    print(item)
}

Выражение 1...5 образует последовательность из пяти чисел от 1 до 5. И цикл проходит по всем элементам последовательности. При каждом проходе он извлекает одно число и передает его в переменную item. Таким образом, цикл сработает пять раз.

С помощью оператора where можно задавать условия выборки из последовательности элементов:


for i in 0...10 where i % 2 == 0 {

	print(i) // 0, 2, 4, 6, 8, 10
}

Здесь из последовательности 0.

..10 извлекаются только те элементы, которые соответствуют условию после оператора where — i % 2 == 0, то есть четные числа.

Цикл while

Оператор while проверяет некоторое условие, и если оно возвращает true, то выполняет блок кода. Этот цикл имеет следующую форму:


while условие {
	
	// действия
}

Например:


var i = 10
while i > 0 {
	
	print(i)
	i-=1
}

При этом надо внимательно подходить к условию. Если оно всегда будет возвращать true, то мы получим бесконечный цикл, и приложение может зависнуть.

Цикл repeat-while

Цикл repeat-while

сначала выполняет один раз цикл, и если некоторое условие возвращает true, то продолжает выполнение цикла. Он имеет следующую форму:


repeat {

    // действия

} while условие

Например, перепишем предыдущий цикл while:


var i = 10

repeat {
	
	print(i)
	i-=1
} while i > 0

Здесь цикл также выполнится 10 раз, пока значение переменной i не станет равно 0.

Но рассмотрим другую ситуацию:


var i = -1

repeat {
	
	print(i)
	i-=1
} while i > 0

Несмотря на то, что переменная i меньше 0, но цикл выполнится один раз.

Операторы continue и break

Иногда возникает ситуация, когда требуется выйти из цикла, не дожидаясь его завершения. В этом случае мы можем воспользоваться оператором break.


for i in 0...10 {
    if i == 5{
        break
    }
    print(i) // 0, 1, 2, 3, 4
}

Поскольку в цикле идет проверка, равно ли значение переменной i числу 5, то когда перебор дойдет до числа 5, сработает оператор break, и цикл завершится.

Теперь поставим себе другую задачу. А что если мы хотим, чтобы при проверке цикл не завершался, а просто переходил к следующему элементу. Для этого мы можем воспользоваться оператором continue:


for i in 0...10 {
    if i == 5{
        continue
    }
    print(i) // 0, 1, 2, 3, 4, 6, 7, 8,, 9, 10
}

В этом случае цикл, когда дойдет до числа 5, которое не удовлетворяет условию проверки, просто пропустит это число и перейдет к следующему элементу последовательности.

Swift: Loops (Циклы) | Каморка сурового программиста

Есть два вида циклов:
1) с предусловием, четко обозначающим сколько раз должен выполниться цикл – for
2) цикл который будет выполняться пока условие цикла не станет false –

while
For делится на 2 подвида: for-in и for
for-in пробегается по элементам последовательности

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

var sum = 1 for _ in 1…5 { sum += sum }

var sum = 1

for _ in 1…5 {

    sum += sum

}

В Swift 2.0 добавили возможность отсеивать данные на лету с помощью where

for var i in 1…5 where i % 2 == 0 { print (i) }

for var i in 1. ..5 where i % 2 == 0 {

    print (i)

}

Будут выведены только 2 и 4, т.к. в указанном диапазоне только они делятся нацело на 2

А for – стандартный цикл с указанием стартового значения, условия выхода из цикла и инкремента

for var index = 0; index < 3; ++index { print(«index is \(index)») }

for var index = 0; index < 3; ++index {

    print(«index is \(index)»)

}

while так же делится на: while и do-while

В swift 2.0 – переименовали do-while на repeat-while

Первый может не выполниться ни разу если условие цикла выдаст сразу false, а do-while (repeat-while в swift 2.0) выполнится как минимум 1 раз, т. к. условие будет проверяться после выполнения тела цикла

и

do { //Swift 1.2 } while условие

do { //Swift 1.2

} while условие

Swift 2.0:

repeat { } while условие

repeat {

} while условие

Swift документация     do, for, iOS, loops, repeat, swift, swift 2.0, while, документация, перевод, программирование, циклы

Навигация по записям

Swift цикл

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

Языки программирования обеспечивают различные управляющие структуры более сложные пути выполнения.

Петли позволяют многократно выполнять оператор или группа операторов, следующие в большинстве языков программирования блок-схема цикла ?:

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

Тип цикла описание

для в

Обходе совокупность всех элементов, которые, например, диапазон от цифрового представления элементов в массиве, строка символов.

цикл

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

в то время как цикл

Последовательность операторов для выполнения, если условие истинно, будет работать до тех пор, условие не станет ложным.

повторить .

.. в то время как цикл

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

Операторы управления Loop

Операторы управления Loop изменить порядок выполнения кода, с помощью которого вы можете перейти код. Swift операторы управления следующий цикл:

Управляющие описание

продолжить заявление

Рассказать итерации цикла немедленно остановить этот цикл и начать заново следующей итерации.

заявление перерыва

Прерывание текущего цикла.

проваливаемся заявление

Если дело после реализации, по-прежнему со следующим случае, требует использования проваливаемся (через) по ключевому слову.

Полезные глобальные функции языка Swift

Глобальные функции (их можно вызвать отовсюду без привязки к области действия определённого типа) — это довольно старая концепция, которая была популярна в таких языках, как С и Objective-C. А вот в Swift её использовать не рекомендуется, тут лучше подходят аккуратно типизированные и разграниченные вещи типа Swifty.

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

zip и dump.

zip()

Возможно, это самая известная глобальная функция. Она нужна, чтобы объединять два и более массива в единую последовательность кортежа. Такое очень сильно помогает, когда нужно проитерировать два объекта одновременно. Без zip надо было бы вручную создать цикл for и отдельно получить каждый индекс каждого массива. А с zip можно получить элементы из всех массивов самым удобным способом for-in.

Например, есть экранная форма регистрации пользователя и мы хотим обновить текстовые поля, чтобы показать список результатов валидации, полученных из бэкенда. Пишем:

func present(validationResults: [FieldValidationResult],
inTextFields textFields: [MyTextField]) {
for i in 0..<textFields.count {
let field = textFields[i]
let result = validationResults[i]
field.render(validationResult: result)
}
}

А с функцией zip можем убрать всё ручное индексирование:

func present(validationResults: [FieldValidationResult],
inTextFields textFields: [MyTextField]) {
for (field, result) in zip(textFields, validationResults) {
field.render(validationResult: result)
}
}

Для возврата типа функции zip есть объект Zip2Sequence, который соответствует Sequence. Так что все другие методы, связанные с последовательностью, можно применить и тут, включая её трансформацию в массив.

dump()

Функция dump — это изящная альтернатива выводу объектов. В то время как вывод объектов — чистый синтаксический “сахар” для свойств типа description или debugDescription, то функция dump — суперзаряженная версия Mirror(reflecting:). Она выводит содержимое объекта при помощи рефлексии, что часто даёт результат со значительно большей информацией, в т.ч. иерархию объектов.

class Foo: NSObject {
    let bar: String = "bar"
}
let foo = Foo()
print(foo)
// <SwiftRocks.Foo: 0x1030b9250>
dump(foo)
// ▿ <SwiftRocks.Foo: 0x1030b9250> #0
//     - super: NSObject
//    - bar: "bar"

sequence()

Глобальная функция sequence() в целом немного непонятная, но при этом классная. С ней можно писать рекурсивные функции с более приятным синтаксисом.

Давайте представим, что мы меняем цвет фона в представлении и во всём остальном, что ниже по иерархии. Возможно, вам на ум приходит такой вот цикл while:

var currentView: UIView? = self
while currentView != nil {
currentView?.backgroundColor = .green
currentView = currentView?.superview
}

Это лучший юзкейс для sequence(). Формат такой: задача этой функции дать вам Sequence, которая обращается к определённому замыканию снова и снова. Так как рекурсивная часть этого метода всегда одинаковая (currentView = currentView?.superview), то мы можем применять sequence(). Так функция трансформируется в простой цикл for:

for view in sequence(first: self, next: { $0.superview } ) {
view.backgroundColor = .green
}

Этот код работает таким образом, что sequence() возвращает пользовательский тип UnfoldFirstSequence. Это простой враппер для Sequence, который продолжает применять замыкание снова и снова в его функции next().

isKnownUniquelyReferenced()

Функция isKnownUniquelyReferenced получает объект class и возвращает логическое значение, которое указывает, что на объект ссылаются только один раз. Это нужно, чтобы вы смогли реализовать семантику значений для ссылочных типов. И хотя структуры сами по себе являются типами значений, их содержимое может не быть таковым. Запомните, что перемещение класса внутрь структуры не означает, что он будет скопирован по назначению: 

class Foo: NSObject {
    var bar: String = "bar"
}
struct FooHolder {
    let foo: Foo = Foo()
    var intValue: Int = 1
}
var fooHolder = FooHolder()
var fooHolder2 = fooHolder
fooHolder2. foo.bar = "bar2"
fooHolder2.intValue = 2
print(fooHolder.intValue)
// 1
print(fooHolder2.intValue)
// 2
print(fooHolder.foo.bar)
// bar2
print(fooHolder2.foo.bar)
// bar2

Разберём пример выше: хотя fooHolder2 и его основное число — это отдельные сущности по отношению к первоначальному холдеру, основной класс всё ещё связан с ними обоими. Чтобы это решить, можно применить isKnownUniquelyReferenced. Эта функция поможет определить, когда происходит обращение к этому свойству, и при необходимости создать новый инстанс класса:

struct FooHolder {
private var _foo: Foo = Foo()
var foo: Foo {
mutating get {
if isKnownUniquelyReferenced(&_foo) {
return _foo
} else {
let newFoo = Foo()
newFoo.bar = _foo.bar
_foo = newFoo
return _foo
}
} set {
_foo = newValue
}
}
var intValue: Int = 1
}

Интересно, что стандартная библиотека Swift позволяет так использовать семантику копирования при записи по отношению к массивам и строчкам.  

repeatElement()

Функция repeatElement() делает именно то, о чём говорит её название. Подав на вход объект и число, мы получим Sequence из объектов данного типа, которая может итерироваться. Количество объектов в последовательности равно данному числу.

let repeated: Repeated<String> = repeatElement("SwiftRocks", count: 3)
for value in repeated {
print(value)
}
//SwiftRocks
//SwiftRocks
//SwiftRocks

Повторение элементов — распространённая операция в Swift, особенно для заполнения пробелов в строчках и массивах. Фактически, у большинства этих типов даже есть специальный инициализатор для этого:

let array = [Int](repeating: 0, count: 10)

Так почему вам стоит использовать repeatElement? Причина в производительности. repeatElement() возвращает тип Repeated<T> Sequence, похожий на Zip2Sequence в том, что он ничего не делает, кроме того, что обеспечивает повторяющуюся функциональность. Давайте представим, что вы хотите заменить определённую секцию числового массива другим числом. Один способ для этого — применить replaceSubrange с другим массивом:

array.replaceSubrange(2...7, with: [Int](repeating: 1, count: 6))
print(array)
// [0, 0, 1, 1, 1, 1, 1, 1, 0, 0]

Да, это срабатывает, но применение [Int](repeating:) приносит и дополнительные заботы: нужно инициализировать буферные массивы, которые тут без надобности. Если вам нужна только повторяющаяся функциональность, то пользуйтесь repeatElement. Это будет эффективным решением. 

array.replaceSubrange(2...7, with: repeatElement(1, count: 6))

stride()

Есть ещё кое-что довольно популярное — это функция stride(). Она появилась в арсенале Swift как способ создавать циклы for, которые могут пропускать определённые элементы. Она была добавлена потому, что исчез способ, эквивалентный стилю С. 

for (int i = 0; i < 10; i += 2) { . .. }

Теперь применим stride(), чтобы получить аналогичное поведение. 

for i in stride(from: 0, to: 10, by: 2) {
    // от 0 до 9, пропустить нечетные числа.
}

Аргументы этой функции соответствуют протоколу Strideable, в котором представлены объекты, отражающие концепцию смещений. 

Привожу пример того, как мы можем добавить концепцию разницы между двумя днями в объектах Date так, чтобы их можно было применять вместе со stride():

extension Date: Strideable {
func advanced(by n: Int) -> Date {
return Calendar.current.date(byAdding: .day,
value: n,
to: self)!
}
func distance(to other: Date) -> Int {
return Calendar.current.dateComponents([.day],
from: other,
to: self).day!
}
}
let startDate = Date()
let finalDate = startDate.advanced(by: 5)
for date in stride(from: startDate, to: finalDate, by: 1) {
print(date)
}
// March 24th
// March 25th
// March 26th
// March 27th
// March 28th

Помните, что у Date уже есть реализация методов Strideable, которая срабатывает за секунды. Так что копирование её в проект не сработает. 

Другие полезные математические функции

Математические функции

max()— возвращает максимальное значение аргументов;

min()— возвращает минимальное значение аргументов;

abs()— возвращает абсолютное значение аргумента (полезно в вопросах так называемого спортивного программирования).

Значения

swap()— меняет значение двух объектов. Я не упоминал об этой функции выше, потому что, если вам нужно поменять местами элементы массива, корректно будет пользоваться Array.swapAt(). Вы, конечно, можете применять swap() в других ситуациях, когда вам понадобится создать фейковое свойство aux для содержания значения.  

Заключение

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

Читайте также:


Перевод статьи Bruno Rocha: Useful Global Swift Functions

Swift Playgrounds — уже в App Store

КУПЕРТИНО — 13 сентября 2016 г. — Swift Playgrounds, передовое приложение Apple для iPad, позволяющее всем желающим научиться с лёгкостью программировать, стало доступно в App Store. В интерактивном интерфейсе Swift Playgrounds начинающие могут изучить основные элементы программирования, а также выучить Swift — лёгкий в освоении язык Apple, на котором профессиональные разработчики создают приложения мирового уровня.
 
Приложение Swift Playgrounds доступно объясняет важнейшие понятия даже тем, кто никогда не программировал. При этом им могут пользоваться и опытные разработчики, готовые экспериментировать и выражать свои творческие идеи на языке Swift.
 
«Возможность изучить программирование должна быть у каждого. Мы с радостью представляем Swift Playgrounds новому поколению программистов, которые ищут лёгкий способ освоения основных понятий в работе с настоящим кодом, — сказал Крейг Федериги, старший вице-президент Apple по программному обеспечению. — Более 100 учебных заведений во всём мире уже добавили Swift Playgrounds в свои учебные планы на эту осень. Нам не терпится увидеть результат того, чего добьются молодые программисты».
 
Swift Playgrounds содержит разработанные Apple уроки программирования, которые предлагают учащимся ряд задач и головоломок. Помогая трём экранным героям продвигаться по увлекательному трёхмерному миру, учащиеся осваивают важнейшие элементы программирования, такие как команды, функции, циклы, условные выражения и переменные. Apple также будет регулярно выпускать новые задачи для тех, кто хочет продолжать обучение.
 
В Swift Playgrounds встроены гибкие шаблоны, на основе которых пользователи могут создавать собственные программы с графическими и сенсорными интерфейсами. Готовые программы можно отправлять друзьям через Почту и Сообщения, а также публиковать в интернете. С учётом того, что в Swift Playgrounds используется настоящий код Swift, проекты можно экспортировать напрямую в Xcode и превращать в полнофункциональные приложения для iOS и Mac.
 
Доступность
Приложение Swift Playgrounds уже сегодня можно загрузить из App Store совершенно бесплатно. Swift Playgrounds работает на iPad Air и iPad Pro, а также iPad mini 2 и новее под управлением iOS 10. Подробная информация, видео, изображения и демонстрации доступны на сайте www.apple.com/swift/playgrounds.
 
Компания Apple произвела революцию в мире персональных устройств, представив в 1984 году Macintosh. Сегодня Apple является мировым лидером в области инноваций, выпуская iPhone, iPad, Mac, Apple Watch и Apple TV. Четыре программных платформы Apple — iOS, macOS, watchOS и tvOS — обеспечивают идеально слаженную работу всех устройств Apple и предоставляют пользователям уникальные сервисы, включая App Store, Apple Music, Apple Pay и iCloud. 100 000 сотрудников Apple полностью посвящают себя созданию лучших продуктов на Земле и помогают сделать мир лучше, чем он был до нас.
 
Контакты для прессы:
Сергей Исаков
Apple
isakov@apple. com
+7 495 961 24 54

STRONG UNOWNED WEAK — В чем разница? | by Hadevs

Перевод статьи Hector Matos «”WEAK, STRONG, UNOWNED, OH MY!” — A GUIDE TO REFERENCES IN SWIFT». Оригинал можно найти здесь.

Я часто беспокоюсь о наличии retain циклов в моем коде. Я считаю, что это общая проблема у многих разработчиков. Не знаю как вы, но я постоянно слышу подобные фразы: “Когда я должен использовать weak ссылки? И что вообще такое unowned?!” Проблема состоит в том, что мы используем strong, weak и unowned в нашем Swift коде, чтобы избежать retain циклов, но мы не до конца понимаем, где и какой спецификатор использовать. К счастью, я знаю, что это такое и когда это использовать! Я надеюсь, что это руководство поможет вам узнать, как работать с ними.

ARC

ARC — это механизм для автоматизированного управления памятью от Apple. Он расшифровывается как автоматический подсчет ссылок (Automatic Reference Counting). Механизм освобождает память для объектов (dealloc) только тогда, когда на них нет сильных (strong) ссылок.

STRONG

Стоит рассказать что такое strong ссылки. По сути, это обычная ссылка, но также она защищает от удаления те объекты, на которые ссылается, путем увеличения retain count на единицу. Ведь, как мы помним, что пока что-то имеет сильную ссылку на объект, он не будет уничтожен. Это важно запомнить на будущее, когда я буду объяснять что такое retain циклы и прочие вещи.

Сильные ссылки используются почти везде в Swift’e. На самом деле декларация свойства уже имеет сильную ссылку изначально. По сути, использовать подобные ссылки безопасно, когда иерархия отношений объектов линейна. Это значит, что когда родитель ссылается на дочерний объект с помощью сильных ссылок, мы не получим retain циклов, здесь все ок.

Здесь пример сильных ссылок в действии:

Здесь мы видим линейную иерархию на примере. У Kraken есть сильная ссылка к объекту класса Tentacle, а у него самого есть сильная ссылка к классу Sucker. Вся иерархия идет от родителя (Kraken) вниз до дочернего объекта (Sucker).

В похожем случае, в блоке анимаций, иерархия ссылка такая же:

Поскольку animateWithDuration является статическим методом в UIView, замыкание здесь является родителем, а self — дочерним объектом.

Но что будет, если дочерний объект захочет сделать ссылку на родительский? Здесь нам приходят на помощь weak и unowned ссылки.

WEAK

Слабая ссылка (weak reference) — это просто указатель на объект, который не защищает нас от уничтожения объекта путем ARC. Если сильные сслыки увеличивают количество ссылок на 1, то слабые — нет. К тому же слабые ссылки обнуляют указатель на ваш объект, когда он был успешно удален. Это гарантирует вам, что когда вы будете обращаться к слабой ссылки, вы получите либо правильный объект, либо nil.

В Swift’e все слабые ссылки — это неконстантные опционалы (Optionals). Потому что подобная ссылка может и будет изменяться на nil, когда на объект больше не будут указываться сильные ссылки.

Например, данный код не будет компилироваться:

Потому что tentacle это let константа. let константы по определению не могут быть изменены в рантайме. Поскольку объекты со слабыми ссылками могут быть нулевыми, если на них никто не ссылается с помощью сильных ссылок, компилятор Swift требует, чтобы все объекты с weakссылками были именно var (переменными).

Важными местами для использовани weak переменных являются те, у которых потенциально может произойти retain cycle. Это случается, когда два объекта имеют strong ссылки друг на друга. В таком случае ARC не будет генерировать соответствующий код сообщения об освобождении памяти для каждого экземпляра, так как они поддерживают друг друга.

Вот небольшое изображение Apple, которое хорошо иллюстрирует это:

Прекрасным примером этого являются API-интерфейсы Notification. Глянем на код:

На данный момент у нас есть retain цикл. Как вы можете видеть, closures (блоки замыкания) в свифте ведут себя точно так же, как и блоки в Objective-C. Если какая-либо переменная объявлена вне области действия замыкания, ссылка на эту переменную внутри области действия замыкания создает еще одну сильную ссылку на этот объект. Единственными исключениями из этого являются переменные, которые используют семантику значений, такую как Ints, Strings, Arrays и Dictionaries в Swift.

Здесь, NotificationCenter сохраняет замыкание, которое захватывает self сильно, когда вызывается функция eatHuman(). Хорошим тоном будет удалять наблюдателя (removeObserver). Проблема здесь в том, что мы не очищаем этот блок до deinit, но ARC никогда не вызовет deinit, потому что замыкание имеет сильную ссылку на экземпляр Kraken!

Подобые ошибки могут быть в таких местах, как NSTimer и NSThread.

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

Изменения self на weak не будет увеличивать количество сильных ссылок на 1, и тогда ARC уничтожит ваш объект правильно.

Чтобы использовать weak и unowned переменные в замыканиях, мы используем [] синтакс в теле этого замыкания. Пример:

Почему weak self здесь внутри квадратных скобок? Это выглядит странно! В свифте мы привыкли видеть подобные скобки у массивов. Здесь есть схожесть с ними, так как мы можем использовать несколько значений в замыканиях. Допустим:

Это уже больше похоже на массив, верно? Тепеь ты знаешь почему они в квадратных скобках. С этими знаниями мы можем исправить retain цикл в коде уведомлений, который мы видели ранее, добавив [weak self]:

Еще одно место, где мы должны использовать weak и unowned переменные — это там, где мы используем протоколы, которые выполняют роль делегатом. В свифте струтуры и enum’ы могут быть подчиняться протоколам, но они используют семантику значений. Если родительский класс использует делегирование с дочерним классом, например, так:

Тогда мы должны использовать weak переменные.

Вот почему:

Tentacle в данном случае хранит сильную ссылку на Kraken, под протоколом LossOfLimbDelegate в свойствеdelegate.

И в тоже время:

Kraken хранит сильную ссылку на Tentacle в свойстве tentacle.

Чтобы использовать weak перменную в данном сценарии, мы добавляем weak оператор в начало декларации нашего делегата:

Ты попробовал и у тебя не скомпилировалось? Да, проблема в том, что мы используем non-class протоколы, которые не могут быть помечены как weak.

В данном случае мы можем добавить в наш протокол :class.

Но когда мы не используем :class? Давайте спросим у Apple:

«Используйте протокол только для класса, когда поведение, определенное требованиями этого протокола, предполагает или требует, чтобы соответствующий тип имел ссылочную семантику, а не семантику значения».

По сути, если у вас есть ссылочная иерархия, точно такая же, как я показал выше, вы должны использовать :class. В ситуациях со структурами и перечислениями нет необходимости в :class, потому что структуры и перечисления используют Value Type, в то время как классы используют Reference Type.

UNOWNED

Поведение weak и unowned ссылок похоже, но не одинаково. Unowned ссылки, как и weak, не увеличивают количество retain ссылок на объект. Тем не менее в Swift unowned ссылка имеет преимущество — она не опциональна. Это облегчает управление объектами с unowned ссылкой. Данный принцип очень схож с force-unwrap у опционалов. Кроме того, unowned ссылки не обнуляются. Это означает, что когда объект освобождается, он не обнуляет указатель. Это означает, что использование unowned ссылок в некоторых случаях может привести к появлению висящих указателей. Для вас, стариков, которые помнят дни Objective-C, как и я, неподтвержденные ссылки отображаются на unsafe_unretained.

Это немного сбивает с толку. weak и unowned ссылки не увеличивают количество сохраняемых данных. Они оба могут быть использованы для избежания retain циклов. Так когда же мы их используем?! Согласно документации Apple:

«Используйте weak ссылку, если вы знаете, что в какой-то момент времени эта ссылка станет нулевой. И наоборот, используйте unowned ссылку, если вы знаете, что ссылка никогда не будет равна нулю.

Приходим к выводу: так же как и force-unwrap, если вы можете гарантировать, что ссылка не будет nil, то используйте unowned. Если нет, то используйте weak.

Здесь хороший пример того, как класс создает retain цикл, используя closure, который не будет nil, потому что он получает значение в init():

В этом случае цикл сохранения происходит от того, что замыкание захватывает self сильной ссылкой, в то время как self имеет сильную ссылку на замыкание через свойство замыкания. Чтобы сломать это, мы просто добавляем [unowned self] в назначение замыкания:

В этом случае мы можем предположить, что self никогда не будет nil, так как мы вызываем closure сразу после инициализации класса RetainCycle.

Если вы знаете, что ваша ссылка будет обнулена должным образом, и ваши две ссылки взаимно зависят друг от друга (один не может жить без другого), то вы должны отдавать предпочтение unowned, а не weak.

then you should prefer unowned over weak, since you aren’t going to want to have to deal with the overhead of your program trying to unnecessarily zero your reference pointers.

Идеальным местом для использования unowned ссылок будут lazy замыкания:

Нам нужен unowned self здесь чтобы избежать retain цикла. Kraken держит businessCardName closure все время, а сам closure держит Kraken'a. Они взаимозависимы, поэтому они всегда будут освобождены одновременно. Таким образом, он удовлетворяет правилам использования unowned!

ОДНАКО, это не должно быть перепутано с ленивыми переменными, которые НЕ ЯВЛЯЮТСЯ ЗАМЫКАНИЯМИ, такие как:

Unowned self не требуется, поскольку на самом деле ничто не сохраняет замыкание, вызываемое переменной lazy. Здесь нет замыкания, поэтому это просто ненужно. Вот скриншот, доказывающий это:

Retain циклы — это отстой. Но при тщательном написании кода можно избежать утечек памяти за счет осторожного использования weak и unowned. Я надеюсь, что это руководство поможет вам в ваших начинаниях.

Ссылки на меня:

Телеграм канал с новостями Swift’a

Телеграм чат, где вам помогут

Мой сайт

Руководство по Инструментам в Swift

Даже если вы уже создали множество iOS приложений, или если это ваш первый опыт в их создании, вы, несомненно, познакомитесь с новыми интересными возможностями или сделаете ваши приложения еще лучше.

Помимо улучшения вашего приложения, добавляя какие-то новые возможности, вы должны познакомиться еще с тем, что должны знать все хорошие разработчики приложений — это … как «инструментировать» собственный код!

Этот туториал покажет вам, как использовать самые нужные возможности инструментов, которые идут в Xcode как Instruments. Они позволят проверить ваш код на производительность, память, безопасные циклы, и решить другие проблемы.

[notice]Это руководство предполагает, что вы знакомы со Swift и iOS программированием. Если вы полный новичок в программировании iOS, то вы можете начать с изучения основ программирования на Swift или с изучения базового курса.

Готовы? Давайте погрузимся в увлекательный мир Инструментов! :][/notice]

Поехали!

Для этого туториала вам не нужно проходить процесс создания приложения с нуля. Ваша задача повысить эффективность текущего предоставленного приложения с помощью изучаемых нами инструментов — очень похоже на то, как вы бы оптимизировали ваши собственные приложения!

Скачайте стартовый проект, распакуйте его и откройте в Xcode.

Этот образец приложения использует Flickr API для поиска изображений. Для использования API вам потребуется ключ API. Для демо проектов, вы можете сгенерировать пробный ключ на сайте Flickr. Задайте любой поиск на Flickr и скопируйте ключ API из URL в нижней части — он следует за «&api_key=»  и вплоть до следующего «&».

 

Вставьте его в верхнюю часть файла FlickrSearcher.swift, заменив существующий ключ API.

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

Запустите приложение, задайте поиск, нажмите на результат, и вы увидите что-то вроде следующего:

Изучите приложение и проверьте основные функции. Можно подумать, что если готов интерфейс, то приложение готово к продаже. Тем не менее, вы увидите разницу, которую привносят инструменты в ваше приложение.

Остальная часть этого туториала покажет вам, как найти и исправить проблемы, которые все еще существуют в приложении. Вы увидите, как Instruments могут помочь отладить существующие проблемы гораздо проще и быстрее!

Профилирование времени

Первый инструмент, который вы рассмотрите — это Time Profiler. В подлежащих измерению интервалах, Instruments остановят выполнение программы и примут трассировку стека в каждом работающем потоке. Считайте, что это то же самое, когда вы нажимаете кнопку паузы в дебаггере Xcode.

Вот предварительный просмотр Time Profiler:

Этот экран отображает Call Tree. Call Tree показывает время, потраченное на выполнение различных методов в приложении. Каждая строка представляет собой метод, который следовал по пути выполнения программы. Время, потраченное на каждый метод может быть определено из числа остановок профайлера в каждом методе.

Например, если в 1 миллисекундный интервал получено 100 сэмплов образцов, и конкретный метод оказывается наверху стека в 10 из этих образцов, то вы можете сделать вывод, что этот метод занимает примерно 10% от общего времени выполнения, т.е. 10 мс — было потрачено на выполнение этого метода. Это довольно грубый подсчет, но он работает!

[icon-box icon=warning-2 width=1]
Всегда тестируйте ваше приложение на реальном устройстве, а не симуляторе. Симулятор iOS имеет все «лошадиных силы» вашего Mac, в то время как устройство будет иметь ограничения мобильного оборудования. Ваше приложение может работать нормально на симуляторе, но проблема производительности может появиться, когда оно будет запущено на реальном устройстве.
[/icon-box]

Пришло время «инструментировать» ваш код!

На панели меню Xcode выберите Product\Profile или нажмите ⌘I.. Это создаст приложение и запустит Instruments. Откроется окно, которое выглядит вот так:

Все это разные шаблоны, которые идут вместе с инструментами.

Выберите инструмент Time Profiler и нажмите Choose. Это откроет новый документ Instruments. Нажмите красную кнопку записи слева сверху, чтобы начать запись и запустить приложение. Вам может быть предложено ввести пароль для авторизации Instruments, чтобы была возможность проанализировать другие процессы — не бойтесь, в данном случае это безопасно! :]

В окне Инструменты, вы можете видеть подсчет времени, маленькая стрелка движется слева направо над диаграммой (графиком) в центре экрана. Это означает, что приложение работает.

Теперь, начнем использовать приложение. Запустите поиск каких-то изображений, и выберите одно или более из списка появившихся. Вы, наверное, заметили, что показ результатов поиска происходит очень медленно, и прокрутка списка результатов поиска также невероятно раздражает — это страшно неуклюжее приложение!

Но вам повезло, потому что вы это исправите! Но в первую очередь давайте найдем в Инструментах, то что нам нужно.

Во-первых, убедитесь, что на переключателе view, на правой стороне панели инструментов, выбраны обе опции, как показано:

Это будет гарантией того, что все панели открыты. Теперь изучите скриншот и объяснение каждого раздела:

  1. Это управление записью. Красная кнопка «record» остановит и запустит профилируемое в данный момент приложение. Кнопка паузы делает именно то, что вы от нее ожидаете: приостанавливает выполнение текущего приложения.
  2. Это таймер работы приложения. Таймер отсчитывает, как долго профилируемое приложение уже запущено, и сколько раз оно запущено. Если вы остановите и перезапустите приложение, используя элементы управления записью, то запустится новый запуск, и затем на дисплее будет показано Run (Запуск) 2 из 2.
  3. Это называется треком. В случае использования шаблона Time Profiler, который вы выбрали, вы используете здесь только один инструмент, поэтому у вас только один трек. Вы узнаете больше об особенностях указанного графика в этом туториале чуть позже.
  4. Это панель деталей. Она показывает основную информацию о конкретном инструменте, который вы используете. В этом случае, она показывает самые «горячие» методы — то есть, те, которые больше всего нагружают CPU. Если вы щелкните по панели сверху, на которой стоит Call Tree (слева), и выберете Sample List, то вы увидите другой вариант представления данных. Этот вид показывает каждый образец по отдельности. Нажмите на несколько образцов, и вы увидите, что появится трассировка стека для захвата в инспектореExtended Detail (справа).
  5. Это панель инспектора. Есть три инспектора: Record Settings (настройки записи), Display Settings (настройки дисплея), и Extended Detail(детализациия). Скоро вы рассмотрите некоторые из них.

Теперь исправим наш неуклюжий интерфейс!

Погружаемся!

Выполните поиск изображений, и «погрузитесь» в список результатов. Я лично поискал бы «собак» (dogs), но вы можете искать все, что угодно, но возможно вы один из тех самых любителей «кошек» (cats)? :]

Теперь прокрутите скроллом вверх и вниз по списку несколько раз, чтобы в вашем Time Profiler появилось достаточное количество данных. Вы должны заметить, что в середине экрана меняются цифры и заполняется диаграмма. Это говорит о том, что в настоящее время используется циклы CPU.

Вы действительно скорее всего никогда не видели таких неуклюжих пользовательских интерфейсов, как этот! Table view не будет готов к отправке, пока он не будет прокручиваться, как по маслу! Чтобы помочь определить проблему, необходимо установить некоторые опции.

На правой стороне, выберите инспектор Display Settings (или нажмите ⌘ + 2). В инспекторе, под разделом Call Tree, выберите Separate by Thread, Invert Call Tree и Hide System Libraries. Это будет выглядеть так:

Вот как эти опции воздействуют на данные, отображаемые в таблице слева:

  1. Separate by Thread: Каждый поток должен рассматриваться отдельно. Это позволяет понять, какие потоки отвечают за наибольшее количество использования CPU.
  2. Invert Call Tree: С помощью этой опции, трассировка стека происходит сверху вниз. Это, как правило, именно то, что вам нужно, когда вы хотите увидеть самые глубокие методы, в которых участвует CPU.
  3. Hide System Libraries: Когда выбрана эта опция, то будут отображается только идентификаторы вашего собственного приложения. Часто бывает полезно выбирать эту опцию, поскольку, как правило, вас интересует только то, где CPU тратит время в вашем собственном коде — вы ведь не можете повлиять на то, как много CPU используют системные библиотеки!
  4. Flatten Recursion: Эта опция имеет дело с рекурсивными функциями (те, которые вызывают сами себя) в виде одной записи в каждой трассировки стека, а не нескольких.
  5. Top Functions: Включение этой функции дает возможность Инструментам рассмотреть общее время, проведенное в функции в виде суммы времени, потраченного непосредственно внутри этой функции, а также время, проведенное в функциях, вызываемых этой функцией. Таким образом, если функция А вызывает Б, то время А передается как время, потраченное в А плюс время, проведенное в Б. Это может быть полезным, так как это позволяет выбрать самый крупный отрезок времени, каждый раз, когда вы спускаетесь в стек вызовов, обнуляя ваши самые трудоемкие методы.
  6. Если вы работаете в Objective-C, тут тоже есть опция Show Obj-C Only. Если ее выбрать, то будут показаны только методы Objective-C, а не функции С или C ++. Если вы смотрите на приложения OpenGL, то вы, например, можете видеть некоторые методы или функции из C ++, хотя в самом приложении этого языка и вовсе нет.

Хотя некоторые значения могут у вас немного отличаться, но после того, как вы установите вышеописанные опции, порядок записей должен быть похож на тот, что в таблице ниже:

Ну что, это выглядит не очень хорошо. Подавляющее большинство времени «съел» метод, который применял «тональный» фильтр к миниатюрам фотографий. То, что загрузка таблицы и прокрутка, когда ячейки таблицы постоянно обновляются, стали самыми «корявыми» в пользовательском интерфейсе, тоже думаю не стало для вас сюрпризом.

Чтобы узнать больше о том, что происходит внутри этого метода, дважды щелкните на его строку в таблице. После этого появится следующее:

А вот это интересно, не правда ли?! applyTonalFilter() является методом, добавленным в UIImage в расширении, и почти 100% от времени, проведенного в нем, проводится для создания вывода CGImage, после применения фильтра на изображение.

На самом деле, мы не можем ускорить этот процесс: создание изображения является достаточно интенсивным процессом, и занимает столько времени, сколько потребуется. Давайте вернемся на шаг назад и посмотрим откуда вызывается applyTonalFilter(). Кликните на Call Tree в хлебных крошках в верхней части, чтобы вернуться на предыдущий экран:

Теперь нажмите на маленькую стрелку слева от applyTonalFilter в верхней части таблицы. Это развернет Call Tree и покажет вызывавшего applyTonalFilter. Вам, возможно, нужно будет развернуть и следующий ряд. Во время профилирования в Swift могут появиться повторяющиеся (продублированные) строки в Call Tree, с префиксом @objc. Вам нужен первый ряд, у которого есть префикс в виде имени таргета вашего приложения (InstrumentsTutorial):

В этом случае, эта строка относится к результатам коллекции view cellForItemAtIndexPath. Дважды щелкните по строке, чтобы увидеть соответствующий код из проекта.

Теперь вы уже видите, что проблема есть. Исполнение метода, применяющего тональный фильтр, занимает много времени и вызывается он непосредственно из cellForItemAtIndexPath, который будет блокировать основной поток (и, следовательно, весь пользовательский интерфейс) каждый раз, когда нужно будет наложить фильтр на изображение.

Разгрузка Работы

Решить эту проблему нужно в два этапа: первый — перенесем изображение с фильтром в фоновый поток с помощью dispatch_async. Затем кэшируйте каждое изображение после того как оно сгенерируется. Существует небольшой, простой класс кэширования изображений (с броским названием ImageCache), он включен в стартовый проект, который просто хранит изображения в памяти и извлекает их на основе данного ключа.

Теперь вам нужно перейти в Xcode и вручную найти исходный файл, который вам нужен в Инструментах, но прямо перед вашими глазами есть удобная кнопка Open in Xcode. Найдите ее на панели чуть выше кода и нажмите:

Вот так! Xcode открывается как раз в нужном месте!

Теперь, внутри collectionView(_:cellForItemAtIndexPath:) замените вызов на loadThumbnail() следующим:

      flickrPhoto.loadThumbnail { image, error in
        if cell.flickrPhoto == flickrPhoto {
          if flickrPhoto.isFavourite {
            cell.imageView.image = image
          } else {
            if let cachedImage = ImageCache.sharedCache. imageForKey("\(flickrPhoto.photoID)-filtered") {
              cell.imageView.image = cachedImage
            } else {
              dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
                if let filteredImage = image?.applyTonalFilter() {
                  ImageCache.sharedCache.setImage(filteredImage, forKey: "\(flickrPhoto.photoID)-filtered")
                  dispatch_async(dispatch_get_main_queue(), {
                    cell.imageView.image = filteredImage
                  })
                }
              })
            }
          }
        }
      }

Первый раздел кода остается таким же, как был раньше, и связан он с загрузкой в Flickr Photo эскизного (уменьшенного) изображения из Интернета. Если фотографию добавили в избранное («лайкнули»), то она ее уменьшенное (эскизное) изображение в ячейке будет отображается как есть. Если фото не понравилось (его не добавили в избранное), то будет применен тональный фильтр.

Эта часть кода, где произошли изменения: во-первых, код проверяет, есть ли отфильтрованное изображение для этого фото в кэше. Если да, то здорово, и изображение отображается в image view. Если нет, то вызов для применения тонального фильтра к изображению отправляется в фоновую очередь. Это позволит интерфейсу оставаться способным реагировать (responsive) в то время как изображение будет фильтроваться. Когда фильтр применен, изображение будет сохранено в кэше, и image view обновится в главной очереди.

Мы позаботились об отфильтрованных изображениях, но ведь есть еще оригинальные эскизы Flickr. Откройте FlickrSearcher.swift и найдитеloadThumbnail(_:). Замените его следующим образом:

  func loadThumbnail(completion: ImageLoadCompletion) {
    if let image = ImageCache.sharedCache.imageForKey(photoID) {
      completion(image: image, error: nil)
    } else {
      loadImageFromURL(URL: flickrImageURL("m")) { image, error in
        if let image = image {
          ImageCache. sharedCache.setImage(image, forKey: self.photoID)
        }
        completion(image: image, error: error)
      }
    }
  }

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

Перезапустите приложение в Instruments через Product\Profile (или ⌘I — эти горячие клавиши реально сэкономят вам время).

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

Выполните несколько поисковых запросов, и обратите внимание, что на этот раз интерфейс не совсем уж так неуклюж! Фильтр изображения в настоящее время применяется асинхронно и изображения кэшируются в фоновом режиме. Вы увидите ряд dispatch_worker_threads в Call Tree — они управляют обработкой тяжелыми графическими фильтрами.

Выглядит отлично! Можно закругляться? Еще нет! :]

Allocations, Allocations, Allocations (Распределение ресурсов)

Следующий инструмент, который мы будем рассматривать в этом туториале — это Allocations. Он дает вам подробную информацию о всех создающихся объектах и памяти, которая поддерживает их. Он также показывает оставшиеся связи каждого объекта.

Чтобы создать новый профиль инструментов, выйдите из приложения Instruments. На этот раз, запустите приложение, и откройте Debug Navigator на Navigators area. Затем кликните на Memory для отображения диаграмм использования памяти в главном окне:

Эти диаграммы полезны для получения представления о том, как работает ваше приложение. Но вашему приложению нужна еще мощность. Нажмите на кнопку Profile in Instruments, а затем Transfer для того, чтобы довести эту сессию до Инструментов. Инструмент Allocations будет запущен автоматически.

В этот раз вы увидите две дорожки. Одна из них называется Allocations (Распределение), другая Leaks (Утечки). О дорожке Allocations мы поговорим позднее, а дорожка Leaks более полезна для Objective-C, и мы не будем рассматривать ее в этом туториале.

Так что какую ошибку вы собираетесь дальше отслеживать? :]

В проекте скрывается что-то, что вы, вероятно, не знаете, что оно там. Вы, вероятно, слышали об утечках памяти. Но вы можете не знать, что в действительности существует два вида утечек:

  1. True memory leaks (действительные утечки памяти), где на объект больше никто не ссылается, но он по-прежнему занимает место — это означает, что память не может быть использована повторно. Даже в Swift и ARC, наиболее распространенный вид утечки памяти это retain cycle (сохраненный цикл) или strong reference cycle (цикл сильных ссылок). Этот цикл образуется, когда два объекта удерживают сильные ссылки друг на друга, так что каждый объект сохраняет другого от того, чтобы стать освобожденным. Это означает, что их память никогда не освободиться!
  2. Unbounded memory growth (Неограниченный рост памяти), где память по-прежнему распределяется и не освобождается. Если это продолжается вечно, то в какой-то момент система памяти будет заполнена, и у вас возникнет проблема с памятью. В iOS это означает, что приложение будет убито системой.

С работающим в приложении инструментом Allocations, создайте пять различных поисковых запросов, но пока не углубляйтесь в результаты. Убедитесь, что поиски дали результаты! Дайте приложению «догрузиться», подождав несколько секунд.

Вы должны были заметить, что диаграмма в треке Allocations начала расти. Это говорит о том, что начала распределяться память. Этот график вам говорит о том, что вы, вероятно, нашли утечку тип «неограниченного роста памяти».

То, что вы собираетесь сделать, это «generation analysis» («генерационный анализ»). Для этого нажмите кнопку под названием Mark Generation. Вы найдете кнопку в верхней части инспектора Display Settings:

Нажмите ее и вы увидите как на треке появится красный флажок, вот такой:

Целью генерационного анализа является выполнение действия несколько раз подряд для того, чтобы можно было увидеть, начинает ли расти память постоянно и неограниченно расти. Зайдите в поиск, подождите несколько секунд для того, чтобы все изображения загрузились, а затем вернитесь на главную страницу. Снова нажмите Mark Generation. Сделайте это несколько раз для различных поисковых запросов.

После того, как вы несколько раз запустите поиск, Инструменты будут выглядеть следующим образом:

Здесь вы должны почувствовать неладное. Обратите внимание, что строчка, выделенная синим, идет вверх с каждым поисковым запросом. Это, безусловно, не очень хорошо. Но подождите, что же с «предупреждениями о проблемах с памятью»? Вы ведь знаете о таких, верно? «Предупреждения о проблемах с памятью» являются способом iOS сообщить приложению о том, что памяти не хватает или она ограничена, и вы должны ее почистить.

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

Сымитируйте предупреждение о проблемах с памятью, выбрав Instrument\Simulate Memory Warning на панели Инструментов, или Hardware\Simulate Memory Warning на панели симулятора. Вы заметите, что использование памяти ослабевает совсем немного, или, возможно, вообще остается без изменений. Конечно, мы ждали не этого. Так что ищите неограниченный рост памяти где-то еще.

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

Поговорим о генерации

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

Посмотрите на колонку Growth (роста), и вы увидите, что, безусловно, он где-то происходит. Откройте одну из генераций, и вы увидите следующее:

Ничего себе, так много объектов! С чего же начать?

К сожалению, в Swift происходит большее «загромождение» этого view, чем было в Objective-C, так как в нем содержатся внутренние типы данных, о которых вам и знать не нужно. Вы можете его немного почистить, переключив Allocation Type на All Heap Allocations. Кроме того, нажмите на заголовок Growth для сортировки по размеру.

Прямо сверху находится ImageIO_jpeg_Data, и это, безусловно, то, с чем имеет дело ваше приложение. Нажмите на стрелку слева от ImageIO_jpeg_Data для отображения полного списка. Выберите одну строчку, а затем выберите Extended Detail инспектор (или нажмите ⌘ + 3):

Здесь показана трассировка стека в тот момент, когда был создан конкретный объект. Части стека, окрашенные в серый цвет относятся к системным библиотекам, части, окрашенные в черный относятся к коду вашего приложения. Чтобы получить больше контекста для этой трессировки, кликните внизу двойным щелчком на вторую черную рамку. Та единственная строка с префиксом «InstrumentsTutorial» и указывает, что это из Swift кода. Двойной клик приведет вас к коду для этого метода — вашему «старому другу» collectionView(_:cellForItemAtIndexPath:).

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

Взгляните на метод, и вы увидите, что он вызывает setImage(_:forKey:). Как вы уже видели, когда вы имели дело с Time Profiler, этот метод кэширует изображение в случае, если он снова будет использовать его в приложении. Ах! Звучит, как проблема! :]

Опять, нажмите кнопку Open in Xcode, чтобы перейти обратно в Xcode. Откройте ImageUtilities.swift и взгляните на реализацию setImage(_:forKey:):

  func setImage(image: UIImage, forKey key: String) {
    images[key] = image
  }

Этот код добавляет изображение в словарь, ключом которого является фото ID в Flickr photo. Но если вы посмотрите на код, вы увидите, что изображение никогда не удаляется из этого словаря!

Вот где происходит ваш неограниченный рост памяти: все работает как надо, но приложение никогда не удалят ничего из кэша — только добавляет их!

Чтобы устранить эту проблему, все, что вам нужно сделать, это заставить ImageCache воспринимать уведомления о проблемах с памятью от UIApplication. Когда ImageCache получает их, он должен очистить кэш.

Для того, чтобы ImageCache прининамал уведомления, добавьте следующий инициализатор и деинициализатор к классу:

init() {
  NSNotificationCenter.defaultCenter().addObserverForName(
    UIApplicationDidReceiveMemoryWarningNotification,
    object: nil, queue: NSOperationQueue.mainQueue()) { notification in
      self.images.removeAll(keepCapacity: false)
  }
}
 
deinit {
  NSNotificationCenter.defaultCenter().removeObserver(self,
    name: UIApplicationDidReceiveMemoryWarningNotification,
    object: nil)
}

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

Все, что нужно сделать коду, это удалить все объекты из кэша. Это будет гарантией того, что больше ничего не держит изображения, и они будут удалены.

Чтобы внести это изменение, снова запустите Инструменты (из Xcode нажатием ⌘+I) и повторите шаги, которые вы прошли ранее. Не забывайте, сымитировать предупреждение о проблемах с памятью в конце!

[icon-box icon=warning-2 width=1]
Убедитесь, что вы заходите из Xcode, запуская фазы билда, а не просто нажимая красную кнопку в Инструментах, для того, чтобы быть уверенными, что вы используете последнюю версию кода. Так же лучше сначала запустите приложение через Xcode, прежде чем запускать его через Instruments, потому как Xcode иногда, похоже, не обновляет запуск приложения на симуляторе до последней версии, если вы просто под Profile.[/icon-box]

На этот раз генерационный анализ должен выглядеть следующим образом:

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

Причина некоторого роста есть еще действительно из-за системных библиотек, и здесь мы не можем ничего сделать. Похоже, что системные библиотеки не освобождают всю свою память, что может быть задуманной особенностью, а может быть и багом. Все, что вы можете сделать в вашем приложении, это освободить столько памяти, сколько возможно, и вы уже сделали это! :]

Отлично сработано! Еще один вопрос улажен! Но остался еще один вопрос утечек первого типа, который мы еще не рассмотрели.

Циклы с сильными ссылками

Наконец, вы найдете цикл сильных ссылок в Flickr Search app. Как упоминалось ранее, такой цикл происходит, когда два объекта удерживают друг на друга сильные ссылки, и не могут быть освобождены, и «съедают» память. Вы можете обнаружить такой цикл с помощью инструмента Allocations, используя его по-другому.

[icon-box icon=warning-2 width=1]
Для того, чтобы следовать этой части туториала, вы должны запускать ваше приложение на реальном устройстве. К сожалению, на момент написания туториала, был баг при работе с инструментом Allocations в приложениях, запущенных на симуляторе, и большинство классов, используемых в этом туториале не отображаются в Инструментах.
[/icon-box]

Закройте Инструменты, вернитесь в Xcode, и убедитесь, что для запуска приложения выбрано реальное устройство. Выберите еще раз Product\Profile, и выберите Allocations.

На этот раз, вы не будете использовать генерационный анализ. Вместо этого, вы посмотрите на количество объектов различных типов, которые находятся в памяти. Вы уже должны увидеть огромное количество объектов, заполняющих панель деталей — очень много!

Чтобы отфильтровать объекты, представляющие для нас интерес, введите «Instruments» в качестве фильтра в поле над Allocations Summary. Теперь вам будут показаны только объекты, у которых в имени типа будет слово «Инструменты». Поскольку образец приложения называется «InstrumentsTutorial«, то Allocations теперь покажет только типы, определенные как часть этого проекта. Уже проще!

Два столбца, которые стоит отметить в Инструментах это # Persistent и # Transient. Столбец Persistent (постоянный) содержит счетчик количества объектов каждого типа, которые находятся постоянно в памяти. Столбец Transient (временный, переходный) показывает количество объектов, которые были в памяти, но были удалены. Persistent объекты используют память, transient объекты освобождают память.

Вы должны заметить, что здесь есть постоянный (persistent) экземпляр ViewController — это имеет смысл, потому что это экран, на который вы в настоящее время смотрите. Также есть AppDelegate и экземпляр Flickr API client.

Вернемся в приложение! Выполним поиск и рассмотрим результаты. Обратите внимание, что появилась куча дополнительных объектов в Инструментах: FlickrPhotos, которые были созданы при парсинге результатов поиска, SearchResultsViewController, и ImageCache среди других. Экземпляр ViewController еще постоянен (persistent), потому что он нужен своему навигационному контроллеру.

Теперь нажмите кнопку «назад» для возврата в приложение, SearchResultsViewController теперь удален из стека навигации, поэтому он должен быть удален (освобожден). Но он все еще находится в # Persistent count 1 в отчете Allocations! Почему он все еще там?

Попробуйте выполнить еще два поисковых запроса и нажмите кнопку назад после каждого из них. Теперь 3 SearchResultsViewControllers?! Тот факт, что эти view контроллеры «висят» в памяти, означает, что что-то держит сильную ссылку на них. Похоже, у вас есть цикл с сильной ссылкой!

Ваша главная подсказка в этой ситуации это то, что не только SearchResultsViewController является постоянным, но и всеSearchResultsCollectionViewCells. Вполне вероятно, что цикл с ссылкой как раз между этими двумя классами.

К сожалению, на момент написания туториала, вывод Инструментов для Swift еще не полностью полезен в некоторых случаях. Инструменты могут только дать вам подсказку, где находится проблема, и показать вам, где распределяются объекты; а ваша работа выяснить, в чем проблема.

Давайте углубимся в код. Наведите указатель мыши на InstrumentsTutorial.SearchResultsCollectionViewCell в колонке Category и нажмите на маленькую стрелку справа от него. Следующий view покажет вам все allocations SearchResultsCollectionViewCells во время работы приложения. А их довольно много — по одному для каждого результата поиска!

Измените Инспектор на инспектор Extended Detail, нажав третью иконку в верхней части панели. Это инспектор показывает трассировку стека выбранного распределения (allocation). Как и в предыдущей трассировке стека, черные части- это код. Дважды щелкните по верхней черной строке (которая начинается с «InstrumentsTutorial»), чтобы увидеть, где располагаются ячейки.

Ячейки размещаются в верхней части collectionView(cellForRowAtIndexPath:). Несколько строк вниз, и вы увидите, что это (без помощи Инструментов, к сожалению!):

cell.heartToggleHandler = { isStarred in
  self.collectionView.reloadItemsAtIndexPaths([ indexPath ])
}

Это замыкание, которое обрабатывает прикосновение на одну из кнопок-сердечек в collection view cells. Это там и находится цикл с сильной ссылкой, но его трудно обнаружить, если вы не сталкивались с ним ранее.

Замыкание ячеек ссылается на SearchResultsViewController, используя self, что создает сильную ссылку. Замыкание захватывает само себя. Swift фактически заставляет вас явно использовать слово self в замыканиях (в то время как вы можете, как правило, не использовать его, обращаясь к методам и свойствам текущего объекта). И вы можете не понять, что вы захватываете его. SearchResultsViewController также имеет сильную ссылку на ячейки через collection view.

Чтобы разорвать цикл с сильной ссылкой, вы можете определить список захвата (capture list) как часть определения замыкания. Этот список может быть использован для объявления экземпляров, захваченных замыканиями, либо как weak или unowned:

  • Weak должен быть использован, когда захватывающая ссылка может быть nil в будущем. Если объект, на который ссылаются, освобождается, то ссылка становится nil. По сути, они опционального типа.
  • Unowned ссылки должны быть использованы, когда замыкание и объект, на который оно ссылается, имеют один и тот же срок жизни, и должны быть удалены в одно и то же время. Unowned ссылки никогда не могут стать nil.

Для того, чтобы исправить этот цикл с сильной ссылкой, нажмите еще раз кнопку Open in Xcode и добавьте capture list (список захвата) в свойство heartToggleHandler в SearchResultsViewController.swift:

cell.heartToggleHandler = { [weak self] isStarred in
  if let strongSelf = self {
    strongSelf. collectionView.reloadItemsAtIndexPaths([ indexPath ])
  }
}

Объявляя self как weak означает, что SearchResultsViewController может быть освобожден, даже если collection view cells содержат ссылку на него, так как они теперь лишь просто слабые ссылки. И освобождение SearchResultsViewController освободит свое collection view и, затем ячейки.

Внутри Xcode, используйте ⌘+I снова запустите приложение Instruments.

Посмотрите на приложение снова из Инструментов, используя инструмент Allocations, как вы делали это раньше (помните о фильтрации результатов, чтобы оставить только классы, являющиеся частью стартового проекта). Выполните поисковый запрос, перейдите в его результаты, и обратно. Вы должны увидеть, что SearchResultsViewController и его ячейки освобождаются в тот момент, когда вы возвращаетесь обратно. Они показывают только переходные (transient) экземпляры, а не постоянные (persistent ).

Цикл разбит!

Конечный проект вы можете скачать здесь.

Что дальше?

Дальше, вы можете продолжить изучать наши туториалы по мере их появления, а также, параллельно читать перевод официальной книги по языку программирования Swift. И, для более подробного изучения языка, вы можете пройти наши курсы!

Урок подготовил: Иван Акулов

For Loops In Swift (подробное руководство) — AppyPie


By Aasif  | Последнее обновление: 3 декабря 2021 г. 10:30 | 5-минутное чтение

Цикл for — это фундаментальная концепция программирования. Вы можете повторить код с помощью цикла for и сделать свой код более выразительным и элегантным. Вы используете for-in, while и repeat-while для зацикливания в Swift.

В этом руководстве вы узнаете, как использовать цикл for-in с коллекциями и диапазонами, а также как использовать другие подходы, такие как while и repeat-while.

Цикл for в Swift — очень полезный инструмент в наборе инструментов любого разработчика, поэтому его необходимо освоить как практическому разработчику iOS.

Синтаксис циклов в Swift удивительно прост. Вот пример:

for item in items {
// Сделайте это
}

Цикл for в Swift всегда имеет ключевые слова for и in. Затем цикл for берет последовательность, элементы из приведенного выше примера, и перебирает последовательность один за другим. В приведенном выше синтаксисе каждый элемент доступен как постоянный элемент в цикле.Повторяющийся код заключен в волнистые скобки { }.

Вы можете прочитать приведенный выше код примера как:

Для каждого «элемента» в «элементах» выполните этот код.

Во многих языках программирования цикл for in называется for-each. Типичный синтаксис цикла for в других языках использует следующий формат: for(i = 0; i Перебор коллекций с помощью For-In

Давайте не будем здесь ограничиваться теорией! Каковы хорошие сценарии использования циклов for для перебора коллекций?

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

Массивы

Давайте рассмотрим пример:

let names = [«Артур», «Зафод», «Триллиан», «Форд», «Марвин»]

for name in names {
print(name)
}

Приведенный выше пример выводит все строки массива одну за другой. Имена переменных не имеют явного типа, но предполагается, что это массив строк: [String]. Следовательно, тип имени переменной должен быть String.

Вот еще один пример:

let numbers = [1, 2, 3, 4, 5, 6]
var sum = 0

for i в числах {
sum += i
}

print(sum)

Что делать, если вы хотите создать функцию, которая возвращает сумму любого массива целых чисел? Вы бы закодировали это так, с циклом for:

func sum(_ numbers:[Int]) -> Int
{
var sum = 0

for n в числах {
sum += n
}

return sum
}

let result = sum([23, 11, 9, 3, 24, 77])
print(result) // Вывод: 147

Интересно, что вы также можете использовать индекс коллекции для ссылка на конкретный элемент. Вместо для элемента в элементах вы делаете это:

для i в 0..

Словари

Как вы можете перебирать словарь? Вот как:

let scores = [“Боб”: 42, “Алиса”: 99, “Джейн”: 13]

for (name, score) in scores
{
print(“\(name)’s scores is \(score)”)
}

В приведенном выше коде мы определили словарь с именами и оценками. Тип оценок — [String: Int], поэтому в словаре есть ключи String и значения Int.

В цикле for in мы деконструируем пару ключ-значение в кортеж (имя, оценка).Имя значения соответствует ключу элемента словаря, а оценка значения соответствует значению элемента словаря. Вы превращаете кортеж в две отдельные константы.

Что делать, если у вас есть массив, но вы все еще хотите получить доступ к индексу для каждого элемента в массиве? Вы не можете использовать синтаксис for name в именах, который мы использовали раньше! К счастью, у массивов есть полезная функция enumerated(). Вот как вы его используете:

пусть простые числа = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]

для (n, простое число) в простых числах. enumerated()
{
print(«\(n) = \(prime)»)
}

В приведенном выше коде мы определили массив целых чисел, называемых простыми числами. В цикле for мы деконструируем индекс и значение элемента массива в кортеж (n, простое число). Для этого мы используем функцию enumerated() для простых чисел. Эта функция вернет массив с кортежами, состоящими из индексов и значений. Это именно то, что нам нужно!

Захват индекса значения массива, подобный этому, исключительно полезен, если вы хотите знать, «где» в массиве вы находитесь.Вы можете использовать индекс для ссылки на тот же элемент массива позже.

Варианты использования

В практической разработке приложений циклы for и коллекции полезны в следующих сценариях.

Если вы хотите применить один и тот же стиль к нескольким элементам пользовательского интерфейса одного типа, например к кнопкам. Вы просто помещаете кнопки в массив, перебираете коллекцию с помощью цикла for и устанавливаете стили для каждой отдельной кнопки в цикле for.

Вот так:

пусть кнопки = [loginButton, signupButton, facebookButton]

для кнопки в кнопках {
кнопка.backgroundColor = UIColor.red
button.layer.cornerRadius = 5.0
}

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

let points = [0,1, 0,2, 0,3, 0,5, 0,7, 0,8]

for x in 0..

Вы уже немного рассмотрели диапазоны, но давайте углубимся. Легко представить себе использование цикла for для перебора коллекций, но перебор диапазонов не менее интересен.

Вот снова пример кода с оператором закрытого диапазона:

for n in 1…5 {
print(n)
}

Оператор закрытого диапазона … создает структуру типа ClosedRange. Как вы видели в предыдущем примере, закрытый диапазон принимает два параметра: нижнюю границу (первый) и верхнюю границу (последний). Затем он создает диапазон между этими числами, включая последний.

Интересно, что вы также можете создавать диапазоны с текстовыми символами! Вот так:

пусть xyz = «x»…»z»
print(xyz.contains(«y»))
// Вывод: true

У структуры ClosedRange есть двоюродный брат Range с оператором полуоткрытого диапазона.. Забавный факт: закрытые диапазоны не могут быть пустыми, потому что они всегда будут включать «расстояние» между нижней и верхней границей — даже если оно равно 0. Полуоткрытые диапазоны могут быть пустыми, потому что верхняя граница не входит в диапазон. Попробуйте так:
(0…0).isEmpty // false
(0.. Цикл с оператором While и Repeat-While

Перебор набора данных или инструкций является важной концепцией программирования по двум причинам:

  • Вычисления и автоматизация — это не что иное, как повторение набора инструкций.Если вы обнаружите, что используете определенный фрагмент кода несколько раз с одинаковыми результатами, вы можете рассмотреть возможность использования цикла.
  • Принцип «Не повторяйся» (DRY) гласит, что… ты не должен повторяться (видишь, что я там сделал?). Это относится и к циклам for, поэтому, если вы делаете что-то три раза, когда вы можете сделать это один раз и повторить это три раза, часто разумнее создать цикл.

Цикл for-in — не единственный вид синтаксиса цикла, который есть в Swift.Есть еще пара:

  1. Цикл while повторяет код до тех пор, пока условие не станет ложным. Это наиболее полезно, когда вы хотите что-то повторить, но не знаете, сколько раз. Условие всегда оценивается в начале цикла. Вот так:
  2. while(условие == true) {
    // Повторить это
    }

  3. Циклы повторения-пока точно такие же, как циклы, за исключением того, что они оценивают условие в конце цикла. Он всегда будет запускаться хотя бы один раз! Вот так:
  4. repeat {
    // Сделайте это
    } while(condition == true)

Операторы for-in, while и repeat-while — самые простые подходы к повторению кода.В Swift и в iOS SDK вы найдете больше концепций итерации. Всегда полезно рассмотреть эти другие варианты, иначе вы можете столкнуться с тем, что пишете неэффективный и чрезмерно объемный код.

Несколько примеров:

  • Если вы хотите манипулировать массивами, например, применить функцию к каждому из элементов массива или свести массив к одному результату (например, «сумма»), вам следует использовать фильтр (), map() и reduce() функции более высокого порядка.
  • Если вы хотите отобразить ячейки в виде таблицы, вы должны использовать реализации UITableView по умолчанию для загрузки ячейки, отображения и повторного использования.Несмотря на то, что это буквально не включает цикл, он повторяет набор инструкций несколько раз.
  • Если вы пишете функцию, которая принимает свои выходные данные в качестве входных данных для последующей итерации, вам следует использовать рекурсию вместо цикла.

И это все!

Итак, теперь вы знаете, как писать циклы for-in в Swift и для чего их лучше всего использовать. Знаете, как говорят — ленивый программист — хороший программист! Никогда не кодируйте дважды, если вместо этого можно использовать цикл…

Создайте свое приложение сейчас

✖ Об авторе

Аасиф Хан — специалист по поисковой оптимизации с более чем 10-летним опытом работы в индустрии цифрового маркетинга. Сегодня он является экспертом в области SEO, SMO, SEM, а также является одним из ведущих авторов блога Appy Pie. Он пишет о текущих тенденциях в индустрии цифрового маркетинга. Он любит заниматься в тренажерном зале, а также уделяет время таким хобби, как игра в крикет и чтение книг, среди прочего.

Swift for Loop (с примерами)

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

Например, если мы хотим показать сообщение 100 раз, мы можем использовать цикл.Это просто простой пример; вы можете добиться гораздо большего с помощью циклов.

В Swift есть 3 типа циклов:

  1. для внутреннего цикла
  2. пока цикл
  3. повтор… цикл while

Петля Swift for-in

В Swift цикл for-in используется для запуска блока кода определенное количество раз. Он используется для перебора любых последовательностей, таких как массив, диапазон, строка и т. д.

Синтаксис цикла for-in :

  для значения в последовательности{
  // заявления
}  

Здесь val обращается к каждому элементу последовательности на каждой итерации. Цикл продолжается до тех пор, пока мы не достигнем последнего элемента в последовательности .


Блок-схема цикла for-in

Работа цикла for-in

Пример: Loop Over Array

  // доступ к элементам массива
пусть языки = ["Swift", "Java", "Go", "JavaScript"]

для языка на языках {
      печать (язык)
}  

Выход

  Свифт
Ява
Идти
JavaScript  

В приведенном выше примере мы создали массив с именем языков .

Изначально значение языка устанавливается в первый элемент массива, т.е. Swift , поэтому оператор печати внутри цикла выполняется.

язык обновляется следующим элементом массива, и оператор печати выполняется снова. Таким образом, цикл выполняется до тех пор, пока не будет получен доступ к последнему элементу массива.


для цикла с пунктом

where

В Swift мы также можем добавить предложение where с циклом for-in . Он используется для реализации фильтров в цикле. То есть, если условие в , где предложение возвращает true , цикл выполняется.

  // удалить Java из массива

пусть языки = ["Swift", "Java", "Go", "JavaScript"]

для языка на языках, где язык != "Java"{
  печать (язык)
}  

Выход

  Свифт
Идти
JavaScript  

В приведенном выше примере мы использовали цикл for-in для доступа к каждому элементу языков .Обратите внимание на использование предложения where в цикле for-in .

для языка на языках, где язык != "Java"

Здесь, если условие в , где предложение возвращает true , выполняется код внутри цикла. В противном случае он не выполняется.

Итерация язык условие: != «Java» Выход
1-й Свифт правда Распечатан Swift .
2-й Ява ложь Java не печатается
3-й Перейти правда Перейти напечатано
4-й JavaScript правда JavaScript печатается

для петли с диапазоном

Диапазон — это ряд значений между двумя числовыми интервалами.Например,

  Значения переменных = 1...3  

Здесь 1...3 определяет диапазон, содержащий значения 1 , 2 , 3 .

В Swift мы можем использовать цикл for для перебора диапазона. Например,

  // итерация от i = 1 до 1 = 3
для я в 1...3 {
    печать (я)
}  

Выход

  1
2
3  

В приведенном выше примере мы использовали цикл for-in для итерации в диапазоне от 1 до 3 .

Значение i устанавливается равным 1 и обновляется до следующего числа диапазона на каждой итерации. Этот процесс продолжается до тех пор, пока не будет достигнуто число 3 .

Итерация Состояние Действие
1-й правда 1 печатается. i увеличивается до 2.
2-й правда 2 печатается. i увеличивается до 3.
3-й правда 3 печатается. i увеличивается до 4.
4-й ложный Цикл завершен

для петли с функцией шага

Если мы хотим, чтобы цикл увеличивался на некоторое фиксированное значение на каждой итерации вместо диапазона, мы должны использовать функцию шага(от:до:по) . Например,

  для i в шаге (от: 1 до: 10, по: 2) {
    печать (я)
}  

Выход

  1
3
5
7
9  

В приведенном выше примере цикл повторяется в диапазоне от 1 до 10 с интервалом 2 . Однако 10 не входит в последовательность.

Здесь значение i устанавливается равным 1 и обновляется с интервалом 2 на каждой итерации.

бесплатный учебник по взлому Swift

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

Чтобы продемонстрировать это, я хочу познакомить вас со специальной функцией отладки, называемой print() : вы даете ей текст для печати, и она его напечатает. Если вы бежите на игровой площадке, как и мы, вы увидите, что ваш текст появится в окне результатов. Если вы запускаете реальное приложение в Xcode, вы увидите, что ваш текст появляется в окне журнала Xcode. В любом случае, print() — отличный способ быстро просмотреть содержимое переменной.

Взгляните на этот код:

  print("1 x 10 равно \(1 * 10)")
print("2 x 10 равно \(2 * 10)")
print("3 x 10 равно \(3 * 10)")
print("4 x 10 равно \(4 * 10)")
print("5 x 10 равно \(5 * 10)")
print("6 x 10 равно \(6 * 10)")
print("7 x 10 равно \(7 * 10)")
print("8 x 10 равно \(8 * 10)")
print("9 x 10 равно \(9 * 10)")
print("10 x 10 равно \(10 * 10)")  

Когда он закончит работу, вы увидите таблицу умножения на 10 на панели результатов вашей игровой площадки.Но это вряд ли эффективный код, и на самом деле гораздо более чистый способ — перебрать диапазон чисел, используя так называемый оператор закрытого диапазона, который состоит из трех точек подряд: . ..

Используя оператор закрытого диапазона, мы могли бы переписать все это в три строки:

  для i в 1...10 {
    print("\(i) x 10 равно \(i * 10)")
}  

На панели результатов для нашего цикла просто отображается «(10 раз)», что означает, что цикл выполнялся 10 раз. Если вы хотите узнать, что на самом деле сделал цикл, щелкните квадрат справа от «(10 раз).Вы увидите поле с надписью «10 x 10 равно 100» внутри вашего кода, и если вы щелкните его правой кнопкой мыши, вы должны увидеть опцию «История значений». Нажмите на это сейчас, и вы должны увидеть картинку ниже:

Цикл считает от 1 до 10 (включая 1 и 10), присваивает это число константе i , затем выполняет блок кода внутри фигурных скобок.

Если вам не нужно знать, какой у вас номер, вместо этого вы можете использовать символ подчеркивания. Например, мы могли бы напечатать текст песни Тейлор Свифт следующим образом:

.
  var str = "Мошенники собираются"

для _ в 1 . .. 5 {
    ул += "подделка"
}

печать(стр)  

Это напечатает «Fakers got fake fake fake fake fake fake», добавляя к строке каждый раз, когда цикл повторяется.

Если Swift не должен присваивать каждый номер переменной каждый раз, когда цикл проходит, он может выполнять ваш код немного быстрее. В результате, если вы напишете вместо i в… , то не используйте i , Xcode предложит вам изменить его на _ .

Существует вариант оператора закрытого диапазона, называемый оператором полуоткрытого диапазона, и их легко спутать.Оператор полуоткрытого диапазона выглядит как ..< и считает от одного числа до , исключая другого. Например, 1 ..< 5 будет учитывать 1, 2, 3, 4.

Перебор массивов

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

.
  var song = ["Shake it Off", "You Belong with Me", "Look What You Made Me Do"]

для песни в песнях {
    print("Моя любимая песня \(песня)")
}  

Вы также можете использовать конструкцию цикла for i in для перебора массивов, потому что вы можете использовать эту константу для индексации массива.Мы могли бы даже использовать его для индексации в два массива, например:

.
  var people = ["игроки", "ненавистники", "сердцеедки", "мошенники"]
var action = ["игра", "ненависть", "сломать", "подделка"]

для я в 0 ... 3 {
    print("\(люди[i]) собираются \(действия[i])")
}  

Вы можете задаться вопросом, какая польза от оператора полуоткрытого диапазона, но он особенно полезен для работы с массивами, поскольку они считают с нуля. Таким образом, вместо того, чтобы считать от 0 до 3 включительно, мы могли бы считать от 0 до 90 537, исключая 90 538 количества элементов в массиве.

Помните: они считают с нуля, поэтому, если у них 4 элемента, максимальный индекс равен 3, поэтому нам нужно использовать , исключая для цикла.

Чтобы подсчитать количество элементов в массиве, используйте someArray.count . Итак, мы могли бы переписать наш код так:

  var people = ["игроки", "ненавистники", "сердцеедки", "мошенники"]
var action = ["игра", "ненависть", "сломать", "подделка"]

для я в 0 ..< people.count {
    print("\(люди[i]) собираются \(действия[i])")
}  

Внутренние петли

Вы можете поместить циклы внутри циклов, если хотите, и даже циклы внутри циклов внутри циклов — хотя вы можете внезапно обнаружить, что делаете что-то 10 миллионов раз, так что будьте осторожны!

Мы можем объединить два наших предыдущих цикла, чтобы создать это:

  var people = ["игроки", "ненавистники", "сердцеедки", "мошенники"]
var action = ["игра", "ненависть", "сломать", "подделка"]

для я в 0 . .< человек.количество {
    var str = "\(люди[i]) собираются"

    за _ в 1 ... 5 {
        ул += "\(действия[i])"
    }

    печать (стр)
}  

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

Одно важное замечание: хотя программисты обычно используют i , j и даже k для констант цикла, вы можете называть их как угодно: для personNumber in 0 ..< people.count вполне допустимо.

Пока циклы

Вы увидите третий вид цикла, который повторяет блок кода до тех пор, пока вы не скажете ему остановиться. Это используется для таких вещей, как игровые циклы, когда вы заранее не знаете, как долго продлится игра — вы просто продолжаете повторять «проверить касания, анимировать роботов, нарисовать экран, проверить касания…» и так далее, пока в конце концов пользователь нажмите кнопку, чтобы выйти из игры и вернуться в главное меню.

Эти петли называются , а петли , и выглядят они так:

  счетчик переменных = 0

пока правда {
    print("Счетчик теперь \(счетчик)")
    счетчик += 1

    если счетчик == 556 {
        ломать
    }
}  

Этот код вводит новое ключевое слово break .Он используется для выхода из цикла while или for в выбранной вами точке. Без него приведенный выше код никогда не закончился бы, потому что условие для проверки просто «истина», а истина всегда истинна. Без этого оператора break цикл будет бесконечным циклом, что является Плохой Вещью.

Эти циклы while лучше всего работают, когда вы используете неизвестные данные, такие как загрузка данных из Интернета, чтение из файла, такого как XML, просмотр пользовательского ввода и т. д.Это потому, что вы знаете, когда остановить цикл, только после того, как запустите его достаточное количество раз.

Существует аналог break под названием continue . В то время как выход из цикла немедленно останавливает выполнение и продолжается сразу после цикла, продолжение цикла приводит только к выходу из текущей итерации цикла — он возвращается к началу цикла и продолжает оттуда.

В качестве примера рассмотрим следующий код:

  var song = ["Shake it Off", "You Belong with Me", "Look What You Made Me Do"]

для песни в песнях {
    если песня == "Ты принадлежишь мне" {
        Продолжать
    }

    print("Моя любимая песня \(песня)")
}  

Это повторяет три песни Тейлор Свифт, но печатает названия только двух.Причиной этого является ключевое слово continue : когда цикл пытается использовать песню «You Belong with Me», вызывается continue , что означает, что цикл немедленно возвращается к началу — вызову print() . никогда не создавался, и вместо этого цикл продолжается прямо до «Посмотри, что ты заставил меня сделать».

Поддержите взлом со Swift и охватите крупнейшее в мире сообщество пользователей Swift!

For Loops In Swift (подробное руководство) — AppyPie


By Aasif  | Последнее обновление: 3 декабря 2021 г.  10:30 | 5-минутное чтение

Цикл for — это фундаментальная концепция программирования.Вы можете повторить код с помощью цикла for и сделать свой код более выразительным и элегантным. Вы используете for-in, while и repeat-while для зацикливания в Swift.

В этом руководстве вы узнаете, как использовать цикл for-in с коллекциями и диапазонами, а также как использовать другие подходы, такие как while и repeat-while.

Цикл for в Swift — очень полезный инструмент в наборе инструментов любого разработчика, поэтому его необходимо освоить как практическому разработчику iOS.

Синтаксис циклов в Swift удивительно прост.Вот пример:

for item in items {
// Сделайте это
}

Цикл for в Swift всегда имеет ключевые слова for и in. Затем цикл for берет последовательность, элементы из приведенного выше примера, и перебирает последовательность один за другим. В приведенном выше синтаксисе каждый элемент доступен как постоянный элемент в цикле. Повторяющийся код заключен в волнистые скобки { }.

Вы можете прочитать приведенный выше код примера как:

Для каждого «элемента» в «элементах» выполните этот код.

Во многих языках программирования цикл for in называется for-each. Типичный синтаксис цикла for в других языках использует следующий формат: for(i = 0; i Перебор коллекций с помощью For-In

Давайте не будем здесь ограничиваться теорией! Каковы хорошие сценарии использования циклов for для перебора коллекций?

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

Массивы

Давайте рассмотрим пример:

let names = ["Артур", "Зафод", "Триллиан", "Форд", "Марвин"]

for name in names {
print(name)
}

Приведенный выше пример выводит все строки массива одну за другой. Имена переменных не имеют явного типа, но предполагается, что это массив строк: [String]. Следовательно, тип имени переменной должен быть String.

Вот еще один пример:

let numbers = [1, 2, 3, 4, 5, 6]
var sum = 0

for i в числах {
sum += i
}

print(sum)

Что делать, если вы хотите создать функцию, которая возвращает сумму любого массива целых чисел? Вы бы закодировали это так, с циклом for:

func sum(_ numbers:[Int]) -> Int
{
var sum = 0

for n в числах {
sum += n
}

return sum
}

let result = sum([23, 11, 9, 3, 24, 77])
print(result) // Вывод: 147

Интересно, что вы также можете использовать индекс коллекции для ссылка на конкретный элемент.Вместо для элемента в элементах вы делаете это:

для i в 0..

Словари

Как вы можете перебирать словарь? Вот как:

let scores = [“Боб”: 42, “Алиса”: 99, “Джейн”: 13]

for (name, score) in scores
{
print(“\(name)'s scores is \(score)”)
}

В приведенном выше коде мы определили словарь с именами и оценками. Тип оценок — [String: Int], поэтому в словаре есть ключи String и значения Int.

В цикле for in мы деконструируем пару ключ-значение в кортеж (имя, оценка).Имя значения соответствует ключу элемента словаря, а оценка значения соответствует значению элемента словаря. Вы превращаете кортеж в две отдельные константы.

Что делать, если у вас есть массив, но вы все еще хотите получить доступ к индексу для каждого элемента в массиве? Вы не можете использовать синтаксис for name в именах, который мы использовали раньше! К счастью, у массивов есть полезная функция enumerated(). Вот как вы его используете:

пусть простые числа = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]

для (n, простое число) в простых числах.enumerated()
{
print("\(n) = \(prime)")
}

В приведенном выше коде мы определили массив целых чисел, называемых простыми числами. В цикле for мы деконструируем индекс и значение элемента массива в кортеж (n, простое число). Для этого мы используем функцию enumerated() для простых чисел. Эта функция вернет массив с кортежами, состоящими из индексов и значений. Это именно то, что нам нужно!

Захват индекса значения массива, подобный этому, исключительно полезен, если вы хотите знать, «где» в массиве вы находитесь.Вы можете использовать индекс для ссылки на тот же элемент массива позже.

Варианты использования

В практической разработке приложений циклы for и коллекции полезны в следующих сценариях.

Если вы хотите применить один и тот же стиль к нескольким элементам пользовательского интерфейса одного типа, например к кнопкам. Вы просто помещаете кнопки в массив, перебираете коллекцию с помощью цикла for и устанавливаете стили для каждой отдельной кнопки в цикле for.

Вот так:

пусть кнопки = [loginButton, signupButton, facebookButton]

для кнопки в кнопках {
кнопка.backgroundColor = UIColor.red
button.layer.cornerRadius = 5.0
}

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

let points = [0,1, 0,2, 0,3, 0,5, 0,7, 0,8]

for x in 0..

Вы уже немного рассмотрели диапазоны, но давайте углубимся. Легко представить себе использование цикла for для перебора коллекций, но перебор диапазонов не менее интересен.

Вот снова пример кода с оператором закрытого диапазона:

for n in 1…5 {
print(n)
}

Оператор закрытого диапазона … создает структуру типа ClosedRange. Как вы видели в предыдущем примере, закрытый диапазон принимает два параметра: нижнюю границу (первый) и верхнюю границу (последний). Затем он создает диапазон между этими числами, включая последний.

Интересно, что вы также можете создавать диапазоны с текстовыми символами! Вот так:

пусть xyz = «x»…»z»
print(xyz.contains("y"))
// Вывод: true

У структуры ClosedRange есть двоюродный брат Range с оператором полуоткрытого диапазона. . Забавный факт: закрытые диапазоны не могут быть пустыми, потому что они всегда будут включать «расстояние» между нижней и верхней границей — даже если оно равно 0. Полуоткрытые диапазоны могут быть пустыми, потому что верхняя граница не входит в диапазон. Попробуйте так:
(0…0).isEmpty // false
(0.. Цикл с оператором While и Repeat-While

Перебор набора данных или инструкций является важной концепцией программирования по двум причинам:

  • Вычисления и автоматизация — это не что иное, как повторение набора инструкций.Если вы обнаружите, что используете определенный фрагмент кода несколько раз с одинаковыми результатами, вы можете рассмотреть возможность использования цикла.
  • Принцип «Не повторяйся» (DRY) гласит, что… ты не должен повторяться (видишь, что я там сделал?). Это относится и к циклам for, поэтому, если вы делаете что-то три раза, когда вы можете сделать это один раз и повторить это три раза, часто разумнее создать цикл.

Цикл for-in — не единственный вид синтаксиса цикла, который есть в Swift. Есть еще пара:

  1. Цикл while повторяет код до тех пор, пока условие не станет ложным. Это наиболее полезно, когда вы хотите что-то повторить, но не знаете, сколько раз. Условие всегда оценивается в начале цикла. Вот так:
  2. while(условие == true) {
    // Повторить это
    }

  3. Циклы повторения-пока точно такие же, как циклы, за исключением того, что они оценивают условие в конце цикла. Он всегда будет запускаться хотя бы один раз! Вот так:
  4. repeat {
    // Сделайте это
    } while(condition == true)

Операторы for-in, while и repeat-while — самые простые подходы к повторению кода.В Swift и в iOS SDK вы найдете больше концепций итерации. Всегда полезно рассмотреть эти другие варианты, иначе вы можете столкнуться с тем, что пишете неэффективный и чрезмерно объемный код.

Несколько примеров:

  • Если вы хотите манипулировать массивами, например, применить функцию к каждому из элементов массива или свести массив к одному результату (например, «сумма»), вам следует использовать фильтр (), map() и reduce() функции более высокого порядка.
  • Если вы хотите отобразить ячейки в виде таблицы, вы должны использовать реализации UITableView по умолчанию для загрузки ячейки, отображения и повторного использования.Несмотря на то, что это буквально не включает цикл, он повторяет набор инструкций несколько раз.
  • Если вы пишете функцию, которая принимает свои выходные данные в качестве входных данных для последующей итерации, вам следует использовать рекурсию вместо цикла.

И это все!

Итак, теперь вы знаете, как писать циклы for-in в Swift и для чего их лучше всего использовать. Знаете, как говорят — ленивый программист — хороший программист! Никогда не кодируйте дважды, если вместо этого можно использовать цикл…

Создайте свое приложение сейчас

✖ Об авторе

Аасиф Хан — специалист по поисковой оптимизации с более чем 10-летним опытом работы в индустрии цифрового маркетинга.Сегодня он является экспертом в области SEO, SMO, SEM, а также является одним из ведущих авторов блога Appy Pie. Он пишет о текущих тенденциях в индустрии цифрового маркетинга. Он любит заниматься в тренажерном зале, а также уделяет время таким хобби, как игра в крикет и чтение книг, среди прочего.

Как использовать цикл for, for each, while и repeat в Swift (подробно)

Цикл for, for each и repeat — это примеры операторов потока управления в Swift. Несмотря на то, что большинство из них внешне похожи, различия есть.

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

Для использования цикла в Swift

Цикл for может быть самым известным методом итерации во всех языках программирования. Он также известен как цикл for-in в Swift.

Петля For-In с коллекторами

В этом примере выполняется итерация по массиву городов, также известному как коллекция в Swift.

  пусть города = ["Амстердам", "Нью-Йорк", "Сан-Франциско"]
для города в городах {
    печать(город)
}

// Амстердам
// Нью-Йорк
// Сан-Франциско  
Цикл For-In со словарями

В этом примере выполняется итерация по словарю. Здесь он выводит возраст каждого человека.

  let ages = ["Антуан": 28, "Яап": 2, "Джек": 72]
для (имя, возраст) в возрасте {
    print("\(имя) - это \(возраст) лет")
}

// Антуану 28 лет
// Яапу 2 года
// Джеку 72 года  
Объединение цикла for с диапазонами

В следующем примере используется диапазон для выполнения оператора печати 4 раза. Забавный факт, мы меняем порядок, поэтому мы создаем обратный отсчет. Это легко делается в строке, а также может использоваться в других циклах.Другой пример — такая сортировка коллекции.

  для индекса в (0...3).reversed() {
    печать("\(индекс)..")
}

/// 3..
/// 2..
/// 1..
/// 0..  

Перебор массива с помощью forEach

Приведенные выше примеры также можно использовать в цикле for each. Фактически, forEach вызывает данное замыкание для каждого элемента в последовательности в том же порядке, что и цикл for-in.

  пусть города = ["Амстердам", "Нью-Йорк", "Сан-Франциско"]
города. forEach { город в
    печать(город)
}

// Амстердам
// Нью-Йорк
// Сан-Франциско

пусть возраст = ["Антуан": 28, "Яап": 2, "Джек": 72]
ages.forEach {имя, возраст в
    print("\(имя) - это \(возраст) лет")
}

// Антуану 28 лет
// Яапу 2 года
// Джеку 72 года

(0...3).reversed().forEach {индекс в
    печать("\(индекс)..")
}

// 3..
// 2..
// 1..
// 0..  

ForIn против forEach

Хотя приведенные выше примеры демонстрируют одинаковое поведение, это не совсем так.Использование метода forEach отличается от цикла for-in двумя важными моментами:

  1. Оператор break  или continue нельзя использовать для выхода из текущего вызова закрытия тела или для пропуска последующих вызовов.
  2. Использование оператора return  в замыкании тела приведет только к выходу из замыкания, а не из внешней области, и последующие вызовы не будут пропущены.
Пример отличий

В следующем примере собираются первые 10 четных чисел набора чисел. Для этого мы используем операторы break и continue. Оператор break останавливает цикл, а оператор continue используется для пропуска текущего числа.

  переменная EvenNumbers = [Int]()
для числа в (0...100) {
    охранять evenNumbers.count < 10 еще {
        ломать
    }

    номер охранника % 2 == 0 else {
        Продолжать
    }
    evenNumbers.append(число)
}
печать (четные числа)

// [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]  

Мы не можем использовать операторы break и continue с forEach.Поэтому мы используем фильтр, чтобы получить все четные числа в сочетании с префиксом, чтобы получить первые 10 элементов.

  let evenNumbers = (0...100).filter { number -> Bool in
    возвращаемое число% 2 == 0
}. префикс (10)

печать (четные числа)

// [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]  

Пока циклы

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

  func rollDice() -> Int {
    вернуть (1...6).randomElement()!
}

пусть maxTries = 6
вар попытки = 0
переменная оценка = 0

в то время как попытки < maxTries {
    оценка += бросокDice()
    пытается += 1
}

print("Общий балл равен \(score)") // Каждый раз новый случайный балл!  

Повторять циклы while

Повторяющиеся циклы while также известны как do while в других языках. Он выполняет один проход через блок перед оценкой условия. Мы могли бы написать приведенный выше пример следующим образом.

  повторить {
    оценка += бросокDice()
    пытается += 1
} пока пытается < maxTries

print("Общий балл равен \(счет)")  

Пока против Повторить Пока

Основное различие между операторами while и repeat while заключается в моменте оценки условия. Repeat while всегда будет выполнять блок один раз до того, как будет прочитано условие. Лучший способ описать разницу — это следующий твит Дэвида Руссе.

Хороший способ понять разницу между while() {} и do {} while() рис.twitter.com/8U0ewhLduf

— Дэвид Руссе (@davrous) 12 января 2019 г.

Объединение петель с где

Цикл for in можно комбинировать с ключевым словом where. Чтобы узнать больше об этом, ознакомьтесь с разделом Где используется в Swift.

Если вы хотите еще больше улучшить свои знания Swift, посетите страницу категорий Swift. Если у вас есть дополнительные советы или отзывы, свяжитесь со мной или напишите мне в Твиттере.

Спасибо!

 

Полное руководство по циклам в Swift | by Manuel Schulze

Перебор последовательностей с помощью цикла for-in-loop

Теперь давайте посмотрим на решение, которое я использую чаще всего, когда речь идет о циклах в Swift.

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

Почему мы вообще зацикливаемся? В большинстве случаев это происходит потому, что мы хотим снова и снова выполнять операцию над определенным количеством элементов, которые часто поступают из внешних источников (например, пользовательский ввод или сетевой запрос).

Теперь мы можем получить количество элементов, а затем использовать решение на основе индекса, описанное выше. Давайте посмотрим, что в коде:

В приведенном выше фрагменте кода у нас есть 3 животных.Мы перебираем их с индексом и получаем доступ к элементу по индексу массива и распечатываем его с интерполяцией строк.

Однако это по-прежнему подвержено ошибкам. Представьте себе, что мы бы случайно использовали следующий диапазон:

 1..

Это привело бы к тому, что мы бы не выводили собаку на консоль. Или как насчет этого:

 0...animals.count 

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

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

Итак, давайте вернемся к приведенному выше примеру с лучшим подходом.

Выглядит лучше! Но как это работает? На самом деле он перебирает последовательность (в данном случае массив) и создает константу этих значений для каждой итерации, которую мы затем можем использовать внутри цикла.

Здесь нам не нужно обрабатывать индексы.Мы можем просто использовать элемент.

Как это работает со словарями? Вот код.

Как видите, мы используем тот же цикл, но на этот раз мы можем получить доступ к ключу животного с помощью свойства key и значения с помощью свойства value .

Знаете ли вы: Вы действительно можете использовать эмодзи в Swift. Вы даже можете зайти так далеко, что будете использовать их как имена переменных, но я бы рекомендовал избегать этого. Это усложняет доступ к ним.

Я опускаю наборы, так как они работают так же, как массивы с точки зрения цикла. Вам просто нужно знать, что наборы не имеют дубликатов. Но будь осторожен! Их тоже не заказывают! Это может иметь некоторые непреднамеренные побочные эффекты.

Но что, если вам все еще нужен индекс элементов? Что, если вы хотите распечатать что-то вроде 0: Собака , 1: Кошка , 2: Рыба ? Или по какой-то другой причине вам также нужно знать индекс элемента?

Цикл for-in довольно универсален, и для него тоже есть решение! Вы можете использовать функцию, перечисленную в массиве, и получить доступ к индексу, а также к элементу.

Перечисленная функция создаст кортеж со структурой (n, x) , где n — это индекс элемента, начинающийся с нуля, а x — это сам элемент.

При этом мы можем получить доступ к индексу вместе с фактическим элементом и использовать оба этих значения в одном и том же цикле без необходимости изменять индекс или обращаться к исходному массиву элементов.

Релевантность: Я уже упоминал, что я предпочитаю эту петлю в этом варианте всем остальным типам петель.Это очень универсально, и вы можете получить доступ к индексу, а также к элементу или к обоим одновременно. Убедитесь, что вы привыкли к синтаксису, потому что вы будете видеть его снова и снова.

циклов for-in в руководстве по Swift

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

Существуют различные типы циклов для управления потоком в Swift.Это for-in , forEach , while и repeat-while циклов. В этой статье мы рассмотрим базовый обзор циклов for-in в Swift. Затем мы продемонстрируем, как с ними работать, на примерах и вариантах использования с разными типами данных.

Мы сосредоточимся на следующем:

  • Синтаксис for-in циклов
  • Массивы
  • Диапазон и шаг
  • Словари
  • перечисления

Чтобы продолжить, вы должны иметь базовые знания языка Swift.

Синтаксис

for-in циклов

Синтаксис начинается со слова вместо , за которым следует конкретный элемент в цикле, который создается как константа. За ним следует слово в и, наконец, последовательность, которую вы хотите зациклить:

 для элемента в элементах {
    // делаем что-то с элементом
}
 

Например, у нас есть список акций, каждая из которых включает цену на определенную дату:

 структура Stock {
    имя переменной: строка
    переменная цена: двойная
    переменная дата = Дата ()
}
 

Мы хотим перебрать массив и распечатать данные для каждой акции.Синтаксис цикла будет выглядеть так:

 // МАРКА: - ПРИМЕР
func printDetails(для акций: [Акции]) {
    на складе на складе {
        печать (название запаса)
        печать (стока. цена)
        печать (на складе.дата)
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
пусть акции = [Акции(название: "Банан", цена: 125),
              Акции(название: "TapeBook", цена: 320),
              Акции(название: "Рамалон", цена: 3200)]

printDetails (для: акций)

// МЕТКА: - ВЫВОД
Банан
125,0
2021-05-21 22:40:42 +0000
ЛентаКнига
320,0
2021-05-21 22:40:42 +0000
Рамалон
3200. 0
2021-05-21 22:40:42 +0000
 

Зная базовый синтаксис, давайте перейдем к зацикливанию основной структуры данных: Массив !

Массивы

Из официальной документации Swift: «Массив хранит значения одного типа в упорядоченном списке. Одно и то же значение может появляться в массиве несколько раз в разных позициях».

Мы используем циклов for-in для перебора сохраненных значений и последующего доступа к каждому значению в массиве.

Базовый пример

Предположим, есть приложение, в котором мы отслеживаем пробежку пользователя.В каждом месте мы хотим отслеживать их скорость. Таким образом, в приложении мы получаем массив локаций:

 пусть местоположения: [CLLocation] = []
 

Мы проходим по массиву и для каждого местоположения печатаем скорость в этом конкретном месте:

 для местоположения в местоположениях {
    print("Скорость в точке (\(местоположение.координата.широта), \(местоположение. координата.долгота) равна \(местоположение.скорость)")
}
 

Используя другую иллюстрацию, мы создаем двумерный массив 10×10 и печатаем значение в каждой точке:

 var board: [[Int]] = Массив (повторение: Массив (повторение: 0, количество: 10), количество: 10)

для строки в доске {
    для числа в строке {
        // печатает 0, сто раз
        печать (число)
    }
}
 

Использование

, где пункт

В некоторых случаях мы хотим ограничить последовательность только элементами, которые соответствуют определенному условию.В этом сценарии мы используем ключевое слово , где .

В приложении to-do нам нужно подмножество достигнутых целей из всех целей. Предположим, такая модель:

 struct Цель: идентифицируемая, хешируемая {
    идентификатор переменной = UUID()
    var name: String = "Имя цели"
    переменная дата = Дата ()
    var targetCompleted: Bool = false
}
 

И в нашем приложении есть массив для Goal . Мы хотим пройтись по массиву и получить доступ только к тем целям, которые выполнены:

 // МАРКА: - ПРИМЕР
func getCompletedGoals(для целей: [Цель]) {
    за гол в голы где гол.цельCompleted == истина {
        /// Доступ только к завершенным целям.
        печать (цель)
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
пусть цели = [Цель (имя: «Изучить базовый синтаксис циклов for-in», targetCompleted: true),
             Goal(name: "Читать о циклах for-in и словарях"),
             Goal(name: "Читать о циклах for-in и перечислениях")]

getCompletedGoals(для: целей)

// МЕТКА: - ВЫВОД
Goal(id: B7B148D6-853B-486A-8407-CD03A904B348, name: «Изучите базовый синтаксис циклов for-in», дата: 21.05.2021, 22:50:38 +0000, targetCompleted: true)
 

Использование

enumerated()

Для одновременного доступа к каждому индексу элемента мы можем использовать метод экземпляра enumerated() .Он возвращает последовательность пар, содержащих как индекс, так и значение элемента. Взяв предыдущий пример, если мы хотим вывести индекс местоположения в массиве, мы можем написать это:

 для (индекс, местоположение) в location.enumerated() {
    print("Скорость в точке (\(местоположение.координата.широта), \(местоположение.координата.долгота) равна \(местоположение.скорость)")

    print("Индекс этого места \(индекс)")
}
 

Использование индексов

Если нам нужен только индекс элемента в массиве, мы можем использовать индексов .Это представляет действительные индексы в массиве в порядке возрастания. Он зацикливается от 0 до последнего элемента массива, т. е. array.count :

 для индекса в array.indices {
    // Доступ к индексу
}
 

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

 // МАРКА: - ПРИМЕР
func updateValues ​​(платы: inout [[Int]]) {
    для rowIndex в board.indices {
        для columnIndex на доске [0]. индексы {
            board\[rowIndex\][columnIndex] = Int.random(in: 0..<10)
        }

        печать (доска [индекс_строки])
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
var board: [[Int]] = Массив (повторение: Массив (повторение: 0, количество: 10), количество: 10)

updateValues(из: &board)

// МЕТКА: - ВЫВОД
[9, 4, 1, 7, 5, 2, 6, 4, 7, 4]
[1, 0, 1, 0, 5, 4, 5, 6, 7, 9]
[4, 7, 6, 3, 8, 9, 3, 5, 9, 5]
[8, 0, 9, 9, 6, 1, 2, 0, 2, 7]
[3, 7, 4, 1, 3, 4, 9, 9, 5, 6]
[5, 2, 5, 1, 8, 1, 8, 0, 0, 1]
[0, 4, 3, 4, 0, 6, 1, 8, 7, 5]
[7, 7, 7, 9, 1, 3, 6, 4, 0, 1]
[9, 5, 6, 5, 3, 8, 0, 1, 3, 4]
[1, 7, 7, 3, 1, 0, 7, 4, 5, 6]
 

Использование дополнительного шаблона

В случае, когда последовательность содержит необязательные значения, мы можем отфильтровать нулевые значения, используя для case let , выполняя цикл только для ненулевых элементов.

Предположим, что в предыдущем примере с приложением списка дел некоторые из наших целей не имеют никакой ценности. getCompletedGoals(для целей:) теперь принимает массив необязательных целей :

.
 // МАРКА: - ПРИМЕР
func getCompletedGoals(для целей: [Цель?]) {
    на случай пусть гол? в целях, где target.goalCompleted == false {
        /// Доступ только к завершенным целям.
        печать (цель)
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
пусть цели: [Цель?] = [Цель(название: "Узнать что-то новое!", targetCompleted: true),
                      Goal(name: "Читать о циклах for-in и словарях"),
                      ноль,
                      Goal(name: "Читать о циклах for-in и перечислениях"),
                      ноль]

getCompletedGoals(для: целей)

// МЕТКА: - ВЫВОД
Goal(id: F6CB6D77-9047-4155-99F9-24F6D178AC2B, name: "Читать о циклах for-in и словарях", дата: 21.05.2021, 23:04:58 +0000, targetCompleted: false)
Goal(id: 822CB7C6-301C-47CE-AFEE-4B17A10EE5DC, name: "Подробнее о циклах for-in и перечислениях", дата: 21.05.2021, 23:04:58 +0000, targetCompleted: false)
 

Диапазон и шаг

Мы также можем использовать циклы for-in для прохода через жестко заданные числовые диапазоны. Их можно разделить на две части:

  • Использование оператора закрытого диапазона ( )
  • Использование оператора полуоткрытого диапазона ( ..< )

Использование оператора закрытого диапазона

Оператор замкнутого диапазона создает диапазон, включающий оба конечных элемента. Базовый пример работы с этим оператором — печать 10 чисел. Здесь будут напечатаны и 1, и 10:

 для числа в 1...10 {
    print("Число равно \(число)")
}
 

FizzBuzz — это простое упражнение по программированию, в котором мы можем использовать for for-in циклов.Подсказка выглядит следующим образом:

Напишите программу, которая печатает числа от 1 до n. Если число кратно 3, напечатайте «шипение» вместо числа, а число, кратное 5, напечатайте «жужжание». Для чисел, кратных как 3, так и 5, вместо числа выведите «FizzBuzz».

Мы перебираем числа от 1 до n , используя оператор закрытого диапазона, чтобы создать константу ClosedRange . Затем мы снова перебираем кортеж в , отображая , и проверяем каждый элемент в кортеже.Если число кратно 3, мы добавляем Fizz к строке .

Когда мы проверяем каждый элемент в сопоставлении , если он также кратен 5, мы добавляем Buzz к строке с результатом FizzBuzz :

 // МАРКА: - ПРИМЕР
func fizzBuzz(для lastNumber: Int) {
    переменная результат = [строка]()
    пусть отображение = [(число: 3, значение: "шипение"), (число: 5, значение: "жужжание")]

    для числа в 1...lastNumber {
        переменная строка = ""

        для кортежа в отображении {
            если число % кортеж.число == 0 {
                строка += кортеж.значение
            }
        }

        если строка == "" {
            строка += "\(число)"
        }

        печать (результат)
    }
    вернуть результат
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
fizzBuzz(для: 10)

// МЕТКА: - ВЫВОД
["1", "2", "шипение", "4", "жужжание", "шипение", "7", "8", "шипение", "жужжание"]
 

Использование полуоткрытого привода

Оператор полуоткрытого диапазона создает диапазон, исключая последний элемент. Базовый пример работы с этим оператором — доступ к индексам массива:

 для индекса в 0..<массив.количество {
    // Доступ к индексу
}
 

Использование шага

В случаях, когда вы хотите пропустить элементы в цикле по определенному номеру, вы можете использовать шаг . Мы также можем использовать это для возврата в цикле, начиная с последнего элемента и переходя к первому.

Возвращаясь к примеру, где мы создали двумерную матрицу размером 10×10 со случайными значениями, мы хотим напечатать каждый альтернативный элемент в первой строке:

 // МАРКА: - ПРИМЕР
func printFirstRow(для доски: [[Int]]) {
    для rowIndex в шаге (от: board.количество - 1, через: 0, по: -2) {
        печать (доска\[индекс_строки\][0])
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
printFirstRow (для: доска)

// МЕТКА: - ВЫВОД
7
4
4
4
8
 

Теперь мы хотим напечатать каждый альтернативный элемент в первом столбце, но в обратном порядке:

 // МАРКА: - ПРИМЕР
func printFirstColumn (для доски: [[Int]]) {
    для rowIndex в шаге (от: board. count - 1, через: 0, по: -2) {
        печать (доска\[индекс_строки\][0])
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
printFirstColumn (для: доска)

// МЕТКА: - ВЫВОД
8
6
0
6
5
 

Словари

Мы также можем перебирать словарь , используя циклы for-in , хотя результат будет неупорядоченным.Синтаксис аналогичен массивам, каждый элемент имеет свой ключ и значение:

.
 // МАРКА: - ПРИМЕР
func printDictionary(для чисел: [Int: Int]) {
    для числа в цифрах {
        // число Dictionary.Element
        print("Значение ключа \(number.key) равно \(number.value)")
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
пусть числа: [Int:Int] = [1:2, 2:3, 3:4]

printDictionary (для: чисел)

// МЕТКА: - ВЫВОД
Значение для ключа 1 равно 2
Значение для ключа 2 равно 3
Значение для ключа 3 равно 4
 

Вместо этого мы можем явно использовать собственные ключевые слова:

 // МАРКА: - ПРИМЕР
func printStockPrices(для акций: [String: Int]) {
    за (название, цена) в акциях {
        print("\(имя) в настоящее время оценивается в $\(цена). ")
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200]

printStockPrices (для: акций)

// МЕТКА: - ВЫВОД
Сейчас банан стоит 125 долларов.
Рамалон в настоящее время оценивается в 3200 долларов.
TapeBook в настоящее время оценивается в 320 долларов.
 

Мы можем использовать вместо в словарях:

 // МАРКА: - ПРИМЕР
func printStockPrices(для акций: [String: Int]) {
    за (название, цена) в акциях, где имя == "Банан" {
        print("\(имя) в настоящее время оценивается в $\(цена).")
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200]

printStockPrices (для: акций)

// МЕТКА: - ВЫВОД
Сейчас банан стоит 125 долларов.
 

Если вам нужна самая высокая цена в этом словаре, вы можете отсортировать словарь, используя sorted(by:) :

 // МАРКА: - ПРИМЕР
func printStockPrices(для акций: [String: Int]) {
    for (название, цена) в stocks. sorted(by: {$0.value > $1.value}) {
        print("\(имя) в настоящее время оценивается в $\(цена).")
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200]

printStockPrices (для: акций)

// МЕТКА: - ВЫВОД
Рамалон в настоящее время оценивается в 3200 долларов.
TapeBook в настоящее время оценивается в 320 долларов.
Сейчас банан стоит 125 долларов.
 

Использование

KeyValuePairs

Как упоминалось ранее, Словарь не имеет определенного порядка. Если вам нужны упорядоченные пары ключ-значение, вы можете использовать KeyValuePairs . Это полезно в тех случаях, когда вы готовы пожертвовать быстрым постоянным временем поиска ради линейного времени:

 // МАРКА: - ПРИМЕР
func printStockPrices(для акций: KeyValuePairs) {
    за (название, цена) в акциях {
        print("\(имя) в настоящее время оценивается в $\(цена).")
    }
}

// ЗНАК: - ИСПОЛЬЗОВАНИЕ
пусть акции: KeyValuePairs = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200]

printStockPrices (для: акций)

// МЕТКА: - ВЫВОД
Сейчас банан стоит 125 долларов. 
TapeBook в настоящее время оценивается в 320 долларов.
Рамалон в настоящее время оценивается в 3200 долларов.
 

перечисления

Вы даже можете перебирать перечисление в Swift, следуя определенному протоколу с именем CaseIterable . Этот тип предоставляет коллекцию всех своих значений. В нашем случае это дает все случаи в Enum .Для доступа к ним мы используем свойство allCases .

Еще один пример: мы работаем над гиперказуальной игрой. Нам нужно установить разные режимы игры на главном экране. Мы создадим перечисление и пройдемся по нему, чтобы получить доступ к имени режима и имени изображения:

 перечисление GameModes: строка {
    дело аркада
    оспаривание дела
    случай случайный
    дело рассчитано
}

расширение GameModes {
    имя переменной: строка {
        self.rawValue.capitalized
    }

    изображение переменной: строка {
        переключиться на себя {
            кейс .аркада: вернуть "🕹"
            case . casual: вернуть "🎮"
            case .challenge: вернуть "🎖"
            case .timed: вернуть "⏳"
        }
    }
}

расширение GameModes: CaseIterable {}

// Использование
для режима в GameModes.allCases {
    пусть gameOptionsView = GameOptionsView()
    gameOptionsStackView.addArrangedSubview(gameOptionsView)

    gameOptionsView.set(имя: режим.имя, изображение: режим.изображение)
}
 

Заключение

Циклы — это фундаментальные знания, которые помогут вам стать лучше в Swift.В этой статье мы рассмотрели циклы for-in с различными примерами и вариантами использования.

LogRocket: полная видимость ваших веб-приложений

LogRocket — это решение для мониторинга внешних приложений, которое позволяет воспроизводить проблемы, как если бы они возникли в вашем собственном браузере. Вместо того, чтобы гадать, почему возникают ошибки, или запрашивать у пользователей скриншоты и дампы журналов, LogRocket позволяет вам воспроизвести сеанс, чтобы быстро понять, что пошло не так. Он отлично работает с любым приложением, независимо от фреймворка, и имеет плагины для регистрации дополнительного контекста из Redux, Vuex и @ngrx/store.

Помимо регистрации действий и состояния Redux, LogRocket записывает журналы консоли, ошибки JavaScript, трассировки стека, сетевые запросы/ответы с заголовками и телами, метаданные браузера и пользовательские журналы. Он также использует DOM для записи HTML и CSS на странице, воссоздавая идеальные до пикселя видео даже для самых сложных одностраничных и мобильных приложений.

Попробуйте бесплатно. .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *