Как сделать базовый конечный автомат в Objective-C - PullRequest
29 голосов
/ 10 марта 2010

Я пытаюсь построить FSM для управления таймером в (iphone sdk) цели c. Я чувствовал, что это был необходимый шаг, потому что в противном случае я заканчивал противным кодом спагетти, содержащим страницы операторов if-then. Сложность, нечитаемость и сложность добавления / изменения функций побуждают меня попробовать более формальное решение, подобное этому.

В контексте приложения состояние таймера определяет некоторые сложные взаимодействия с NSManagedObjects, Core Data и т. Д. Я оставил всю эту функциональность пока в попытке получить четкое представление о коде FSM.

Проблема в том, что я не могу найти никаких примеров такого рода кода в Obj-C, и я не очень уверен в том, как я перевел его из примера кода C ++, который я использовал. (Я совсем не знаю C ++, так что есть некоторые догадки.) Я основываю эту версию разработки шаблонов состояний на этой статье: . Я не создаю игру, но эта статья обрисовывает в общих чертах понятия, которые работают для того, что я делаю.

Чтобы создать код (опубликован ниже), мне пришлось изучить много новых концепций, включая протоколы 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. Одна конкретная проблема, с которой я сталкиваюсь, - это создание объектов id в функции init таймера. Как видите, я закомментировал релизы, потому что они вызывали предупреждение «релиз не найден в протоколе». Я не был уверен, как справиться с этим.

Что мне не нужно, так это комментарии о том, что этот код является излишним, бессмысленным формализмом или чем-то еще. Это стоит того, чтобы узнать это, даже если эти идеи верны. Если это поможет, подумайте об этом как о теоретическом проекте для FSM в obj-c.

Заранее благодарю за любые полезные комментарии.

(это не слишком помогло: Конечный автомат в Objective-C )

Ответы [ 5 ]

15 голосов
/ 10 марта 2010

Я предлагаю использовать State Machine Compiler , он выведет Objective-C код. Я имел хороший успех в Java и Python, используя это.

Вы не должны писать код конечного автомата вручную, вы должны что-то использовать для генерации кода для вас. SMC сгенерирует чистый и понятный код, который вы сможете просмотреть, если хотите извлечь из него урок, или вы можете просто использовать его и покончить с этим.

8 голосов
/ 10 марта 2010

Когда вы используете протокол в качестве модификатора типа, вы можете предоставить список протоколов через запятую. Поэтому все, что вам нужно сделать, чтобы избавиться от предупреждения компилятора, это добавить NSObject в список протоколов следующим образом:

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

    // Create scheduled timers, etc...
}
7 голосов
/ 19 марта 2013

Если вы хотите очень простую реализацию конечного автомата в Objective-C, которую я только что выпустил TransitionKit , которая предоставляет хорошо разработанный API для реализации конечных автоматов. Он тщательно протестирован, хорошо документирован, очень прост в использовании и не требует генерации кода или внешних инструментов.

3 голосов
/ 12 июля 2012

Я бы посоветовал проверить Statec у него есть хороший маленький DSL для выполнения FSM и вывода кода ObjC. Это как генератор для конечных автоматов.

1 голос
/ 10 марта 2010

Я довольно новичок в Objective-C, но я бы посоветовал вам взглянуть на прямую реализацию ANSI C для конечного автомата.

То, что вы используете Какао, не означает, что вам нужно использовать сообщения Objective-C здесь.

В ANSI C реализация конечного автомата может быть очень простой и удобочитаемой.

Моя последняя реализация в C FSM указала #define STATE_x или перечислила типы для состояний и имела таблицу указателей на функции для выполнения каждого состояния.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...