한 줄 요약: 메모리는 한정적입니다. DiContainer 2.0의 지능적 메모리
관리로 안전하고 효율적인 DI를 구현하세요.
안녕하세요! iOS 개발자 Roy입니다. 오늘은 DiContainer 2.0의 메모리 관리
전략을 깊이 있게 다루되, 이해하기 쉽게 풀어보겠습니다.\
DI(의존성 주입)를 쓰다 보면 메모리 누수, 순환 의존성 같은 문제를
마주하게 되는데, 이를 어떻게 우아하게 해결할 수 있을까요?
실제 프로덕션 앱에서 발생했던 사례입니다:
// ❌ 문제가 있는 코드 – 메모리 누수 발생
class UserService {
private let repository: UserRepository
private let cacheManager: CacheManager
init(repository: UserRepository, cacheManager: CacheManager) {
self.repository = repository
self.cacheManager = cacheManager
// 강한 참조로 인한 순환 참조
cacheManager.delegate = self
repository.observer = self
}
}
측정 결과: 위 패턴이 50개 서비스에 적용되자, 실행 30분 후 메모리가\
200MB → 350MB로 증가했습니다.
DiContainer 2.0은 5가지 스코프를 제공합니다. 올바른 스코프 선택은
메모리 안정성의 핵심입니다.
await DependencyContainer.bootstrap { container in
container.register(DatabaseManager.self, scope: .singleton) {
DatabaseManager()
}
}
class DatabaseService {
@Inject var database: DatabaseManager // 항상 같은 인스턴스
}
container.register(CacheManager.self, scope: .weak) { CacheManager() }
class DataService {
@Inject var cache: CacheManager?
}
container.register(Logger.self, scope: .unowned) { Logger.shared }
class APIService {
@Inject var logger: Logger
}
await container.beginScope("UserSession") { scopedContainer in
scopedContainer.register(UserSession.self) { UserSession(userId: currentUserId) }
}
// 로그아웃 시 자동 정리
await container.endScope("UserSession")
container.register(APIRequest.self, scope: .transient) { APIRequest() }
public actor CyclicDependencyDetector {
private var currentResolutionPath: [ObjectIdentifier] = []
func checkForCycle<T>(_ type: T.Type) async throws {
let typeId = ObjectIdentifier(type)
if currentResolutionPath.contains(typeId) {
throw DIError.circularDependency("순환 참조 발견")
}
currentResolutionPath.append(typeId)
defer { currentResolutionPath.removeLast() }
}
}
protocol UserServiceDelegate: AnyObject { func userDidUpdate(_ user: User) }
class UserService {
weak var delegate: UserServiceDelegate?
@Inject private var repository: UserRepository
}
await container.purgeTransientInstances()
await container.cleanupWeakReferences()
await container.evictLeastRecentlyUsed(maxCount: 10)
| 시나리오 | 기존 DI | DiContainer 2.0 | 개선율 |
|---|---|---|---|
| 앱 시작 직후 | 45MB | 42MB | 6.7% ↓ |
| 30분 사용 후 | 180MB | 95MB | 47.2% ↓ |
| 메모리 워닝 후 | 200MB+ | 80MB | 60% ↓ |
| 백그라운드 복귀 | 150MB | 75MB | 50% ↓ |
weak, unowned 적극 활용)#if DEBUG
print(DependencyContainer.live.generateMemoryReport())
#endif
메모리는 한정된 자원입니다.\
DiContainer 2.0으로 더 안전하고 효율적인 앱을 만들어 보세요 🚀
다음 글 예고: SwiftUI + DiContainer 완전 정복 -- EnvironmentObject
vs @Inject
궁금한 점은 GitHub Issues에 남겨주세요!