왜 어떤거는 di.registerFactory 이고 어떤거는 di.registerLazySingleton 인가요?
이번에 firebase 에 데이터를 저장하는 작업을 했다. 의존성 주입(DI)로 get_it 을 쓰고있던 중에, 팀원분이 요런 질문을 남겨주셔서.. 답변 정리한 김에 블로그에도 올려본다.
저 질문이 나오게 된 부분..
GetIt di = GetIt.instance;
void initLocator() {
// FireStore
di.registerLazySingleton<FirestoreService>(() => FirestoreService(FirebaseFirestore.instance));
di.registerLazySingleton<FirestoreRepository>(() => FirestoreRepositoryImpl(di<FirestoreService>()));
/// Blocs
di.registerFactory(() => HomeBloc(di<GooglePlaceRepository>()));
di.registerFactory(() => SearchBloc(di<SearchBakeryUseCase>()));
}
아마 firebase 에 접근하는 service 클래스는 registerLazySingleton 로 선언하고, bloc 은 registerFactory 를 선언해서 그 둘의 차이점을 궁금해하셨던것같다.
결론부터 말하면 초기화 시점 문제 + Bloc 생명주기 때문에 블록은 registerFactory 를 해줬다.
ex) service 같은 경우.. DB에 접근할 필요가 없을 땐 인스턴스를 만들지 않고 있다가, 실제로 데이터를 저장해야 할 때 인스턴스를 생성하기에 lazySingleton 을 사용하면 메모리가 절약됩니다.
왜냐면 다음과 같은 2가지 이유가 있습니다.
블럭 초기화 요청 -> 블럭이 초기화 되기 전에 Provider 가 먼저 접근 -> 해당 bloc 을 위젯트리에 등록하지 못함
쉽게 말해 블럭 인스턴스를 만들고 있는 시점에, Provider 가 사용하려고 하니까 에러가 난다는거지요 (저도 이렇게 썼다가 context 를 읽어올 수 없다는 오류가 났었어요 주르륵..)
그리고 Bloc 에도 생명주기가 있다는 사실 혹시 아십니까!
생명주기가 있지만 우리가 크게 신경쓰지 않아도 잘 굴러갔죠. 왜냐면 위젯의 생명주기와 동일하도록 BlocProvider 가 알아서 관리해주고 있었으니까요.. (create, close)

바로 이부분.. 이렇게 create 를 BlocProvider 가 관리하면 close 도 관리해줘요.
child 에 등록한 위젯이 dispose되면, BlocProvider 가 해당 bloc 을 close 를 해줍니다.
그!런!데!!!
lazySingleton 은 결국 인스턴스 생성 시점만 늦출 뿐 싱글톤이니까, 인스턴스를 최초 1번만 생성해서 계속 같은 걸 쓰잖아요? create 가 몇번 호출되든, 이미 생성된 걸 돌려씁니다.
"인스턴스가 유지된다 = 위젯이 dispose 되어도 bloc 의 참조가 해제되지 않는다"는 의미입니다. 따라서 수동으로 close() 를 호출하여 해제 해줘야 합니다. 이걸 까먹으면 메모리 누수 가능성이 생깁니다.
매번 수동으로 관리해주면 매우 귀찮아지겠죠? 그래서 bloc 주입 관련해서는 registerFactory 를 썼습니다. 얘는 싱글톤이 아니예요. 쭉 유지되지 않습니다.
만약 get_it 으로 bloc 을 주입해야한다면 registerFactory 을 쓰자.