ObjectIdentifier을 이용한 DI
ObjectIdentifier
A unique identifier for a class instance or metatype.
Overview
This unique identifier is only valid for comparisons during the lifetime of the instance.
In Swift, only class instances and metatypes have unique identities. There is no notion of identity for structs, enums, functions, or tuples.
ObjectIdenfier는 메타타입 또는 클래스타입의 인스턴스를 식별값을 정해주고 활용 할 수 있도록 도와주는데요.
DI를 적용할 때 ObjectIdenfier 활용하여 주입시키는 것을 알아보도록 하겠습니다.
(실제로 Swinject 내부도 ObjectIdenfier로 사용 중!)
class IntegerRef {
let value: Int
init(_ value: Int) {
self.value = value
}
}
let x = IntegerRef(10)
let y = x
print(ObjectIdentifier(x) == ObjectIdentifier(y))
// Prints "true"
print(x === y)
// Prints "true"
let z = IntegerRef(10)
print(ObjectIdentifier(x) == ObjectIdentifier(z))
Prints "false"
print(x === z)
// Prints "false"
기본 활용법은 위와 같이 해당 객체의 레퍼런스를 기반으로 확인할 수 있는 기능을 가지고 있는데요.
좀 더 나아가서 ObjectIdenfier을 활용하여 의존성주입을 도와 줄 수 있는 클래스를 만들어 볼게요!
protocol DependencyInjectable {
func register<T>(_ type: T.Type, closure: (DependencyInjectable) -> (T))
func resolve<T>(_ anyClassType: T.Type) -> T?
}
객체 타입을 저장 할 수 있는 register와
타입을 넣으면 등록되어 있는 객체를 가져올 수 있는 resolve를 선언했습니다.
class DependencyInjecter: DependencyInjectable {
var objectMap: [ObjectIdentifier: AnyObject] = [:] //[ObjectIdentifier: AnyObject]로 담을 수있는 전역변수
func register<T>(_ type: T.Type, closure: (DependencyInjectable) -> (T)) {
let key = ObjectIdentifier(type)
guard objectMap[key] == nil else {
return
}
objectMap[key] = closure(self) as AnyObject
}
/.../
func printKeys() {
print("=================")
print(objectMap.keys)
print("=================")
}
}
1)
register를 통해서 들어오는 타입( 메타 타입, 클래스)을
ObjectIdentifier 통해서 key값으로 만들기!
2)
closure를 통해서 타입에 대한 구체적인 객체를 value 와서 키에 매칭하여 넣어주기
let dependencyInjecter = DependencyInjecter()
dependencyInjecter.register(Animal.self) { _ in
Tiger(name: "tiger")
}
dependencyInjecter.register(Animal.self) { _ in
Tiger(name: "WhiteTiger")
}
dependencyInjecter.printKeys()
/*prints
=================
[ObjectIdentifier(0x00007fff80c631a0)]
=================
*/
Animal 프로토콜을 따르는 Tiger라는 객체를 두번 등록했지만 결과적으로는 처음 등록 했던 일반 타이거만 등록되게 됩니다.
같은 타입을 여러 개 등록하는 방법으로 확장하는 것은 2탄 기능확장편에서 하도록 하겠습니다!
// DependencyInjecter
func resolve<T>(_ anyClassType: T.Type) -> T? {
let key = ObjectIdentifier(anyClassType)
return objectMap[key] as? T
}
실제로 등록된 객체를 가져오는 resolve 입니다!
간단히 써보기
protocol MovieRepository {
func getMovieName() -> String
}
final class MovieRepositoryImpl: MovieRepository {
func getMovieName() -> String {
return "Spider Man"
}
}
protocol Interactorable {}
protocol MovieInteractorable: Interactorable {
func getMovieTitle() -> String
}
final class MovieInteractor: MovieInteractorable {
let movieRepository: MovieRepository
init(movieRepository: MovieRepository) {
self.movieRepository = movieRepository
}
func getMovieTitle() -> String {
return movieRepository.getMovieName()
}
}
let dependencyInjecter = DependencyInjecter()
dependencyInjecter.register(MovieRepository.self) { _ in
MovieRepositoryImpl()
}
dependencyInjecter.register(MovieInteractorable.self) { di in
MovieInteractor(movieRepository: di.resolve(MovieRepository.self)!)
}
let movieObjc = dependencyInjecter.resolve(MovieInteractorable.self)
if let title = movieObjc?.getMovieTitle() {
print(title)
}
//prints "Spider Man"
2탄에서는
DependencyInjecter의 기능과 예외처리를 추가하고 데모 프로젝트에 적용하는 시간을 갖도록 하겠습니다.
참고자료