每次我们存一个对象的时候都会使用Get.put()。要用的时候都是Get.find()。
那么Getx是如何将我们需要的对象保存起来?而且还可以跨页面共享数据的呢?
接下来,带着疑问去源码寻找我们需要的答案。
首先我们来看一看我们put的时候的代码
S put<S>(S dependency,
{String? tag,
bool permanent = false,
InstanceBuilderCallback<S>? builder}) =>
GetInstance().put<S>(dependency, tag: tag, permanent: permanent);
extension Inst on GetInterface {
这块代码是 在一个叫Inst的 扩展类里。他是对GetInterface的拓展。
接着我们看看GetInstance类的put方法
S put<S>(
S dependency, {
String? tag,
bool permanent = false,
@deprecated InstanceBuilderCallback<S>? builder,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder ?? (() => dependency));
return find<S>(tag: tag);
}
看到上面isSingleton这个参数是true,说明这个put方法永远是单例的对象。如果不想单例的话
可以使用Get.create()这个方法,每次find时候都会新建一个实例对象,从代码可以看出isSingleton是false。
void create<S>(
InstanceBuilderCallback<S> builder, {
String? tag,
bool permanent = true,
}) {
_insert(
isSingleton: false,
name: tag,
builder: builder,
permanent: permanent,
);
}
接着可以看到又调用了insert方法
void _insert<S>({
bool? isSingleton,
String? name,
bool permanent = false,
required InstanceBuilderCallback<S> builder,
bool fenix = false,
}) {
final key = _getKey(S, name);
if (_singl.containsKey(key)) {
final dep = _singl[key];
if (dep != null && dep.isDirty) {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
lateRemove: dep as _InstanceBuilderFactory<S>,
);
}
} else {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
);
}
}
我们一句一句的看
首先通过name(就是我们传入的tag)和 S (我们注入的数据类型)去获取key。一般情况下是没有设置tag的。所以,我们使用put的时候,然后find拿到的都是同一对象。如果设置了tag的那么就会生成相同类型但是实例对象不同。从上面的源码看就可以清楚了。
String _getKey(Type type, String? name) {
return name == null ? type.toString() : type.toString() + name;
}
name为空的时候直接使用S的数据类型作为key,不为空的时候直接两者的拼接。
第2行代码是if判断。判断key是否包含在_singl里。_singl是一个以String为key,_InstanceBuilderFactory为value的Map对象。
static final Map<String, _InstanceBuilderFactory> _singl = {};
那什么情况是脏对象呢?看看下面的源码就可以知道当widget被dispose的时候,这个put的对象就会被标记为脏,然后回调删除的方法,将标记为脏的对象进行回收。
static void reportRouteWillDispose(Route disposed) {
final keysToRemove = <String>[];
_routesKey[disposed]?.forEach(keysToRemove.add);
/// Removes `Get.create()` instances registered in `routeName`.
if (_routesByCreate.containsKey(disposed)) {
for (final onClose in _routesByCreate[disposed]!) {
// assure the [DisposableInterface] instance holding a reference
// to onClose() wasn't disposed.
onClose();
}
_routesByCreate[disposed]!.clear();
_routesByCreate.remove(disposed);
}
for (final element in keysToRemove) {
GetInstance().markAsDirty(key: element);
//_routesKey.remove(element);
}
keysToRemove.clear();
}
void markAsDirty<S>({String? tag, String? key}) {
final newKey = key ?? _getKey(S, tag);
if (_singl.containsKey(newKey)) {
final dep = _singl[newKey];
if (dep != null && !dep.permanent) {
dep.isDirty = true;
}
}
}
好了,insert方法这部分看完了,这部分主要是将数据存储起来。后面发现他接着调用了find方法。
一般来讲我们都会直接将插入的对象进行返回,但是呢Getx却要再费力的find方法。这可能让人有点奇怪。接下去看就知道了。
S find<S>({String? tag}) {
final key = _getKey(S, tag);
if (isRegistered<S>(tag: tag)) {
final dep = _singl[key];
if (dep == null) {
if (tag == null) {
throw 'Class "$S" is not registered';
} else {
throw 'Class "$S" with tag "$tag" is not registered';
}
}
final i = _initDependencies<S>(name: tag);
return i ?? dep.getDependency() as S;
} else {
// ignore: lines_longer_than_80_chars
throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"';
}
}
还是通过tag和S来生成key。接着又是一个if判断,如果要查找的对象已经注册了。
就可以通过key拿到注册的对象。我们直接看不为空的情况。调用了_initDependencies方法
我们看看源码
S? _initDependencies<S>({String? name}) {
final key = _getKey(S, name);
final isInit = _singl[key]!.isInit;
S? i;
if (!isInit) {
i = _startController<S>(tag: name);
if (_singl[key]!.isSingleton!) {
_singl[key]!.isInit = true;
if (Get.smartManagement != SmartManagement.onlyBuilder) {
RouterReportManager.reportDependencyLinkedToRoute(_getKey(S, name));
}
}
}
return i;
}
判断实例对象是不是首次初始化,如果不是直接返回对象。
是的话就开始controller对象初始化操作。
/// 初始化 controller
S _startController<S>({String? tag}) {
final key = _getKey(S, tag);
final i = _singl[key]!.getDependency() as S;
if (i is GetLifeCycleBase) {
i.onStart();
if (tag == null) {
Get.log('Instance "$S" has been initialized');
} else {
Get.log('Instance "$S" with tag "$tag" has been initialized');
}
if (!_singl[key]!.isSingleton!) {
RouterReportManager.appendRouteByCreate(i);
}
}
return i;
}
我们看这一句代码 final i = _singl[key]!.getDependency() as S;
这个实例对象调用自身的一个方法getDependency。我们看看他是做什么的?
S getDependency() {
if (isSingleton!) {
if (dependency == null) {
_showInitLog();
dependency = builderFunc();
}
return dependency!;
} else {
return builderFunc();
}
}
首选判断他是不是单例的,如果是直接新建一个对象,赋值给dependency 存储起来,下次find的时候就可以直接返回对象了。如果不是,每次就会新建一个对象。
接着下一句代码 if (i is GetLifeCycleBase)
这个if判断就是controller绑定widget生命周期的关键。判断put的对象类型是不是GetLifeCycleBase的子类。我写controller的时候是不是要继承一个GetxController。我们看看下面的类继承关系就知道了
1. abstract class GetxController extends DisposableInterface
with ListenableMixin, ListNotifierMixin {}
2. abstract class DisposableInterface extends GetLifeCycle {}
3. abstract class GetLifeCycle with GetLifeCycleBase {
GetLifeCycle() {
$configureLifeCycle();
}
}
从上面的继承关系我们就可以很清楚的了解到,我们建的controller类只要是继承GetxController的都是GetLifeCycleBase 的子类。
接着调用onStart()方法,这个主要是初始化controller的相应生命周期的
onInit,onReady,onClose。
if (!_singl[key]!.isSingleton!) {
RouterReportManager.appendRouteByCreate(i);
}
这句的意思是如果不是单例对象,就是用Get.create()创建的实例的才会调用这个方法。
static void appendRouteByCreate(GetLifeCycleBase i) {
_routesByCreate[_current] ??= HashSet<Function>();
// _routesByCreate[Get.reference]!.add(i.onDelete as Function);
_routesByCreate[_current]!.add(i.onDelete);
}
这个方法作用就是将资源回收与路由关联起来,等到widget被disposed就会回调controller的onClose方法。
接着看_startController之后的代码
_singl[key]!.isInit = true;
if (Get.smartManagement != SmartManagement.onlyBuilder) {
RouterReportManager.reportDependencyLinkedToRoute(_getKey(S, name));
}
将isInit 设置为true,然后下次就不会再执行这段代码了,只初始化一次。
调用RouterReportManager类的reportDependencyLinkedToRoute方法。
这段代码主要是用来将controller类关联路由的。
SmartManagement.full 这是默认的。销毁那些没有被使用的、没有被设置为永久的类。在大多数情况下,你会希望保持这个配置不受影响。如果你是第一次使用GetX,那么不要改变这个配置。
SmartManagement.onlyBuilders 使用该选项,只有在init:中启动的控制器或用Get.lazyPut()加载到Binding中的控制器才会被销毁。
如果你使用Get.put()或Get.putAsync()或任何其他方法,SmartManagement将没有权限移除这个依赖。
在默认行为下,即使是用 "Get.put
"实例化的widget也会被移除,这与SmartManagement.onlyBuilders不同。
SmartManagement.keepFactory 就像SmartManagement.full一样,当它不再被使用时,它将删除它的依赖关系,但它将保留它们的工厂,这意味着如果你再次需要该实例,它将重新创建该依赖关系。
Get.put与Get.lazyPut的对比
S put<S>(
S dependency, {
String? tag,
bool permanent = false,
@deprecated InstanceBuilderCallback<S>? builder,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder ?? (() => dependency));
return find<S>(tag: tag);
}
void lazyPut<S>(
InstanceBuilderCallback<S> builder, {
String? tag,
bool? fenix,
bool permanent = false,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder,
fenix: fenix ?? Get.smartManagement == SmartManagement.keepFactory,
);
}
通过对比发现,put是直接传入一个实例对象,而lazyPut是一个builder对象
put是直接find返回一个对象,而lazyPut没有,只有当你需要的时候也就是你使用了Get.find才会新建一个实例对象,所以懒加载也就是这个意思。
void create<S>(
InstanceBuilderCallback<S> builder, {
String? tag,
bool permanent = true,
}) {
_insert(
isSingleton: false,
name: tag,
builder: builder,
permanent: permanent,
);
}
我们看到了permanent设置了true。表示该对象持久存在。
我们看看如下源码,可以看到在回收对象的方法上使用到了,接着往下看。
GetInstance类的delete方法
bool delete<S>({String? tag, String? key, bool force = false}) {
final newKey = key ?? _getKey(S, tag);
if (!_singl.containsKey(newKey)) {
Get.log('Instance "$newKey" already removed.', isError: true);
return false;
}
final dep = _singl[newKey];
if (dep == null) return false;
final _InstanceBuilderFactory builder;
if (dep.isDirty) {
builder = dep.lateRemove ?? dep;
} else {
builder = dep;
}
if (builder.permanent && !force) {
Get.log(
// ignore: lines_longer_than_80_chars
'"$newKey" has been marked as permanent, SmartManagement is not authorized to delete it.',
isError: true,
);
return false;
}
final i = builder.dependency;
if (i is GetxServiceMixin && !force) {
return false;
}
if (i is GetLifeCycleBase) {
i.onDelete();
Get.log('"$newKey" onDelete() called');
}
if (builder.fenix) {
builder.dependency = null;
builder.isInit = false;
return true;
} else {
if (dep.lateRemove != null) {
dep.lateRemove = null;
Get.log('"$newKey" deleted from memory');
return false;
} else {
_singl.remove(newKey);
if (_singl.containsKey(newKey)) {
Get.log('Error removing object "$newKey"', isError: true);
} else {
Get.log('"$newKey" deleted from memory');
}
return true;
}
}
}
我们拿出一段代码看一下,可以看到permanet为true时,整个就返回false了,就不执行后面的对象删除操作了,是不是很清楚了。
if (builder.permanent && !force) {
Get.log(
// ignore: lines_longer_than_80_chars
'"$newKey" has been marked as permanent, SmartManagement is not authorized to delete it.',
isError: true,
);
return false;
}
Future<S> putAsync<S>(
AsyncInstanceBuilderCallback<S> builder, {
String? tag,
bool permanent = false,
}) async {
return put<S>(await builder(), tag: tag, permanent: permanent);
}
可以看到除了builder设置为异步之外,跟put没什么不同。
从源码来看的话,Getx的本质就是使用Map来保持一种依赖关系。通过使用find就能够找到相应的对象。
最后想说的就是,熟悉源码能帮助我们更好的使用框架。如果对你有用的话,请不要吝啬给个赞吧!