NSKeyedUnarchiver
, который является конкретным подклассом NSCoder
, работает как магия.
Пожалуйста, посмотрите на этот пример:
У меня есть 2 класса,Person
и CreditCard
class Person: NSObject, NSCoding {
var creditCard: CreditCard
init(coder aDecoder: NSCoder) {
aDecoder.decodeObject(forKey: "creditCard") as? CreditCard
}
}
class CreditCard: NSObject, NSCoding {
weak var person: Person
init(coder aDecoder: NSCoder) {
aDecoder.decodeObject(forKey: "person") as? Person
}
}
Как видите, объекты связаны друг с другом.Что удивительно в NSKeyedUnarchiver
, так это то, как он может обрабатывать бесконечные циклы.Если кодер следует за каждой ссылкой и слепо кодирует каждый объект, с которым он сталкивается, эта циклическая ссылка сгенерирует бесконечный цикл:
Person -> CreditCard -> Person -> CreditCard -> ....infinite loop
Ниже приведены шаги:
При инициализации класса Person
вызывается aDecoder.decodeObject(forKey: "creditCard") as? CreditCard
.
Этот метод вызывает init(coder aDecoder: NSCoder)
в классе CreditCard
.
Но класс CreditCard
нельзя инициализировать, поскольку в инициализаторе aDecoder.decodeObject(forKey: "person")
.И этот метод вызовет init(coder aDecoder: NSCoder)
в классе Person
.
Он будет повторяться бесконечно, и оба Person
и CreditCard
не смогут быть инициализированы, как eggи курица.
Я пытался найти, как NSKeyedArchiver
может предотвратить эту петлю.
В соответствии с этим:
https://github.com/gnustep/libs-base/blob/master/Source/NSKeyedUnarchiver.m
Хитрость заключается в том, чтобы использовать o = [className allocWithZone: _zone];
(строка 237) без init
и сохранять его в массиве.В следующий раз, когда мы вызовем aDecoder.decodeObject(forKey:)
, вместо вызова init (кодер aDecoder: NSCoder) он вернет этот объект в массиве (строка 174).
Я попытался воспроизвести это поведение в Swift длямоя библиотека.Но я не смог найти способ сделать это в Swift, потому что мы не можем сделать alloc
без init
в Swift.Любое предложение?Может, перезапись байтов в указателе объекта будет работать?