Обожаю тесты!


Серьёзно. Особенно те, которые проваливаю. Они неоспоримо придают моему внутреннему ios-нику моментального ускорения. После них я, будто бы, обретаю возможность летать… , как и тот бедный ёж, которого пнули.
Стоп, стоп, стоп. Если вы подумали, что терпеть фиаско — постыдно, то поверьте, "неприятности случаются, это неизбежно. Пусть лучше они будут такими" (с) мой друг — Президент Тамбовской областной торгово-промышленной палаты Воронина Елена Александровна.
Природа испытателя проявляется равнозначно и в моменты триумфа, и в моменты фиаско, братан. Зато сегодня у нас есть возможность разложить по полкам всё то, чего не хватило до успеха.

История вкратце такова:


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

Приступим
Так как сведений о правильных ответах нет, придётся пройти всё заново.

1 question:




Засучиваем рукава…
Плейграунд — наше всё.



Для тех, кто с этим ни разу не сталкивался, рекомендую почитать вот тут
blog.bobthedeveloper.io/generic-protocols-with-associated-type-7e2b6e079ee2

Пишем что-то типо такого и сразу же видим подсказку:


Далее получаем код:
protocol GenericProtocol {
    associatedtype myType
    var propertyWithMyType: myType { get set }
}

class MyClass: GenericProtocol {
    var propertyWithMyType = "BOB"
}

let object = MyClass()

Ошибок не выдаёт. Да, я понимаю, что может возникнуть вопрос касаемо typealias и всё сведётся к такому коду:
class SomeClass: GenericProtocol {
 typealias myType = String
 var anyProperty: myType = "Bob"
}
, но это — уже детали.

Теперь снова вернёмся к ответам на вопрос. Напоминаю, они таковы:

  1. Да, почему нет?
  2. Нет, но может быть протокол с associatedtype с тем же мотивом
  3. Нет, т.к. это не конкретный тип данных
  4. Да, но только если протокол для объектов-наследников NSObject

На эти ответы у меня появляются вопросы:
1. Что в вашем понимании generic protocol помимо того, что он может работать с genericType-объектами?
2. Почему максимально правильный ответ заключён в вопросеНаВопрос?
3. Что есть «мотив для протокола с associatedtype»? Это же инструмент, со своими особенностями реализации и возможностями. Мотив для убийства имеет разработчик.

А теперь интересно осветить, что же такое объект-наследник NSObject?.
Во время поиска ответа проходил мимо записи где над определением ломает голову коллективный разум — twitter.com/mobileunderhood/status/1103205173336436736. Забавно.

На мой взгляд, самое доходчивое пояснение даёт Andre Braga в обсуждении www.quora.com/What-is-NSObject-When-do-Swift-developers-need-to-use-NSObject .
Его ответ тут
The answer to this question is two-pronged. Either you deal with NSObject directly by subclassing (some class that descends from) it, or indirectly when you use dynamic facilities powered by the Objective-C runtime. But what is NSObject?

It is one of the few root classes in Cocoa (the other immediately coming to mind is NSProxy). This predates Swift by a quarter of a century, mind you ;) It offers a number of useful features such as a default memory allocation scheme, a default comparison scheme (by memory address, i.e., reference identity or the equivalent of JavaScript’s === operator), and the most important, being a root class it kickstarts the message passing machinery.

See, Objective-C is a dynamic language by design through the means of its runtime. Method calls are invoked on objects in response to the arrival of a message, but messages can be intercepted, inspected, ignored, modified, forwarded, replaced and whatnot. Their handlers – the methods – can also be modified to some extent during runtime (a technique known as swizzling). This flexibility allows building several design patterns with tremendous ease, and also enables some surprising technology such as aspect oriented programming (my GitHub repo has a nice example[1] of this) without the amount of hacking involved in, for example, Aspect-J, that works by manipulating the compiled bytecode.

On the other hand Swift’s dynamic facilities are opt-in, meaning whatever can be determined statically, is, for speed; but when the flexibility is necessary, you can progressively turn the knob to dynamic.

But that's in theory. In practice, at this point in its evolution Swift doesn't have much of a dynamic runtime of its own, rather piggybacking on Objective-C’s (implicitly via adopting some objc runtime mechanism or explicitly via subclassing NSObject) on several occasions to achieve the same functionalities such as indirect method calling via selectors, nib archiving and key-value coding (essential for things we take for granted like wiring outlets and actions in Xibs and Storyboards); late binding, message forwarding, key-value observation (together with KVC this is what makes Core Data magical); method swizzling (essential for aspects which I mentioned before but there are other uses); and of course for basic interoperability such as subclassing anything that inherits from NSObject, of note the whole of UIKit/AppKit (the basic memory layout of Swift- and NS-Objects is pretty much compatible but not identical; Swift objects have other data such as vtables etc.[2] [this was Way Back Then; I’m not sure whether anyone has dissected the current Swift 3 or 4 layout]). In such situations you are dealing with dynamic objects backed by the Objective-C runtime, not pure Swift objects. And this is a good thing. The Objective-C runtime has been refined for almost 30 years now, and it’s been influential to the point of Aarch64 (the definition of the 64-bit ARM architecture that powers almost every smartphone you can buy today) having a feature whose existence can be traced back directly to it, Top Byte Ignore for tagged pointers[3][4]. Swift is truly standing on the shoulders of a giant.

So. Until Swift gets to the point of (natively) offering all of those dynamic facilities it can't fully replace the Objective-C runtime, at least not for Cocoa development, and the whole point is that it doesn't have to. It was carefully crafted to be interoperable and to bring many advantages while doing so. Whatever Swift gains in native dynamic capabilities is to the greater benefit of other platforms where the Objective-C runtime is not implied, such as Linux.

Some people look down on Objective-C or Swift or both because of this, but I really believe that this is out of ignorance. Tools have no value by themselves, it's up to the programmer to extract value from them.

And when it comes to Computer Science “purity” is rarely a good thing; unless we dope semiconductors with “impurities” we can't build processors. We employ heuristics to tackle otherwise intractable problems. We represent real numbers with floating point. We compress (moving) images and music by discarding imperceptible details. Same goes with programming environments: some compromises are for the best. In nature purebreds are seldom the fittest for survival ;)


Footnotes
[1] github.com/andrebraga/AOP-for-Objective-C
[2] www.mikeash.com/pyblog/friday-qa-2014-07-18-exploring-swift-memory-layout.html
[3] nondot.org/sabre/Resume.html
[4] developer.arm.com/docs/den0024/latest/12-the-memory-management-unit/125-translation-table-configuration/1251-virtual-address-tagging%20(https://developer.arm.com/docs/den0024/latest/12-the-memory-management-unit/125-translation-table-configuration/1251-virtual-address-tagging
, но если вкратце, то наследование объекта от NSObject-интерфейса вместо стандартного SwiftObject дает возможности полноценного использования runtime, swizzling, динамическую типизацию, особенности диспетчеризации… и прочие волшебные инструменты Objective-C.

Возвращаемся к вопросу
Лично я не вижу связки между вопросом и этим вариантом ответа. Кстати говоря, напомню, что в Objective-C generic-ов нет как таковых. В крайнем случае все объекты имеют указатели на области памяти (да-да, тот самый параметр id), что и позволяет «атипизировать» параметры.

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

2 question:



Проверяем:

Так и есть.

3 question:




Пишем код:
protocol SomeProtocol { // Вариант 1
    func some1()
}

protocol SomeAnyProtocol: Any {  // Вариант 2
    func some2()
}

protocol SomeAnyObjectProtocol: AnyObject {  // Вариант 3
    func some3()
}

class SomeClass {
    weak var delegate1: SomeProtocol?
    weak var delegate2: SomeAnyProtocol?
    weak var delegate3: SomeAnyObjectProtocol?
}

let some = SomeClass()
в случае с Any и ненаследованием протокола от чего-либо возникает ошибка:
error: 'weak' must not be applied to non-class-bound 'SomeProtocol'; consider adding a protocol conformance that has a class bound
Правильным ответом был AnyObject.
Хорошее пояснение тут
stackoverflow.com/questions/24066304/how-can-i-make-a-weak-protocol-reference-in-pure-swift-without-objc

Supplemental Answer



I was always confused about whether delegates should be weak or not. Recently I've learned more about delegates and when to use weak references, so let me add some supplemental points here for the sake of future viewers.

— The purpose of using the weak keyword is to avoid strong reference cycles (retain cycles). Strong reference cycles happen when two class instances have strong references to each other. Their reference counts never go to zero so they never get deallocated.
— You only need to use weak if the delegate is a class. Swift structs and enums are value types (their values are copied when a new instance is made), not reference types, so they don't make strong reference cycles.
— weak references are always optional (otherwise you would used unowned) and always use var (not let) so that the optional can be set to nil when it is deallocated.
— A parent class should naturally have a strong reference to its child classes and thus not use the weak keyword. When a child wants a reference to its parent, though, it should make it a weak reference by using the weak keyword.
— weak should be used when you want a reference to a class that you don't own, not just for a child referencing its parent. When two non-hierarchical classes need to reference each other, choose one to be weak. The one you choose depends on the situation. See the answers to this question for more on this.
As a general rule, delegates should be marked as weak because most delegates are referencing classes that they do not own. This is definitely true when a child is using a delegate to communicate with a parent. Using a weak reference for the delegate is what the documentation recommends. (But see this, too.)
Protocols can be used for both reference types (classes) and value types (structs, enums). So in the likely case that you need to make a delegate weak, you have to make it an object-only protocol. The way to do that is to add AnyObject to the protocol's inheritance list. (In the past you did this using the class keyword, but AnyObject is preferred now.)

protocol MyClassDelegate: AnyObject {
    // ...
}

class SomeClass {
    weak var delegate: MyClassDelegate?
}

Further Study


Reading the following articles is what helped me to understand this much better. They also discuss related issues like the unowned keyword and the strong reference cycles that happen with closures.

docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID276
developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
krakendev.io/blog/weak-and-unowned-references-in-swift
www.andrewcbancroft.com/2015/05/08/strong-weak-and-unowned-sorting-out-arc-and-swift


Related


stackoverflow.com/a/33549729/3681880
natashatherobot.com/ios-weak-delegates-swift
stackoverflow.com/questions/30056526/swift-delegation-when-to-use-weak-pointer-on-delegate



4 question:



По факту имеем:


5 question:



Лично меня подобные вопросы иногда смущают, потому что они находятся на крайне поверхностном уровне.
Вот, что в документации Apple на Objective-C для view у UIViewController:
@property(nonatomic, strong) UIView *view;
приведено описание:
The view stored in this property represents the root view for the view controller'??s view hierarchy. The default value of this property is nil.
If you access this property and its value is currently nil, the view controller automatically calls the loadView method and returns the resulting view.

Описание напоминает принцип работы lazy в Swift, отсюда такой вердикт, но вообще ответ даётся больше интуитивно, поэтому встречать такой вопрос в тесте — сомнительное удовольствие.

6 question:



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


7 question:



сила defer в том, что он вызывается после выполнения функции (да-да, после return):


8 question:



об этом написана отличная статья habr.com/ru/post/338766, спасибо IFITOWS.

9 question:



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

правильным был первый ответ, а я поспешил, глупая ошибка.

10 question:



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

Выходит, правильным был третий ответ. Ранее этот механизм не использовал, теперь буду знать!

Заключение о первой партии вопросов


Всего в тесте было 26 (если не ошибаюсь) вопросов. Тест длился примерно полчаса. В этой статье мне удалось более-менее осветить решение по 10 вопросам. Как мы видим, учиться никогда не поздно и уверенность в своих знаниях бывает ошибочной. Если к этой статье будет интерес, то постараюсь написать еще 1-2 статьи по оставшимся вопросам.