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

mwt*_*mwt 30 protocols objective-c 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
Run Code Online (Sandbox Code Playgroud)

这是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;
Run Code Online (Sandbox Code Playgroud)

而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
Run Code Online (Sandbox Code Playgroud)

不要担心这堂课有什么遗漏.它没有做任何有趣的事情.我目前正在努力使语法正确.目前它编译(并且工作)但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
Run Code Online (Sandbox Code Playgroud)

同样,到目前为止它除了宣布它所处的阶段(或子状态)之外什么都不做.但这不是重点.

我希望在这里学到的是这个架构是否在obj-c语言中正确组合.我遇到的一个具体问题是在计时器的init函数中创建id对象.正如您所看到的,我评论了这些版本,因为它们导致了"在协议中找不到发布"警告.我不知道如何处理.

我不需要的是关于这段代码过度或无意义的形式主义或其他什么的评论.值得我学习这一点,即使这些想法是真实的.如果有帮助,可以将其视为对象中FSM的理论设计.

提前感谢您提供任何有用的评论.

(这没有太大帮助:Objective-C中的有限状态机)

小智 15

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

你不应该手工编写状态机代码,你应该使用一些东西来为你生成代码.SMC将生成干净清晰的代码,如果您想从中学习,可以查看,或者您可以使用它并完成它.


jle*_*ehr 8

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

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

    // Create scheduled timers, etc...
}
Run Code Online (Sandbox Code Playgroud)

  • 您还可以使协议符合其他协议,因此不必每次都提及 - 声明它像`@protocol TimerState <NSObject>`.这告诉编译器,所有TimerState对象也必须符合NSObject协议. (15认同)

Bla*_*ers 7

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