我正在尝试构建一个FSM来控制(iphone sdk)目标c中的计时器。我觉得这是一个必要的步骤,因为我最终得到了含有if-then语句页面的令人讨厌的意大利面条代码。复杂性,不可读性以及添加/更改功能的难度使我尝试更正式的解决方案。
在应用程序的上下文中,计时器的状态确定与NSManagedObjects,Core Data等的一些复杂交互。我暂时保留了所有功能,试图清楚地了解FSM代码。
麻烦的是,我在Obj-C中找不到这种代码的任何例子,我对如何从我使用的C ++示例代码中翻译它并不是很有信心。 (我完全不懂C ++,所以有一些猜测。)我在这篇文章的基础上建立了这个版本的状态模式设计:http://www.ai-junkie.com/architecture/state_driven/tut_state1.html。我不是在制作游戏,但是这篇文章概述了适用于我正在做的事情的概念。
为了创建代码(在下面发布),我必须学习很多新概念,包括obj-c协议等等。因为这对我来说是新的,就像状态设计模式一样,我希望得到一些关于这种实现的反馈。这是你如何在obj-c中有效地使用协议对象?
以下是协议:
@class Timer;
@protocol TimerState
-(void) enterTimerState:(Timer*)timer;
-(void) executeTimerState:(Timer*)timer;
-(void) exitTimerState:(Timer*)timer;
@end
这是Timer对象(最简单的形式)头文件:
@interface Timer : NSObject
{
id<TimerState> currentTimerState;
NSTimer *secondTimer;
id <TimerViewDelegate> viewDelegate;
id<TimerState> setupState;
id<TimerState> runState;
id<TimerState> pauseState;
id<TimerState> resumeState;
id<TimerState> finishState;
}
@property (nonatomic, retain) id<TimerState> currentTimerState;
@property (nonatomic, retain) NSTimer *secondTimer;
@property (assign) id <TimerViewDelegate> viewDelegate;
@property (nonatomic, retain) id<TimerState> setupState;
@property (nonatomic, retain) id<TimerState> runState;
@property (nonatomic, retain) id<TimerState> pauseState;
@property (nonatomic, retain) id<TimerState> resumeState;
@property (nonatomic, retain) id<TimerState> finishState;
-(void)stopTimer;
-(void)changeState:(id<TimerState>) timerState;
-(void)executeState:(id<TimerState>) timerState;
-(void) setupTimer:(id<TimerState>) timerState;
Timer对象的实现:
#import "Timer.h"
#import "TimerState.h"
#import "Setup_TS.h"
#import "Run_TS.h"
#import "Pause_TS.h"
#import "Resume_TS.h"
#import "Finish_TS.h"
@implementation Timer
@synthesize currentTimerState;
@synthesize viewDelegate;
@synthesize secondTimer;
@synthesize setupState, runState, pauseState, resumeState, finishState;
-(id)init
{
if (self = [super init])
{
id<TimerState> s = [[Setup_TS alloc] init];
self.setupState = s;
//[s release];
id<TimerState> r = [[Run_TS alloc] init];
self.runState = r;
//[r release];
id<TimerState> p = [[Pause_TS alloc] init];
self.pauseState = p;
//[p release];
id<TimerState> rs = [[Resume_TS alloc] init];
self.resumeState = rs;
//[rs release];
id<TimerState> f = [[Finish_TS alloc] init];
self.finishState = f;
//[f release];
}
return self;
}
-(void)changeState:(id<TimerState>) newState{
if (newState != nil)
{
[self.currentTimerState exitTimerState:self];
self.currentTimerState = newState;
[self.currentTimerState enterTimerState:self];
[self executeState:self.currentTimerState];
}
}
-(void)executeState:(id<TimerState>) timerState
{
[self.currentTimerState executeTimerState:self];
}
-(void) setupTimer:(id<TimerState>) timerState
{
if ([timerState isKindOfClass:[Run_TS class]])
{
secondTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(currentTime) userInfo:nil repeats:YES];
}
else if ([timerState isKindOfClass:[Resume_TS class]])
{
secondTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(currentTime) userInfo:nil repeats:YES];
}
}
-(void) stopTimer
{
[secondTimer invalidate];
}
-(void)currentTime
{
//This is just to see it working. Not formatted properly or anything.
NSString *text = [NSString stringWithFormat:@"%@", [NSDate date]];
if (self.viewDelegate != NULL && [self.viewDelegate respondsToSelector:@selector(updateLabel:)])
{
[self.viewDelegate updateLabel:text];
}
}
//TODO: releases here
- (void)dealloc
{
[super dealloc];
}
@end
不要担心这门课有什么遗漏。它没有做任何有趣的事情。我目前正在努力使语法正确。目前它编译(并且工作)但isKindOfClass方法调用导致编译器警告(在协议中找不到方法)。我还不确定我是否想要使用isKindOfClass。我想给每个id<TimerState>
对象一个名称字符串并使用它来代替。
另一方面:所有id<TimerState>
声明都是最初的TimerState *声明。将它们保留为属性似乎是有意义的。不确定id<TimerState>
是否合理。
以下是其中一个州类的示例:
#import "TimerState.h"
@interface Setup_TS : NSObject <TimerState>{
}
@end
#import "Setup_TS.h"
#import "Timer.h"
@implementation Setup_TS
-(void) enterTimerState:(Timer*)timer{
NSLog(@"SETUP: entering state");
}
-(void) executeTimerState:(Timer*)timer{
NSLog(@"SETUP: executing state");
}
-(void) exitTimerState:(Timer*)timer{
NSLog(@"SETUP: exiting state");
}
@end
同样,到目前为止它除了宣布它所处的阶段(或子状态)之外什么都不做。但这不是重点。
我希望在这里学到的是这个架构是否在obj-c语言中正确组合。我遇到的一个具体问题是在计时器的init函数中创建id对象。正如您所看到的,我评论了这些版本,因为它们导致了“在协议中找不到发布”警告。我不知道如何处理。
我不需要评论这段代码是过度的或无意义的形式主义,或者其他什么。值得我学习这一点,即使这些想法是真实的。如果有帮助,可以将其视为对象中FSM的理论设计。
提前感谢您提供任何有用的评论。
(这没有太大帮助:Finite State Machine in Objective-C)
答案 0 :(得分:15)
我建议使用State Machine Compiler,它会输出Objective-C代码。我使用它在Java和Python上取得了很好的成功。
您不应该手动编写状态机代码,您应该使用某些东西来为您生成代码。 SMC将生成干净清晰的代码,如果您想从中学习,可以查看,或者您可以使用它并完成它。
答案 1 :(得分:8)
将协议用作类型修饰符时,可以提供以逗号分隔的协议列表。因此,除了编译器警告之外,您需要做的就是将NSObject添加到协议列表中,如下所示:
- (void)setupTimer:(id<TimerState,NSObject>) timerState {
// Create scheduled timers, etc...
}
答案 2 :(得分:7)
如果你想要一个非常简单的Objective-C状态机实现,我刚刚发布了TransitionKit,它提供了一个设计良好的API来实现状态机。它经过全面测试,文档齐全,易于使用,不需要任何代码生成或外部工具。
答案 3 :(得分:3)
我建议检查Statec它有一个很好的小dsl来做FSM并输出ObjC代码。它有点像状态机的发电机。
答案 4 :(得分:1)
我是Objective-C的新手,但我建议您查看状态机的直接ANSI C实现。
仅仅因为你使用Cocoa并不意味着你必须在这里使用Objective-C消息。
在ANSI C中,状态机实现可以非常简单和可读。
我在FS的C中的最后一次实现指定了#define STATE_x
或枚举状态的类型,并且有一个指向执行每个状态的函数的指针表。