31

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

以及定时器对象的实现:

#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>'s 是否有意义。

以下是其中一个状态类的示例:

#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 对象。如您所见,我注释掉了这些版本,因为它们导致了“在协议中找不到版本”警告。我不知道如何处理。

我不需要的是关于此代码过度杀伤或无意义的形式主义或其他什么的评论。即使这些想法是正确的,我也值得学习这一点。如果有帮助,请将其视为 obj-c 中 FSM 的理论设计。

预先感谢您提供任何有用的意见。

(这并没有太大帮助:Finite State Machine in Objective-C

4

5 回答 5

15

我建议使用State Machine Compiler,它会输出Objective-C代码。使用它,我在 Java 和 Python 方面取得了很好的成功。

你不应该手动编写状态机代码,你应该使用一些东西来为你生成代码。SMC 将生成干净清晰的代码,然后您可以查看是否想从中学习,或者您可以直接使用它并完成它。

于 2010-03-09T22:06:14.347 回答
8

如果你想要一个状态机的非常简单的 Objective-C 实现,我刚刚发布了TransitionKit,它为实现状态机提供了一个设计良好的 API。它经过全面测试,有据可查,非常易于使用,并且不需要任何代码生成或外部工具。

于 2013-03-18T23:02:22.900 回答
8

当您使用协议作为类型修饰符时,您可以提供以逗号分隔的协议列表。因此,摆脱编译器警告所需要做的就是将 NSObject 添加到协议列表中,如下所示:

- (void)setupTimer:(id<TimerState,NSObject>) timerState {

    // Create scheduled timers, etc...
}
于 2010-03-09T22:42:33.743 回答
3

我建议查看Statec,它有一个不错的小 dsl 用于执行 FSM 并输出 ObjC 代码。它有点像状态机的 mogenerator。

于 2012-07-11T22:28:54.873 回答
1

我对 Objective-C 比较陌生,但我建议您直接查看状态机的 ANSI C 实现。

仅仅因为您使用 Cocoa 并不意味着您必须在此处使用 Objective-C 消息。

在 ANSI C 中,状态机实现可以非常简单易读。

我最后一次在 C 中实现 FSM#define STATE_x为状态指定或枚举类型,并有一个指向函数的指针表来执行每个状态。

于 2010-03-09T22:01:46.917 回答