Pergunta

Estou tentando construir um FSM para controlar um timer no objetivo (iPhone SDK) C. Eu senti que era uma etapa necessária, porque estava acabando com o código espaguete desagradável contendo páginas de declarações if-then. A complexidade, a não leitura e a dificuldade de adicionar/alterar recursos me levam a tentar uma solução mais formal como essa.

No contexto da aplicação, o estado do timer determina algumas interações complexas com os NSmanAgedObjects, os dados principais e assim por diante. Deixei toda essa funcionalidade por enquanto, na tentativa de obter uma visão clara do código FSM.

O problema é que não consigo encontrar nenhum exemplo desse tipo de código no OBJ-C e não estou tão confiante sobre como o traduzi do código de exemplo C ++ que estava usando. (Eu não sei C ++, então há algumas adivinhações envolvidas.) Estou baseando esta versão de um design de padrões de estado neste artigo: http://www.ai-junkie.com/architecture/state_driven/tut_state1.html. Não estou fazendo um jogo, mas este artigo descreve conceitos que funcionam para o que estou fazendo.

Para criar o código (publicado abaixo), tive que aprender muitos novos conceitos, incluindo protocolos OBJ-C e assim por diante. Como isso é novo para mim, assim como o padrão de design do estado, espero alguns comentários sobre essa implementação. É assim que você trabalha com objetos de protocolo efetivamente no OBJ-C?

Aqui está o protocolo:

@class Timer;
@protocol TimerState 

-(void) enterTimerState:(Timer*)timer;
-(void) executeTimerState:(Timer*)timer;
-(void) exitTimerState:(Timer*)timer;

@end

Aqui está o objeto do timer (em sua forma mais despojada) arquivo de cabeçalho:

@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;

E a implementação do objeto do 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

Não se preocupe que faltem coisas nesta classe. Ainda não faz nada interessante. Atualmente, estou apenas lutando para obter a sintaxe correta. Atualmente, ele compila (e funciona), mas as chamadas do método ISKindofclass causam avisos do compilador (o método não é encontrado no protocolo). Não tenho certeza de que quero usar o ISKindofclass de qualquer maneira. Eu estava pensando em dar a cada id<TimerState> Objeta uma string de nome e usando isso.

Em outra nota: todos aqueles id<TimerState> As declarações eram originalmente Timerstate * declarações. Parecia fazer sentido mantê -los como propriedades. Não tenho certeza se faz sentido com id<TimerState>'s.

Aqui está um exemplo de uma das classes estaduais:

#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

Novamente, até agora não faz nada, exceto anunciar que está dentro de que fase (ou sub-estatal) está. Mas esse não é o ponto.

O que espero aprender aqui é se essa arquitetura é composta corretamente no idioma OBJ-C. Um problema específico que estou encontrando é a criação dos objetos de identificação na função init do timer. Como você pode ver, comentei os lançamentos, porque eles estavam causando um aviso "Release não encontrado no protocolo". Eu não tinha certeza de como lidar com isso.

O que eu não preciso é de comentários sobre esse código sobre o exagero ou o formalismo sem sentido, ou o que quer que seja. Vale a pena aprender isso, mesmo essas idéias são verdadeiras. Se ajudar, pense nisso como um design teórico para um FSM no OBJ-C.

Agradeço antecipadamente por quaisquer comentários úteis.

(Isso não ajudou muito: Máquina de Estado Finito em Objective-C)

Foi útil?

Solução

Quando você usa um protocolo como modificador de tipo, você pode fornecer uma lista de protocolos separados por vírgula. Então, tudo o que você precisa fazer para se livrar do aviso do compilador é adicionar nsObject à lista de protocolos como assim:

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

    // Create scheduled timers, etc...
}

Outras dicas

Eu sugiro usar Compilador de máquinas de estado, será lançado Objective-C código. Eu tive um bom sucesso em Java e Python usando isso.

Você não deve escrever o código da máquina do estado manualmente, você deve usar algo para gerar o código para você. O SMC gerará um código limpo limpo que você pode analisar se quiser aprender com ele, ou você pode usá -lo e acabar com ele.

Se você deseja uma implementação muito simples, objetiva-c de uma máquina de estado que acabei de lançar TransitionKit, que fornece uma API bem projetada para a implementação de máquinas de estado. É cuidadosamente testado, bem documentado, muito fácil de usar e não requer nenhuma geração de código ou ferramentas externas.

Eu sugiro conferir Statec Tem um pequeno DSL agradável para fazer o código FSM e Saídas OBJC. É como o Mogerator para máquinas de estado.

Sou bastante novo no Objective-C, mas sugiro que você olhe para a implementação direta do ANSI C para a máquina de estado.

Só porque você está usando o cacau não significa que você precisa usar as mensagens Objective-C aqui.

Na ANSI C, uma implementação de máquina de estado pode ser muito direta e legível.

Minha última implementação em C de um FSM especificado #define STATE_x ou enumerar tipos para os estados e tinha uma tabela de ponteiros para funções para executar cada estado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top