└── iOS-developer-interview.md
/iOS-developer-interview.md:
--------------------------------------------------------------------------------
1 | # Swift
2 |
3 | ## Уровень Middle
4 |
5 |
6 | Чем value type отличается от reference type?
7 |
8 | * value type передаётся / присваивается по значению, т.е. каждая переменная получает свою копию объекта.
9 | * reference type передаётся / присваивается ссылке, т.е. все переменные указывают на одну копию объекта.
10 |
11 |
12 |
13 | Какие value type есть в Swift?
14 |
15 | * `struct`
16 | * `enumeration`
17 |
18 |
19 |
20 | Как устроены типы `Int`, `Float`, `Bool`?
21 | "Простые" типы: `Int`, `Float`, `Bool` и тд. являются структурами
22 |
23 |
24 |
25 | Какие reference type есть в Swift?
26 |
27 | * `class`
28 | * `closure`
29 | * `recursive enumeration`
30 |
31 |
32 |
33 | Что такое ARC?
34 |
35 | Система управления памятью на основе подсчёта количества сильных ссылок (счётчик ссылок) на объект.
36 |
37 |
38 |
39 | Для каких типов используется ARC?
40 |
41 | reference type
42 |
43 |
44 |
45 | Что такое retain cycle (strong reference cycle)?
46 |
47 | Ситуация, при которой 2+ объекта держат сильные ссылки друг на друга, не позволяя счётчикам ссылок занулиться и освободить память.
48 |
49 |
50 |
51 | Как избежать retain cycle?
52 |
53 | * `weak` reference
54 | * `unowned` reference
55 |
56 |
57 |
58 | Почему `weak` reference объявляется как `var` а не `let`?
59 |
60 | `ARC` запишет в эту ссылку `nil` при удалении объекта.
61 |
62 |
63 |
64 | Вызовется ли при этом property observer (`didSet`)?
65 |
66 | Нет
67 |
68 |
69 |
70 | В каком ещё случае не вызовется property observer (`didSet`)?
71 |
72 | * `init`
73 | * `didSet`
74 |
75 |
76 |
77 |
78 | Чем отличается `weak` от `unowned`?
79 |
80 | * `weak`: `ARC` запишет в эту ссылку `nil` при удалении объекта.
81 | * `unowned`: `ARC` НЕ запишет в эту ссылку `nil` при удалении объекта.
82 |
83 |
84 |
85 | Что произойдёт, если обратиться к объекту по `unowned` reference после его удаления?
86 |
87 | Падение приложения / `runtime error`
88 |
89 |
90 |
91 | Как решить что использовать: `weak` или `unowned`?
92 |
93 | * `unowned` - более производительный вариант, нужно использовать в тех местах, где есть уверенность что объект не будет удалён.
94 | * `weak` - во всех остальных случаях.
95 |
96 |
97 |
98 | Что такое `autoreleasepool`?
99 |
100 |
101 | * В `Objective-C`, `NSAutoreleasePool` - объект, в который помещаются ссылки на объекты. При `drain` делает `release` для каждого `autorelease` посланного объекту.
102 | * В `Swift` - функция, принимающая на вход замыкание. После выполнения замыкания делает тоже самое для `Objective-C` объектов.
103 |
104 |
105 |
106 | Что такое `enumeration`?
107 |
108 | Value type, который может принимать одно из заданных значений (case).
109 |
110 |
111 |
112 | Что такое `associated values`?
113 |
114 | Значение `enumeration` может иметь кортеж ассоциированных с ним значений любого типа.
115 |
116 |
117 |
118 | Что такое `indirect case`?
119 |
120 | Кортеж ассоциированных значений может содержать другое значение из этого же `enumeration`.
121 |
122 |
123 |
124 | Что такое `Optional`?
125 |
126 | `enumeration` с 2мя значениями: `case none`, `case some(Wrapped)`
127 |
128 |
129 |
130 | Чем `nil` отличается от `Optional.none`?
131 |
132 | * `nil` - синтаксический сахар для `Optional.none`
133 | * Возможно из-за того, что `Optional` реализует протокол `ExpressibleByNilLiteral`.
134 |
135 |
136 |
137 | Как получить значение из Optional?
138 |
139 | * `!`
140 | * `if let`
141 | * `guard let`
142 | * `??`
143 | * `switch`
144 | * `map`
145 | * `flatMap`
146 |
147 |
148 |
149 | Какие есть базовые коллекции в Swift?
150 |
151 | * `Array`
152 | * `Dictionary`
153 | * `Set`
154 |
155 |
156 |
157 | Какого они типа: class, struct, enum?
158 |
159 | * struct
160 |
161 |
162 | ## Уровень Senior
163 |
164 |
165 | Почему NSArray типа class, а Array - struct?
166 |
167 | Сделано специально, чтобы избежать ошибок из-за изменения массива из другого потока при работе с ним на текущем потоке.
168 |
169 |
170 |
171 | Если Array - struct, значит при передаче в качестве параметра он копируется. Почему это работает быстро?
172 |
173 | `Copy-on-Write`
174 |
175 |
176 |
177 | Как реализован `Array` с `Copy-on-Write`?
178 |
179 | Внутри `Array` хранится класс `ManagedBuffer`, который отвечает за фактическое хранение данных.
180 | При каждой модификации `Array` делается проверка `isKnownUniquelyReferenced`. Если на объект более одной ссылки, делается копия.
181 |
182 |
183 |
184 | Чем отличается `@objc` от `dynamic`?
185 |
186 | * `@objc` - метод доступен из `Objective-C`. В `Objective-C` вызывается динамически, в `Swift` - статически.
187 | * `dynamic` - метод вызывается в `Swift` динамически.
188 |
189 |
190 |
191 | # Замыкания
192 |
193 | ## Уровень Middle
194 |
195 |
196 | Что такое closure (замыкание)?
197 |
198 | Отдельный блок кода, который можно передавать как параметр и вызывать в процессе выполнения программы.
199 |
200 |
201 |
202 | Что будет напечатано?
203 |
204 | ```Swift
205 | func makeIncrementer(forIncrement amount: Int) -> () -> Int {
206 | var runningTotal = 0
207 | func incrementer() -> Int {
208 | runningTotal += amount
209 | return runningTotal
210 | }
211 | return incrementer
212 | }
213 |
214 | let incrementByTen = makeIncrementer(forIncrement: 10)
215 | print(incrementByTen())
216 | print(incrementByTen())
217 | print(incrementByTen())
218 |
219 | let incrementBySeven = makeIncrementer(forIncrement: 7)
220 | print(incrementBySeven())
221 |
222 | print(incrementByTen())
223 |
224 | let alsoIncrementByTen = incrementByTen
225 | print(alsoIncrementByTen())
226 |
227 | print(incrementByTen())
228 | ```
229 |
230 |
231 | 10 20 30 7 40 50 60
232 |
233 |
234 |
235 | Что такое `escaping` замыкания?
236 |
237 | Замыкания, помеченные как `escaping` параметр функции, могут быть вызваны после завершения функции.
238 |
239 |
240 | ## Уровень Senior
241 |
242 |
243 | Что такое `autoclosure`?
244 |
245 | * `autoclosure` - замыкание, которое автоматически создаётся для того, чтобы обернуть выражение, переданное в качестве аргумента при вызове функции.
246 | * `autoclosure` позволяет отложить вычисление выражения с момента вызова до того момента, когда значение понадобится.
247 |
248 |
249 |
250 | Что делает этот код?
251 |
252 | ```Swift
253 | func allValues(in array: [Int], match predicate: (Int) -> Bool) -> Bool {
254 | return array.lazy.filter { !predicate($0) }.isEmpty
255 | }
256 | ```
257 |
258 |
259 | Проверяет, что все элементы массива удовлетворяют предикату.
260 |
261 |
262 |
263 | Какая ошибка есть в этом коде?
264 |
265 | Ошибка компиляции: `error: closure use of non-escaping parameter 'predicate' may allow it to escape`
266 |
267 | `non-escaping` замыкание используется как `escaping`
268 |
269 |
270 |
271 | Как исправить?
272 |
273 | ```Swift
274 | func allValues(in array: [Int], match predicate: (Int) -> Bool) -> Bool {
275 | return array.filter { !predicate($0) }.isEmpty
276 | }
277 | ```
278 | Минус: Создание промежуточного массива. Лишнее потребление памяти.
279 |
280 | ```Swift
281 | func allValues(in array: [Int], match predicate: @escaping (Int) -> Bool) -> Bool {
282 | return array.lazy.filter { !predicate($0) }.isEmpty
283 | }
284 | ```
285 | Минус: Замыкание стало escaping, что противоречит сути (оно всегда вызывается до завершения функции `allValues`), и лишает компилятор возможности оптимизировать код.
286 |
287 | Правильный вариант:
288 | ```Swift
289 | func allValues(in array: [Int], match predicate: (Int) -> Bool) -> Bool {
290 | return withoutActuallyEscaping(predicate) { escapablePredicate in
291 | array.lazy.filter { !escapablePredicate($0) }.isEmpty
292 | }
293 | }
294 | ```
295 |
296 |
297 | # Многопоточность
298 |
299 | ## Уровень Middle
300 |
301 |
302 | Какие есть способы реализации многопоточности?
303 |
304 | * `OperationQueue`
305 | * `DispatchQueue`
306 | * `Thread`
307 |
308 |
309 |
310 |
311 | Чем они отличаются?
312 |
313 | * `DispatchQueue` - абстракция поверх потоков. Очередь, управляющая выполнением поставленных в неё задач на пуле потоков управляемых системой.
314 | * `OperationQueue` - высокоуровневая надстройка поверх `DispatchQueue`.
315 | * Предоставляет следующие возможности поверх `DispatchQueue`:
316 | 1. Установка зависимостей между задачами.
317 | 2. Отмена всех операций.
318 | 3. Получение количества операций в очереди.
319 | 4. Динамическое управление количеством одновременно выполняемых операций.
320 | 5. Приостановка выполнения.
321 |
322 |
323 |
324 |
325 | Чем отличается `DispatchQueue.sync` от `DispatchQueue.async`?
326 |
327 | * `DispatchQueue.async` - ставит задачу в очередь и продолжает выполнение.
328 | * `DispatchQueue.sync` - ставит задачу в очередь и ждёт, пока эта операция будет выполнена.
329 |
330 |
331 | ## Уровень Senior
332 |
333 |
334 | Как синхронизировать выполнение нескольких DispatchQueue операций?
335 |
336 | * `DispatchGroup`
337 | * `DispatchQueue.sync(flags: .barrier) {}`
338 |
339 |
340 |
341 | Что будет напечатано?
342 |
343 | ```Swift
344 | let queue = DispatchQueue(label: "Queue")
345 | print("1")
346 | queue.sync {
347 | print("2")
348 | queue.sync { print("3") }
349 | queue.async { print("4") }
350 | print("5")
351 | }
352 | print("6")
353 | ```
354 |
355 |
356 | 1 2 (Приложение зависнет / упадёт)
357 |
358 |
359 |
360 | Что будет напечатано?
361 |
362 | ```Swift
363 | let queue = DispatchQueue(label: "Queue", attributes: .concurrent)
364 | print("1")
365 | queue.sync {
366 | print("2")
367 | queue.sync { print("3") }
368 | queue.async { print("4") }
369 | print("5")
370 | }
371 | print("6")
372 | ```
373 |
374 |
375 | * 1 2 3 4 5 6
376 | * 1 2 3 5 4 6
377 | * 1 2 3 5 6 4
378 |
379 |
380 |
381 | Что такое RunLoop?
382 |
383 | Программный интерфейс обработки входных источников и операций, запускаемых на потоке, в цикле
384 | Всегда есть на главном потоке.
385 | Каждая операция на RunLoop оборачивается в `autoreleasepool`.
386 | Нужен для работы таймеров.
387 | Нет у `DispatchQueue`.
388 |
389 |
390 |
391 | Когда чистится память для `DispatchQueue`?
392 |
393 | Зависит от параметра `DispatchQueue.AutoreleaseFrequency` при создании очереди.
394 | Стандартное поведение - когда у очереди нет активных задач.
395 |
396 |
397 | # iOS
398 |
399 | ## Уровень Middle
400 |
401 |
402 | Какие есть состояния жизненного цикла iOS приложения?
403 |
404 | * Not running
405 | * Inactive
406 | * Active
407 | * Background
408 | * Suspend
409 |
410 |
411 |
412 | Какие системные события приложение может/должно обрабатывать?
413 |
414 | * **Memory warning**
415 | * **Protected data becomes available/unavailable**
416 | * **State restoration**
417 | * **Open URLs**
418 | * **Local/remote notifications**
419 | * **Location changes**
420 | * **Application shortcuts**
421 | * AV sessions
422 | * File download
423 | * Handoff tasks
424 | * Inter-app communication
425 |
426 |
427 |
428 | Жизненный цикл `UIViewController`.
429 |
430 | * `init`
431 | * `viewDidLoad`
432 | * `viewWillAppear`
433 | * `viewDidAppear`
434 | * `viewWillLayoutSubviews`
435 | * `viewDidLayoutSubviews`
436 | * `viewWillDisappear`
437 | * `viewDidDisappear`
438 | * `willMove(toParent:)`
439 | * `didMove(toParent:)`
440 | * `deinit`
441 |
442 |
443 |
444 | Какая разница между использованием делегатов и нотификаций?
445 |
446 | Общее:
447 | * Получение уведомлений при каком-то изменении наблюдаемого объекта.
448 |
449 | Различия:
450 | * Делегирование позволяет передать часть логики другому объекту. Этот объект один и отвечает только на те вопросы, которые ему задают.
451 | * Нотификации позволяют подписаться на любые изменения наблюдаемого объекта.
452 | * Количество подписчиков на нотификации не ограничено.
453 | * Подписчики не влияют на логику поведения наблюдаемого объекта.
454 |
455 |
456 |
457 | Какие есть способы подписки на нотификации?
458 |
459 | * `NotificationCenter`
460 | * `NSKeyValueObserving`
461 |
462 |
463 |
464 | Чем отличается `NotificationCenter` от `NSKeyValueObserving`?
465 |
466 | * `NotificationCenter` - ручная посылка нотификаций всем подписчикам. Для подписки нужен доступ к наблюдаемому классу, чтобы посылать нотификации.
467 | * `NSKeyValueObserving` - все наследники NSObject автоматически посылают сообщения всем своим подписчикам при изменении своих полей. Можно подписаться на изменение полей любого класса.
468 |
469 |
470 |
471 | Нужно ли вызывать `NotificationCenter.removeObserver`?
472 |
473 | Есть 2 способа подписки: через селектор и через замыкание (блок).
474 | Если используются замыкания - отписываться обязательно нужно.
475 | Если используется селектор, начиная с iOS 9.0 это не обязательно. До iOS 9.0, произойдёт падение, если объект удаляется, пока у него есть подписчики.
476 |
477 |
478 | ## Уровень Senior
479 |
480 |
481 | Что будет напечатано?
482 |
483 | ```Swift
484 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
485 | print("1")
486 | DispatchQueue.main.sync { print("2") }
487 | print("3")
488 | return true
489 | }
490 | ```
491 |
492 |
493 | 1 (Приложение зависнет / упадёт)
494 |
495 |
496 |
497 | Что такое `NSKeyValueCoding`?
498 |
499 | Механизм непрямого доступа к полям любого наследника `NSObject` по имени или ключу.
500 |
501 |
502 | # UI
503 |
504 | ## Уровень Middle
505 |
506 |
507 | Чем `UIView` отличается от `CALayer`?
508 |
509 | * `CALayer` отвечает за представление информации на экране.
510 | * `UIView` содержит в себе `CALayer`, отвечает за взаимодействие с пользователем (`UIResponder`) и участвует в расчёте геометрии представления на экране (layout).
511 |
512 |
513 |
514 | Чем `frame` отличается от `bounds`?
515 |
516 | * `frame` - координаты UIView в родительской системе координат.
517 | * `bounds` - координаты видимой области в собственной системе координат.
518 |
519 |
520 |
521 | Как сделать закруглённые края у UIView?
522 |
523 | 1.
524 | ```Swift
525 | clipsToBounds = true
526 | layer.cornerRadius = *value*
527 | ```
528 | Минус: часто пересчитывается.
529 |
530 | 2.
531 | ```Swift
532 | mask = MaskView()
533 | MaskView.layerClass = CAShapeLayer.self
534 | shapeLayer.path = UIBezierPath
535 | ```
536 | Минус: Обновление маски только при изменении frame
537 |
538 | 3.
539 | ```Swift
540 | layer.cornerRadius = *value*
541 | layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, ...]
542 | layer.masksToBounds = true
543 | ```
544 | Минус: `layer.maskedCorners` доступен с iOS 11.0+.
545 |
546 |
547 |
548 | Чем `CALayer.transform` отличается от `UIView.transform`?
549 |
550 | * `CALayer.transform` - `CATransform3D`
551 | * `UIView.transform` - `CGAffineTransform` (2D)
552 |
553 |
554 |
555 | Что поменяется при изменении `CALayer.transform`, `UIView.transform` - `frame` или `bounds`?
556 |
557 | frame
558 |
559 |
560 |
561 | Что такое autolayout, как он работает?
562 |
563 | Система динамического расчёта позиции и размера UIView.
564 | Для определения позиции и размера используются заданные для UIView правила (констреинты).
565 |
566 |
567 |
568 | Что такое hugging и resistance?
569 |
570 | Приоритеты, с которыми `UIView` противостоит попыткам растянуть / сжать её от `intrinsicContentSize`.
571 |
572 |
573 |
574 | Что такое `intrinsicContentSize`?
575 |
576 | Предпочтительный размер `UIView` для отображения всех внутренностей. Не учитывает внешние ограничения.
577 |
578 |
579 |
580 | Как сообщить autolayout, что предпочтительный размер `UIView` изменился?
581 |
582 | Вызвать метод `invalidateIntrinsicContentSize`.
583 |
584 |
585 |
586 | Как анимировать позицию/размер `UIView` при помощи autolayout?
587 |
588 | ```Swift
589 | constraint.constant = *value*
590 | UIView.animate(withDuration: ) { self.layoutIfNeeded() }
591 | ```
592 |
593 |
594 | ## Уровень Senior
595 |
596 |
597 | Чем отличаются Layer tree, Presentation tree и Render tree?
598 |
599 | * Layer tree - объекты в этом дереве хранят конечные значения анимаций. При изменении свойств слоя используется объект из этого дерева.
600 | * Presentation tree - объекты в этом дереве хранят текущие значения анимаций.
601 | * Render tree - используется для фактической отрисовки. Не доступно для разработчика.
602 |
603 |
604 |
605 | Как работает `UIView.transform` и autolayout?
606 |
607 | autolayout работает с `frame` `UIView` до трансформации.
608 | Значение фрейма после трансформации не определено и должно игнорироваться.
609 |
610 |
611 |
612 | Чем отличаются методы `layoutIfNeeded`, `layoutSubviews`, `setNeedsLayout`?
613 |
614 | * `layoutIfNeeded` - немедленно обновляет layout, если это необходимо. Может начать выше по дереву.
615 | * `layoutSubviews` - непосредственный layout, начиная с текущего `UIView` и ниже по дереву. В документации не рекомендуется вызывать этот метод напрямую.
616 | * `setNeedsLayout` - помечает layout как требующий обновления. layout произойдёт на следующий цикл обновления UI.
617 |
618 |
619 |
620 | Как бы ты написал `hitTest`?
621 |
622 | ```Swift
623 | func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
624 | guard point(inside: point, with: event) else { return nil }
625 | guard isUserInteractionEnabled && !isHidden && alpha > 0.01 else { return nil }
626 | for subview in subviews {
627 | if let hitView = subview.hitTest(convert(point, to: subview), with: event) {
628 | return hitView
629 | }
630 | }
631 | return self
632 | }
633 | ```
634 |
635 |
--------------------------------------------------------------------------------