Как было написано в других главах - сервисы позволяют получать объект не только по типу, но и по дополнительным критериям. В принципе список всех этих критериев, по которым можно получить экземпляр объекта и является сервисом. Но выделяется два вида критериев: основной тип и модификатор. Модификатор - дополнительная информация к типу объекта для получения экземпляра объекта. Бывают трех видов:
- Теги
- Имена
- Множественная
Тег - это типизированный модификатор. То есть любой тип в программе является тегом. Я рекомендую использовать protocol для декларирования тегов, но никто не запрещает использовать структуры или классы. Единственное что не используйте typealias - Да имя вроде другое, но, к сожалению, для swift распознать это имя очень тяжеловесная операция и поэтому от нее отказались в пользу более быстрой.
Тег является наиболее предпочтительным модификатором по сравнению с именем, так как:
- Может быть легко изменён во всей программе с помощью средств рефакторинга, которые в XCode9 начали работать.
- Имеет область видимости - Если получить объект нельзя в стороннем модуле, но базовый тип при этом известен, тэги могут спасти.
- Поддержаны библиотекой - то есть их синтаксис короткий, и работает во всех случаях.
Дальше будет приведен пример кода, как объявлять теги и зарегистрировать/получить несколько классов/объектов с разными тегами:
protocol YourTag1 {}
protocol YourTag2 {}
container.register(YourClass.init)
.as(YourProtocol.self, tag: YourTag1.self)
.as(YourProtocol.self, tag: YourTag2.self)
let obj1: YourClass = container.resolve(tag: YourTag1.self)
let obj2: YourClass = by(tag: YourTag2.self, on: *container)Мы зарегистрировали свой класс так, что он доступен по двум тегам. В реальной программе это нужно достаточно редко, но хочу обратить внимание - если объект имеет время жизни singleton, то поведение старой версии и новой будет отличаться - в старой версии для каждого тега создавался свой экземпляр класса, что слегка не соответствуем тому, что это синглетон. В новой версии это был убрано, и если надо получить один и тот же класс, но в нескольких экземплярах по разным тегам, то можно написать несколько почти одинаковых регистраций.
Выше я писал, что теги поддержаны библиотекой - посмотрите на то, как получается второй объект - такой синтаксис можно использовать в любом месте:
// в методе инициализации
container.register{ YourClass(p: by(tag: YourTag.self, on: $0) }
// во внедрении зависимостей
.injection{ $0.param = by(tag: YourTag.self, on: $1) }По этой причине я настоятельно рекомендую не использовать старый способ получения объекта по имени, а использовать новый - он более универсальный.
Начиная с версии 3.1.3 появилась возможность использовать несколько тегов одновременно. Это будет означать что выберется тот объект который соответствует сразу всем тегам. Для этого можно использовать следующий вариант синтаксиса:
// этот вариант может быть расширен на любое количество тегов
let obj: YourClass = by(tag: YourTag1.self, by(tag: YourTag2.self, on: *container))
// этот вариант может максиум содержать 3 тега - впринципе зачем больше?
let obj: YourClass = by(tags: YourTag1.self, YourTag2.self, on: *container)Имя - это строковый модификатор. То есть любая строка может быть именем для модификатора. Имя и теги очень сильно похожи, но имена имеют меньше возможностей, и все варианты будут представлены ниже:
container.register(YourClass.init)
.as(YourProtocol.self, name: "your_name")
.injection(name: "other_name") { $0.paramByName = $1 }
let obj: YourClass = container.resolve(name: "your_name")Да именно так, по имени нельзя:
- Получить объект внутри метода инициализации
- Получить объект внутри внедрения через метод
При этом можно:
- Получить объект по имени на прямую из контейнера
- Внедрить объект по имени с помощью внедрения через свойства
Одна из самых интересных возможностей - так как никто более ее не поддерживает. Это такой же модификатор, который позволяет получить все экземпляры класса по некоторому типу. В прошлой главе был пример логгера, в этой я приведу пример с животными:
container.register(Cat.init)
.as(Animal.self)
container.register(Dog.init)
.as(Animal.self)
container.register(Hamster.init)
.as(Animal.self)
let animals1: [Animal] = container.resolveMany()
let animals2: [Animal] = many(*container)
container.register{ Desert(speciesOfAnimal: many($0)) }
container.register(Desert.init)
.injection{ $0.speciesOfAnimals = many($1) }Видно, что этот модификатор поддерживается также хорошо, как и Теги. Стоит только напомнить про одну его особенность: в случае если класс имеет получение самого себя в методе инициализации через множественную регистрацию, то он просто не будет учтен в массиве объектов - его там не будет. То есть рекурсивной ссылки не будет.
Начиная с версии 3.1.3 этот модификатор стал сочетаться с тегами, то есть можно сказать: "Дай мне все объекты которые соответствуют некоторому тегу или тегам". Синтаксис для этой операции не отличается от уже существующего и выглядит следующих образом:
let animals: [Animal] = many(by(tag: YourTag1.self, on: *container))При сочетании множественного модификатора и тегов надо учитывать одно правило - many должен быть завершающим модификатором и не может находится в середине условия. В принципе компилятор не даст скомпилировать по другому.