如何在Objective-C中建立一个基本的有限状态机

时间:2010-03-09 21:30:06

标签: objective-c protocols state-machine

我正在尝试构建一个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

5 个答案:

答案 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或枚举状态的类型,并且有一个指向执行每个状态的函数的指针表。