다음 글 : 2. Object Registration - Deeping
getIt은 아래 기준에 따라 상이한 등록 방식 제공
- 언제 생성될 지
- 얼마나 오래 객체가 지속할 지
Quick Reference
| Type | When Created | How Many Instances | Lifetime | Best For |
|---|---|---|---|---|
| Singleton | Immediately | One | Permanent | Fast to create, needed at startup |
| LazySingleton | First access | One | Permanent | Expensive to create, not always needed |
| Factory | Every get() | Many | Per request | Temporary objects, new state each time |
| Cached Factory | First access + after GC | Reused while in memory | Until garbage collected | Performance optimization |
1. SingleTon
추천 케이스
- 생성 비용이 매우 낮아 앱의 초기 렌더링을 지연시키지 않는 객체
- 앱 구동 시 즉각적으로 필요한 전역 상태 혹은 설정 정보를 다루는 객체
T registerSingleton<T extends Object>(
T instance, {
String? instanceName,
bool? signalsReady,
DisposingFunc<T>? dispose,
});- 인스턴스의 경우, 등록 즉시 생성.
get<T>()을 통해T를 제공하면 해당 값을 받을 수 있음.
Parameters 필수
instance: 등록할 인스턴스 선택instanceName: 같은 타입의 인스턴스를 여러 개 등록할 경우, 이름을 다르게 하여 구분할 때 사용.signalReady:true설정 시 해당 인스턴스가 준비가 되면 항상 신호를 제공- (
async를 이용한 초기화 시 주로 사용)
- (
disponse: 등록 해제 혹은 재설정 시 사용할cleanUp함수
사용 예시
void configureDependencies() {
// Simple registration
getIt.registerSingleton<Logger>(Logger());
// With disposal
getIt.registerSingleton<Database>(
Database(),
dispose: (db) => db.close(),
);
}2. LazySingleton
적합한 사용 예시
- 생성 비용이 비싼 객체 :
database,Http Client등 - 모든 User에게 항상 필요한 객체가 아닌 경우.
- 지연 초기화를 하고 싶은 경우
void registerLazySingleton<T>(
FactoryFunc<T> factoryFunc, {
String? instanceName,
DisposingFunc<T>? dispose,
void Function(T instance)? onCreated,
bool useWeakReference = false,
}) {}registerLazySingleton을 사용할 때,T라는 원하는 인스턴스 타입을 반환하는 팩토리 함수 제공.- 해당 함수는
get<T>()에 최초 접근 시에 호출- 이후에는 항상 동일한 인스턴스 제공
Parameters
factoryFunc : 인스턴스를 생성하는 함수
선택
instanceName , dispose ⇒ SingleTon 에서와 동일
onCreated : 인스턴스가 생성된 이후의 콜백 함수
useWeakReference : true 설정 시 사용하지 않으면 GC 의 대상이 되어 정리된다.
**예시
void configureDependencies() {
// Simple registration
getIt.registerLazySingleton<ApiClient>(() => ApiClient());
// With disposal and onCreated callback
getIt.registerLazySingleton<Database>(
() => Database(),
dispose: (db) => db.close(),
onCreated: (db) => print('Database initialized'),
);
}
// First access - factory function runs NOW
final api = getIt<ApiClient>(); // ApiClient() constructor called
// Subsequent calls - returns existing instance
final sameApi = getIt<ApiClient>(); // Same instance, no constructor call3. Factory
적합한 사용 예시
- 일시적으로 사용하는 객체 →
dialog,forms, 임시적인 데이터 보관 역할 등 - 매번 초기값이어야 하는 객체
- 짧은 생명 주기를 가진 객체
생성 비용이 비싼 경우 ⇒ cached factory 의 사용을 검토
void registerFactory<T>(
FactoryFunc<T> factoryFunc, {
String? instanceName,
}) {}- 등록 시, 매번 새로운
T라는 인스턴스를 생성하는 메서드를 제공하여야 함.SingleTon에서와 달리 매번 호출 마다 새로운 객체를 획득함.
Parameters
→ SingleTon 에서와 동일
**예시
void configureDependencies() {
getIt.registerFactory<ShoppingCart>(() => ShoppingCart());
}4. Cached Factories
- 일반 팩토리와 싱글톤 사이의
성능 최적화방식- 첫 번째 호출 시 : 새로운 인스턴스 생성
- → 이를
weak reference로 캐싱
- → 이를
- 이후 해당 인스턴스가 메모리에 남아 있는 경우, 캐싱된 인스턴스를 사용
- 첫 번째 호출 시 : 새로운 인스턴스 생성
//type-a
void registerCachedFactory<T>(
FactoryFunc<T> factoryFunc, {
String? instanceName,
});
//type-b
void registerCachedFactoryParam<T, P1, P2>(
FactoryFuncParam<T, P1, P2> factoryFunc, {
String? instanceName,
});
//type-c
void registerCachedFactoryAsync<T>(
FactoryFuncAsync<T> factoryFunc, {
String? instanceName,
});
//type-d
void registerCachedFactoryParamAsync<T, P1, P2>(
FactoryFuncParamAsync<T, P1, P2> factoryFunc, {
String? instanceName,
});- Type-a : `registerCachedFactory
- 매개변수 없이 객체 생성
- Type-b :
registerCachedFactoryParam<T, P1, P2>- 두 개의 매개변수(P1, P2) 를 전달받아 T 타입의 인스턴스 생성
- 객채 생성 시점에 동적인 데이터가 필요할 때 사용
- Type-c :
registerCachedFactoryAsync<T>- 비동기적으로
T타입의 인스턴스를 생성. Future<T>를 반환하는 생성 로직에 사용
- 비동기적으로
- Type-d :
registerCachedFactoryParamAsync<T, P1, P2>- 두 개의 매개변수를 전달받아 비동기적으로 인스턴스를 생성.
- 매개변수가 필요한 복잡한 비동기 초기화 로직을 수행 시 사용.
동작
- 최초 호출 :
factory처럼 새로운 인스턴스 생성 - 만약 캐시된 인스턴스가 존재하면 다른 호출에서는 캐시된 인스턴스 반환 →
싱글톤 - 만약
Garbage Collected된다면 새로운 객체 생성
사용 예제
// Register a cached factory
getIt.registerCachedFactory<HeavyParser>(() => HeavyParser());
// First call - creates instance
final parser1 = getIt<HeavyParser>();
print('parser1: $parser1'); // New instance created
// Second call - reuses if not garbage collected
final parser2 = getIt<HeavyParser>();
print('parser2: $parser2'); // Same instance (if still in memory)
// After garbage collection (no references held)
final parser3 = getIt<HeavyParser>();
print('parser3: $parser3'); // New instance createdWith parameters:
getIt.registerCachedFactoryParam<ImageProcessor, int, int>(
(width, height) => ImageProcessor(width, height),
);
// Creates new instance
final processor1 = getIt<ImageProcessor>(param1: 1920, param2: 1080);
print('processor1: $processor1');
// Reuses same instance (same parameters)
final processor2 = getIt<ImageProcessor>(param1: 1920, param2: 1080);
print('processor2: $processor2');
// Creates NEW instance (different parameters)
final processor3 = getIt<ImageProcessor>(param1: 3840, param2: 2160);
print('processor3: $processor3');✅ 좋은 사용 사례:
- 빈번하게 재생성되는 무거운 객체: 파서(Parser), 포맷터(Formatter), 계산기(Calculator)
- 메모리에 민감한 시나리오: 자동 정리를 원하면서도 가급적 재사용을 선호하는 경우
- 초기화 비용이 비싼 객체: 데이터베이스 연결, 파일 리더
- 수명이 짧거나 중간 정도인 객체: 일정 기간 동안만 활성 상태로 유지되고 영구적이지 않은 경우
❌ 다음의 경우 사용 금지:
- 객체가 항상 새로 생성되어야 할 때: 일반 팩토리 사용
- 객체가 영구적으로 유지되어야 할 때: 싱글톤 또는 레이지 싱글톤 사용
- 재사용해서는 안 되는 중요한 상태를 객체가 보유하고 있을 때
Performance characteristics:
| Type | Creation Cost | Memory | Reuse |
|---|---|---|---|
| Factory | Every call | Low (immediate GC) | Never |
| Cached Factory | First call + after GC | Medium (weak ref) | While in memory |
| Lazy Singleton | First call only | High (permanent) | Always |
Comparison example:
// Factory - always new, immediate cleanup
getIt.registerFactory<JsonParser>(() => JsonParser());
final p1 = getIt<JsonParser>();
print('p1: $p1'); // Creates instance 1
final p2 = getIt<JsonParser>();
print('p2: $p2'); // Creates instance 2 (different)
// Cached Factory - reuses if possible
getIt.registerCachedFactory<JsonParser>(() => JsonParser());
final p3 = getIt<JsonParser>();
print('p3: $p3'); // Creates instance 3
final p4 = getIt<JsonParser>();
print('p4: $p4'); // Returns instance 3 (if not GC'd)
// Lazy Singleton - reuses forever
getIt.registerLazySingleton<JsonParser>(() => JsonParser());
final p5 = getIt<JsonParser>();
print('p5: $p5'); // Creates instance 4
final p6 = getIt<JsonParser>();
print('p6: $p6'); // Returns instance 4 (always)Note
어떤 타입으로
GetIt에 인스턴스를 등록해야할까?구체적인 타입, 인터페이스(추상 인터페이스) 모두 가능.
여러 구현체를 사용하는 경우 → 인터페이스 그 외의 경우에는 구체적인 클래스를 직접 등록하는 것이 좋다. → 코드의 단순성, IDE 탐색의 용이성 증가