TBStateMachine
一个轻量级的 Objective-C 级联状态机框架。
特性
- 基于块的 API
- 嵌套状态
- 正交区域
- 虚拟状态(分支、汇合和交叉点)
- 具有守卫和动作的外部、内部和本地转移
- 使用最小编码等级算法(LCA)进行状态切换
- 线程安全的事件处理
- 异步事件处理
- 支持NSNotificationCenter
要求
- watchOS 2.0
- iOS 6.0
- OS X 10.8
TBStateMachine 可以通过 CocoaPods 获得。要安装,只需将以下行添加到您的 Podfile 中:
pod 'TBStateMachine'
#import <TBStateMachine/TBSMStateMachine.h>
创建状态,设置进入和退出代码块
TBSMState *a = [TBSMState stateWithName:@"a"];
a.enterBlock = ^(id data) {
};
a.exitBlock = ^(id data) {
};
创建状态机
TBSMStateMachine *stateMachine = [TBSMStateMachine stateMachineWithName:@"main"];
添加状态并设置状态机。除非您显式设置初始状态,否则状态机始终将数组中给出的第一个状态作为初始状态
stateMachine.states = @[a, b, ...];
stateMachine.initialState = a;
[stateMachine setUp:nil];
您可以添加事件处理器,触发到特定目标状态的转变
[a addHandlerForEvent:@"transition_1" target:b];
您还可以添加具有附加动作和守卫块的事件处理器
TBSMActionBlock action = ^(id data) {
};
TBSMGuardBlock guard = ^BOOL(id data) {
return YES;
};
[a addHandlerForEvent:@"transition_1" target:b kind:TBSMTransitionExternal action:action guard:guard];
如果您为同一事件注册了多个处理器,则守卫块将决定哪个转变将被触发。
不同类型的转换
默认情况下,转换是外部的。要显式定义转换类型,请选择以下三个类型属性之一
TBSMTransitionExternal
TBSMTransitionInternal
TBSMTransitionLocal
调度事件
要调度事件,请调用 scheduleEvent:
并传递指定的 TBSMEvent
实例以及(可选的)负载对象
TBSMEvent *event = [TBSMEvent eventWithName:@"transition_1" data:aPayloadObject];
[stateMachine scheduleEvent:event];
负载将在所有执行直到事件成功处理为止的动作、守卫、进入和退出块中可用。
枚举事件
如果您不想像这样为每个事件编写字符串常量
FOUNDATION_EXPORT NSString * const Transition_1;
NSString * const Transition_1 = @"transition_1";
可以使用一个结构体
FOUNDATION_EXPORT const struct StateMachineEvents {
__unsafe_unretained NSString *Transition_1;
__unsafe_unretained NSString *Transition_2;
} StateMachineEvents;
const struct StateMachineEvents StateMachineEvents = {
.Transition_1 = @"transition_1",
.Transition_2 = @"transition_2"
};
可以这样使用它
[stateMachine scheduleEventNamed:StateMachineEvents.Transition_1 data:aPayloadObject];
运行到完成
事件处理遵循运行到完成模型,以确保一次只处理一个事件。单个RTC步骤封装了从评估事件到执行转换直到执行守卫、动作、退出和进入块的整个过程。
事件将按顺序排队并处理。
嵌套状态
TBSMState
实例可以通过 TBSMSubState
进行嵌套。
TBSMSubState *b2 = [TBSMSubState subStateWithName:@"b2"];
b2.states = @[b21, b22];
由于 TBSMSubState
是 TBSMState
的子类型,您还可以在该类型上注册事件、添加进入和退出块。
正交区域
要构建正交区域,请使用 TBSMParallelState
。
TBSMParallelState *b3 = [TBSMParallelState parallelStateWithName:@"b3"];
b3.states = @[@[b311, b312], @[b321, b322]];
伪状态
TBStateMachine 支持使用分叉和汇合伪状态来构建复合转换。
分叉
TBSMFork *fork = [TBSMFork forkWithName:@"fork"];
[a addHandlerForEvent:@"transition_15" target:fork];
[fork setTargetStates:@[c212, c222] inRegion:c2];
汇合
TBSMJoin *join = [TBSMJoin joinWithName:@"join"];
[c212 addHandlerForEvent:@"transition_16" target:join];
[c222 addHandlerForEvent:@"transition_17" target:join];
[join setSourceStates:@[c212, c222] inRegion:c2 target:b];
节点
TBSMJunction *junction = [TBSMJunction junctionWithName:@"junction"];
[a addHandlerForEvent:@"transition_18" target:junction];
[junction addOutgoingPathWithTarget:b1 action:nil guard:^BOOL(id data) {
return (data[@"goB1"]);
}];
[junction addOutgoingPathWithTarget:c2 action:nil guard:^BOOL(id data) {
return (data[@"goC2"]);
}];
通知
TBSMState
在进入和退出时发布 NSNotification
- TBSMStateDidEnterNotification
- TBSMStateDidExitNotification
通知的 userInfo
包含
{
TBSMDataUserInfo:theData
}
要接收通知
[self.stateMachine subscribeToEntryAtPath:@"c/c2@1/c222" forObserver:self selector:@selector(myHandler:)];
[self.stateMachine subscribeToExitAtPath:@"c/c2@1/c222" forObserver:self selector:@selector(myHandler:)];
- (void)myHandler:(NSNotification *)notification
{
id myPayloadObject = notification.userInfo[TBSMDataUserInfo];
}
TBSMState
还在执行内部转换时发布带有事件名称的 NSNotification
[self.stateMachine subscribeToAction:@"transition_10" atPath:@"a/a1" forObserver:self selector:@selector(myHandler:)];
要通过路径方案(如上所示)查找特定状态,可以使用该方案。路径由用斜杠分隔的状态名称组成
b/b2/b21
在正交区域内,添加了 @
符号和索引以选择特定的子区域
c/c2@1/c222
配置文件
状态机可以通过 json 文件进行配置并由 TBSMStateMachineBuilder
构建
pod 'TBStateMachine/Builder'
#import <TBStateMachine/TBSMStateMachineBuilder.h>
NSString *path = // path to file
TBMSStateMachine *stateMachine = [TBSMStateMachineBuilder buildFromFile:path];
配置文件的格式可以在测试固定文件中查看。
配置文件的 json 格式遵循Pod/Builder/Schema/schema.json中定义的 json-schema。
有关 json schema 的一般信息请参阅https://json-schema.fullstack.org.cn。
线程安全与并发
TBStateMachine
是线程安全的。默认情况下,每个事件都在主队列上异步处理。这使得处理 UIKit 组件变得方便。
要使用专用的后台队列,只需设置
NSOperationQueue *queue = [NSOperationQueue new];
queue.name = @"com.myproject.queue";
queue.maxConcurrentOperationCount = 1;
stateMachine.scheduledEventsQueue = queue;
调试支持
TBStateMachine
通过子规范 DebugSupport
提供调试支持。只需将其添加到您的 Podfile
中(很可能添加到一个 beta 目标,以避免在生产代码中使用)
target 'MyBetaApp', :exclusive => true do
pod 'TBStateMachine/DebugSupport'
end
然后،在堆栈顶部包含 TBSMDebugger.h
以调试状态机
#import <TBStateMachine/TBSMDebugger.h>
[[TBSMDebugger sharedInstance] debugStateMachine:stateMachine];
状态机将输出每个事件、转换、初始化、清理、进入和退出等的日志消息,包括执行 Run-to-Completion 步骤的持续时间
[Main]: attempt to handle event 'transition_4' data: 12345
[stateA] will handle event 'transition_4' data: 12345
[Main] performing transition: stateA --> stateCc data: 12345
Exit 'a3' data: 12345
Exit 'a' data: 12345
Enter 'b' data: 12345
Enter 'b2' data: 12345
Enter 'b21' data: 12345
[Main]: run-to-completion step took 1.15 milliseconds
[Main]: remaining events in queue: 1
[Main]: (
transition_8
)
在调试实例上调用 -activeStateConfiguration
时,您将获得整个堆栈的当前活动状态配置
NSLog(@"%@", [[TBSMDebugger sharedInstance] activeStateConfiguration]);
Main
b
bSubMachine
b2
b2Submachine
b21
开发设置
首先克隆仓库,然后从 Example
目录运行 pod install
。项目包含针对开发的单元测试目标。
关于 UML 状态机的有用理论
作者
Julian Krumow, [邮箱保护: obfuscatron.com]
许可协议
TBStateMachine 在MIT许可下可用。更多信息请参阅LICENSE文件。