开始使用
当前最新版本为: 1.1.7
在 “pubspec.yaml” 文件中加入
dependencies:
quicklibs: ^1.1.7
github
https://github.com/CimZzz/quicklibs
Usage
迭代器: 用提供闭包来实现循环每一步控制逻辑
Time: 提供一系列关于时间的操作,如时间格式化,字符串转时间等方法
转换方法: 提供一系列关于转换具体类型的操作
Scope: 提供在限定的作用域内,有状态无状态消息之间的交互。
迭代器
闭包命名
typedef EachBeginCallback<T> = T Function();
typedef EachCallback<T> = Function(T elem);
typedef EachJudgeCallback<T> = bool Function(T obj);
typedef EachChangeCallback<T> = T Function(T obj);
typedef EachOverCallback = dynamic Function(dynamic obj);
typedef EachOverAsCallback<T> = T Function(dynamic obj);
- EachBeginCallback 为一次循环回调闭包,只在循环开始时执行一次(可选)
- EachCallback 为循环体回调闭包,每次循环均会执行(可选)
- EachJudgeCallback 为判断循环终止回调闭包,当返回 false 时循环终止
- EachChangeCallback 为末尾循环体回调闭包,每次循环体最后执行
- EachOverCallback 处理循环结果闭包回调,一般在循环执行完成后调用
- EachOverAsCallback 处理循环结果闭包回调,一般在循环执行完成后调用,在处理的同时会将结果进行转型
以 for 循环为例(仅仅为参考样例)
for(var i = 0 ; i < 10 ; i ++) {
};
将回调带入后
for(EachBeginCallback; EachJudgeCallback; EachChangeCallback) {
final result = EachCallback;
if(result != null)
return EachOverCallback(result);
}
迭代构造器 EachBuilder<T>
提供一系列构造迭代器的方法
EachBuilder begin(EachBeginCallback<T> beginCallback); //构建一次循环回调闭包
EachBuilder change(EachChangeCallback<T> changeCallback); //构建末尾循环体回调闭包
EachBuilder judge(EachJudgeCallback<T> judgeCallback); //构建判断循环终止回调闭包
EachBuilder call(EachCallback<T> callback); //构建循环体回调闭包
EachBuilder configAll({
EachBeginCallback<T> beginCallback,
EachChangeCallback<T> changeCallback,
EachJudgeCallback<T> judgeCallback,
EachCallback<T> callback
}); // 一次性配置全部回调(空闭包会被忽略)
void loopOnly(); // 只执行循环不考虑结果
EachResult loop(); // 执行循环返回结果 EachResult
E loopForResult<E>(); // 执行循环直接获取最终结果
List<E> loopForList<E>(); // 执行循环直接获取最终指定类型列表
loopOnly 例子如下:
loop1() {
final builder = EachBuilder<int>();
builder.begin(() => 0);
builder.judge((position) => position < 5)
.change((position) => position + 1)
.call((position) => print(position))
.loopOnly();
}
执行结果为:
0
1
2
3
4
如果循环需要返回值,可以通过 loop 函数来获取循环结果,
至于返回值,可以通过 EachCallback 回调闭包返回,这里分为两种情况:
- 返回类型为 EachResult 类型,强制中断循环返回 EachResult
- 返回类型为 非EachResult 类型,将值存入临时的 List 中(不保证下标位置关系),
在循环结束后返回一个包装 List 对象的 EachResult 对象
loop 例子如下:
loop2() {
final list = EachBuilder<int>()
.begin(() => 0)
.judge((position) => position < 5)
.change((position) => position + 1)
.call((position) => position)
.loop()
.end();
print(list);
}
执行结果为:
[0, 1, 2, 3, 4]
或者使用 loopForResult 来实现同样的功能
loop3() {
final list = EachBuilder<int>()
.begin(() => 0)
.judge((position) => position < 5)
.change((position) => position + 1)
.call((position) => position)
.loopForResult();
print(list);
}
同样,loopForList 也能实现同样功能,并且会返回一个明确类型的列表而不是 dynamic 类型的列表
loop12() {
final list = EachBuilder<int>()
.begin(() => 0)
.judge((position) => position < 5)
.change((position) => position + 1)
.call((position) => position)
.loopForList<int>();
print(list);
}
迭代结果 EachResult
如果使用迭代构造器的 loop 方法,则会在执行结束后返回 EachResult 作为循环结果
我们可以操作这个 EachResult 对象来处理返回结果
提供一些处理迭代结果的方法:
EachResult then(EachOverCallback overCallback); // 追加处理结果回调
T as<T>(EachOverAsCallback<T> overCallback); // 执行一次处理结果回调后返回有具体类型的结果
dynamic end(); // 返回结果
注意: 当 EachResult 返回结果后,无法通过任何方式追加处理结果回调
下面这个例子,通过循环产生一个整数数组,然后通过结果处理返回数组之和:
loop4() {
final value = EachBuilder<int>()
.begin(() => 0)
.judge((position) => position < 5)
.change((position) => position + 1)
.call((position) => position)
.loop() // 返回 EachResult
.then((list) {
var sum = 0;
list.forEach((num) {
sum += num;
});
return sum;
})
.end();
print(value);
}
执行结果如下:
10
这个处理结果是单链表结构,意味着可以链接多个处理回调。当收到返回结果为 EachResult 类型时,则不在执行下去忽略其余处理回调,如:
loop5() {
final value = EachBuilder<int>()
.begin(() => 0)
.judge((position) => position < 5)
.change((position) => position + 1)
.call((position) => position)
.loop() // 返回 EachResult
.then((list) {
var sum = 0;
list.forEach((num) {
sum += num;
});
return sum;
})
.then((sum){
return sum * 10;
})
.then((sum) {
return sum + 50;
})
.then((sum){
return EachResult(sum - 1);
})
.then((sum) {
return sum * 10000;
})
.end();
print(value);
}
执行结果为:
149
如果可以明确最后一个处理回调,建议使用 as 方法
需要注意的是此方法不能返回 EachResult 类型对象
loop6() {
final value = EachBuilder<int>()
.begin(() => 0)
.judge((position) => position < 5)
.change((position) => position + 1)
.call((position) => position)
.loop() // 返回 EachResult
.then((list) {
var sum = 0;
list.forEach((num) {
sum += num;
});
return sum;
})
.then((sum){
return sum * 10;
})
.then((sum) {
return sum + 50;
})
.as((sum){
return sum - 1;
});
print("value type: ${value.runtimeType}, value: $value");
}
执行结果为:
value type: int, value: 149
简化整数迭代器
常规循环通过构造器方式直接创建太过繁琐复杂,因为我们并不需要那么强的兼容性,
我们这里封装了一种常用的整数迭代器,具体还是使用迭代构造器来实现的,绝大部分迭代逻辑无需自己实现
/// 快捷生成整数循环迭代器的方法,返回最终结果
/// 通过 [intEachBuilder] 生成整数循环构造器,通过返回的 EachBuilder<int> 获得返回值
dynamic intEach({
int start = 0,
int end = 0,
int total = 0,
EachCallback<int> callback,
EachChangeCallback<int> changeCallback
});
/// 快捷生成整数循环迭代器的方法,返回最终 List 结果
/// 通过 [intEachBuilder] 生成整数循环构造器,通过返回的 EachBuilder<int> 获得返回 List 值
List<E> intEachList<E>({
int start = 0,
int end = 0,
int total = 0,
EachCallback<int> callback,
EachChangeCallback<int> changeCallback
});
/// 快捷生成整数循环迭代器的方法,返回 EachBuilder<int>
EachBuilder<int> intEachBuilder({
int start = 0,
int end = 0,
int total = 0,
EachCallback<int> callback,
EachChangeCallback<int> changeCallback
});
- EachCallback 循环回调
- start 起始下标(可选)
- end 终止下标(可选)
- total 表示循环总数(可选)
- EachChangeCallback 循环执行末尾回调,用于提供自增/自减方法变化下标
以下实例均以 “intEach” 方法为例
举个例子,一个最简单的循环
loop7() {
var i = 0;
intEach(
callback: (position) {
//do something
i += position;
}, total: 100);
print(i);
}
上述程序结果为 4950,等同于 ∑99。
同样也可以写作
loop8() {
var i = 0;
intEach(
callback: (position) {
//do something
i += position;
}, start: 0, end: 100);
print(i);
}
结果同样为 4950
上述两个例子展示了执行整数迭代循环的两种方式,下面总结一下全部的方式
- 提供 total 参数与 start 参数,表示从 start 对应的下标开始,循环 total 次
- 提供 start 参数与 end 参数,表示从 start 对应下标开始,到 end 下标终止(遵循 “左闭右开” 原则)
changeCallback
整数迭代器会跟传递的参数自动识别迭代的方向(正/负),提供默认的 自增/自减 闭包。
当然,如果普通的自增自减无法满足需求(比如以指数增长),可以通过 changeCallback 来决定增长趋势
loop9() {
intEach(
callback: (position) {
//do something
print("curPosition: $position");
}, total: 100, changeCallback: (position) => position == 0 ? 1 : position * 3);
}
上述程序执行结果如下:
curPosition: 0
curPosition: 1
curPosition: 3
curPosition: 9
curPosition: 27
curPosition: 81
注意: changeCallback 增长方向要与实际方向一致,否则循环不会执行
简化列表迭代器
有些时候原生的 list-for-each 方式并不能满足我们的需求,
我们提供了简化的列表迭代器
/// 快捷生成列表循环迭代器的方法,返回最终结果
/// 通过 [listEachBuilder] 生成整数循环构造器,通过返回的 EachBuilder<int> 获得返回值
dynamic listEach<T>(List<T> list,{
EachCallback<T> callback
});
/// 快捷生成列表循环迭代器的方法,返回 EachBuilder<int>
EachBuilder<int> listEachBuilder<T>(List<T> list,{
EachCallback<T> callback
});
下面演示一个简单的列表遍历
loop13() {
listEach([1, 2, 3, 4, 5],
callback: (item) {
print(item);
});
}
执行结果如下:
1
2
3
4
5
我们可以利用 EachBuilder 的特性,来完成列表一些特殊操作,如下
loop14() {
var list = ["1", "2", "3", "4", "5"];
var newList = listEachBuilder(
list,
callback: (item) {
return int.parse(item);
}
).loopForList<int>();
print("list type: ${list.runtimeType}, $list");
print("newList type: ${newList.runtimeType}, $newList");
}
执行结果如下:
list type: List<String>, [1, 2, 3, 4, 5]
newList type: List<int>, [1, 2, 3, 4, 5]
注意: 前提是在 callback 回调中返回对应的类型,否则会自动筛选列表,将满足指定类型的元素重新组成一个新的列表
中断循环
对于大部分闭包循环来说,中断循环始终是一个烦人的问题。不过通过这个迭代器,只要在迭代回调中返回由 EachResult 包装的值,
可以很轻易的实现中断闭包循环,而这个值还会当做迭代的最终结果返回
注意: 如果返回值不使用 EachResult 包装,则会记录到一个内部的临时列表中,在循环正常结束后将会返回该列表。如果没有返回值则
不会触发任何逻辑
如下方一个整数迭代器:
loop10() {
var i = 0;
var j =
intEach(
callback: (position) {
if(position > 50)
return EachResult(i);
i += position;
}, total: 100);
print("i: $i, j: $j");
}
执行结果为:
i: 1275, j: 1275
快速通过迭代生成列表
在每次迭代返回一个整数值,当循环结束后可以获得由返回值组成的一个列表
如下方一个整数迭代器:
loop11() {
var list =
intEach(
callback: (position) {
return position * 10;
}, total: 10);
print(list);
}
执行结果为:
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
Time
提供了一系列关于操作时间的方法,通过 Time 类来访问其中方法
闭包命名
typedef TimeFormatCallback = void Function(DateTime, int, StringBuffer);
typedef TimeParseCallback = void Function(_TimeParseBuilder, String);
typedef TimeFormatter = String Function(DateTime);
typedef TimeParser = int Function(String);
缺省的时间占位符
- y: 表示年份
- M: 表示月份
- d: 表示天
- H: 表示小时(全时制)
- m: 表示分钟
- s: 表示秒
时间格式化
将 DateTime 按照指定格式转化成字符串
static String format(
DateTime dateTime,
String formatStr,
{List<TimePlaceholder> otherPlaceholder}
)
- dateTime DateTime 实例
- formatStr 格式化字符串,如 “yyyy-MM-dd HH:mm:ss”
- otherPlaceholder 其他自定义时间占位符(可选)
示例如下
void example1() {
print(Time.format(DateTime.now(), "yyyy-MM-dd HH:mm:ss"));
}
执行结果:
2019-06-04 14:56:14
时间格式化方法对象
将格式化方法包装成闭包对象返回,每次调用只需传递 DateTime 作为参数即可
将格式化中一些对象进行复用,优化了执行效率
static TimeFormatter generateFormatMethod(
String formatStr,
{List<TimePlaceholder> otherPlaceholder}
)
- formatStr 格式化字符串,如 “yyyy-MM-dd HH:mm:ss”
- otherPlaceholder 其他自定义时间占位符(可选)
返回 TimeFormatter 类型的闭包对象
示例如下
void example2() {
final method = Time.generateFormatMethod("yyyy-MM-dd HH:mm:ss");
print(method(DateTime.now()));
}
执行结果:
2019-06-04 14:56:14
时间解析方法
将字符串按照指定格式转化为 时间戳(注意不是 DateTime 实例)
注意: 如果源字符串中包含非占位符,用 “*” 来代替
static int parse(
String sourceStr,
String formatStr,
{
List<TimePlaceholder> otherPlaceholder,
bool isSafe = false,
Duration timeZoneOffset
}
)
- sourceStr 源字符串,如 “2019-06-04 15:05:25”
- formatStr 格式化字符串,如 “yyyy*MM*dd*HH*mm*ss”
- otherPlaceholder 其他自定义时间占位符(可选)
- isSafe 如果为 true,解析发生异常时返回 null,否则会抛出异常(可选)
- timeZoneOffset 时区偏移量,默认为本地时区(可选)
示例如下:
void example3() {
print(Time.parse("2019-06-04 15:05:25", "yyyy*MM*dd*HH*mm*ss"));
}
执行结果:
1559631925000 // 本人在东八区,北京时间
时间解析方法对象
将解析方法包装成闭包对象返回,每次调用只需传递源字符串作为参数即可
将解析化中一些对象进行复用,优化了执行效率
static TimeParser generateParseMethod(
String formatStr,
{
List<TimePlaceholder> otherPlaceholder,
bool isSafe = false,
Duration timeZoneOffset
}
)
- formatStr 格式化字符串,如 “yyyy*MM*dd*HH*mm*ss”
- otherPlaceholder 其他自定义时间占位符(可选)
- isSafe 如果为 true,解析发生异常时返回 null,否则会抛出异常(可选)
- timeZoneOffset 时区偏移量,默认为本地时区(可选)
返回 TimeParser 类型的闭包对象
示例如下:
void example4() {
final method = Time.generateParseMethod("yyyy*MM*dd*HH*mm*ss");
print(method("2019-06-04 15:05:25"));
}
执行结果:
1559631925000
测量执行时间
在开发测试中,会需要测量某段代码具体的执行时间,来选择最优的算法
提供了一个用来满足这个需求的方法
static Duration measure(void run())
- run 表示执行方法函数闭包
示例如下:
void example5() {
final duration = Time.measure(() {
print("hello world");
});
print(duration);
}
执行结果:
hello world
0:00:00.000369
利用这个方法,我们可以来比较一下时间解析方法的效率
void example6() {
final loopCount = 10000;
final duration1 = Time.measure(() {
intEach(
callback: (position) {
DateTime.parse("2019-06-04 15:05:25");
}, total: loopCount);
});
final duration2 = Time.measure(() {
intEach(
callback: (position) {
Time.parse("2019-06-04 15:05:25", "yyyy*MM*dd*HH*mm*ss");
}, total: loopCount);
});
final duration3 = Time.measure(() {
final method = Time.generateParseMethod("yyyy*MM*dd*HH*mm*ss");
intEach(
callback: (position) {
method("2019-06-04 15:05:25");
}, total: loopCount);
});
print("dart 原生Api解析 $loopCount 次耗时: $duration1");
print("Time 直接解析 $loopCount 次耗时: $duration2");
print("Time 生成解析方法解析 $loopCount 次耗时: $duration3");
}
执行结果:
dart 原生Api解析 10000 次耗时: 0:00:00.233731
Time 直接解析 10000 次耗时: 0:00:00.089006
Time 生成解析方法解析 10000 次耗时: 0:00:00.012564
从结果可见,生成解析方法比原生方法大约快 20 倍左右
转换方法
转化动态对象为指定类型列表
/// 如果 needPicker 为 false 时,只有 obj 是 List<T> 类型才会返回具体值,否则一律返回 null
/// 如果 needPicker 为 true 时,分多种情况拾取指定类型的对象
/// 1. obj 是 Iterable 的子类,遍历迭代器,将所有指定类型的对象放入新的列表中,返回新的列表
/// 2. obj 是指定类型对象,则将对象包装到一个新的列表中,返回新的列表
List<T> convertTypeList<T>(dynamic obj, {bool needPicker = false});
示例如下:
void convert1() {
final list = [1, "2", 5];
print(convertTypeList<String>(list, needPicker: false));
}
void convert2() {
final list = [1, "2", 5];
print(convertTypeList<String>(list, needPicker: true));
}
执行结果为:
null
[2]
转换对象为指定类型
/// 转换对象为指定类型
/// 如果对象为 null 或者对象类型不匹配则返回 null
T castTo<T>(dynamic obj)
实例如下:
/// 转换对象
void convert3() {
dynamic number = 123;
print(castTo<String>(number));
print(castTo<int>(number));
}
执行结果为:
null
123
Scope
抽象了一个拥有状态的作用域:
- Activated (启用状态)
- Deactivated (禁用状态)
- Destroy (销毁状态)
闭包命名
typedef ScopeMessageCallback = Future Function(dynamic obj);
typedef ScopeActiveDelayMessageCallback = void Function(Map<dynamic, dynamic>);
typedef ScopeBroadcastReceiver = Function(dynamic obj);
typedef ScopeProxyAsyncRunnable<T> = Future<T> Function();
typedef ScopeProxySyncRunnable<T> = Future<T> Function();
主要使用用例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RvBBJHTe-1574221115045)(mdsrc/scope-relation.jpeg)]
可使用接口描述
/// 激活指定 Scope
static activate(Scope scope);
/// 关闭指定 Scope
static deactivate(Scope scope);
/// 销毁指定 Scope
static destroy(Scope scope);
/// 将指定 Scope 作为自己子 Scope
T fork<T extends Scope>(T scope);
/// 将调用的 Scope 与上级 Scope 断开
void dropSelf();
/// 将调用的 Scope 与全部子 Scope 断开
void dropChildren();
/// 注册消息接收器
/// 默认在关闭状态也可以接收
void registerMessageCallback(dynamic key, ScopeMessageCallback callback);
/// 注册消息接收器
/// 可以指定在何种状态下可以接收
void registerStatusMessageCallback(dynamic key, ScopeStatus allowRunStatus, ScopeMessageCallback callback);
/// 注销消息接收器
void unregisterMessageCallback(dynamic key);
/// 向下分发一次性消息,在消息第一次被接收后停止分发
/// 该方法可以返回处理后的结果
Future dispatchOneTimeMessage(dynamic key, dynamic data, {bool allowTraceBack = false, bool onlyTrackBack = false}) async;
/// 向下分发消息,会触发相同 key 值下全部的接收器
Future dispatchMessage(dynamic key, dynamic data) async;
/// 向上分发消息
/// 由该 Scope 向父级 Scope 传递消息
Future dispatchParentMessage(dynamic key, dynamic data, { int traceCount = 1}) async;
/// 注册活动延迟消息回调
/// 只可注册一次
void registerActiveDelayCallback(ScopeActiveDelayMessageCallback callback);
/// 发送活动延迟消息
void postActiveDelayMessage(dynamic key, dynamic data);
/// 重置活动延迟消息相关资源
void resetActiveDelay();
/// 发送广播给对应 key 下注册的全部广播接收器
static void broadcast(dynamic key, dynamic data) async;
/// 注册广播接收器
void registerBroadcast(dynamic key, ScopeBroadcastReceiver receiver);
/// 注销广播接收器
/// 如果指定广播接收器的话,则只注销指定的广播接收器,否则会将指定 key 值下全部的广播接收器全部注销
void unregisterBroadcast(dynamic key, {ScopeBroadcastReceiver receiver});
/// 代理执行 Future
/// 当 Scope 状态为销毁状态时不会指定并返回 null
Future<T> proxyAsync<T>(ScopeProxyAsyncRunnable<T> runnable) async;
/// 代理同步执行回调
/// 当 Scope 状态为销毁状态时返回 null
T proxySync<T>(ScopeProxySyncRunnable<T> runnable);
/// 设置存储数据
/// 可以设置 `syncParent = true`,同时会将数据同步到父 Scope 中;若想同步全部父 Scope,
/// 设置 `untilNotExistParent = true` 会一直向上同步,直到到达顶级 Scope.
T setStoredData<T>(dynamic key, T data, { bool syncParent = false, bool untilNotExistParent = false } );
/// 只设置父 Scope 存储数据,不影响自身
T setParentStoredData<T>(dynamic key, T data, { bool syncParent = false, bool untilNotExistParent = false });
/// 获取存储的数据
/// 如果没有找到对应数据,可以设置 `fromParentIfNotExist = true` 从父 Scope 中
/// 获取对应 Key 下的数据,如果存在父 Scope 的话;如果父 Scope 仍然不存在数据,可以设置
/// `fromParentUntilNotExist = true`,如此会一直向上查找,直到找数据或已到达顶级 Scope.
/// * `untilNotExistParent` 只在 `fromParentIfNotExist = true` 下才生效.
/// 但是如果自身对应 Key 下存在数据,但是类型不匹配的话,会直接返回 `null`.
T getStoredData<T>(dynamic key, { bool fromParentIfNotExist = false, bool untilNotExistParent = false });
/// 重置存储所用的全部数据
/// * 只会重置自身存储的数据,不影响父 Scope 中的数据
void resetStoredData();
详细见使用样例