31

(iphone sdk)ObjectiveCでタイマーを制御するFSMを構築しようとしています。そうでなければ、if-thenステートメントのページを含む厄介なスパゲッティコードになってしまうので、それは必要なステップだと感じました。機能の追加/変更の複雑さ、読みやすさ、難しさから、このようなより正式なソリューションを試すことになりました。

アプリケーションのコンテキストでは、タイマーの状態によって、NSManagedObjects、CoreDataなどとの複雑な相互作用が決まります。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で意味があるかどうかわからない。

状態クラスの1つの例を次に示します。

#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言語で正しく構成されているかどうかです。私が遭遇している特定の問題の1つは、タイマーのinit関数でのidオブジェクトの作成です。ご覧のとおり、「プロトコルにリリースが見つかりません」という警告が表示されていたため、リリースをコメントアウトしました。どう対処したらいいのかわからなかった。

私が必要としないのは、このコードがやり過ぎであるか無意味な形式主義であるかなどについてのコメントです。それらの考えが真実であるとしても、これを学ぶことは私にとって価値があります。それが役立つ場合は、obj-cのFSMの理論的な設計と考えてください。

有益なコメントをよろしくお願いします。

(これはあまり役に立ちませんでした:Objective-Cの有限状態マシン

4

5 に答える 5

15

ステートマシンコンパイラを使用することをお勧めします。Objective-Cコードが出力されます。私はこれを使ってJavaとPythonで良い成功を収めました。

ステートマシンコードを手動で作成するのではなく、コードを生成するために何かを使用する必要があります。SMCは、クリーンでクリアなコードを生成します。このコードから学習したい場合は、SMCを使用するか、それを使用して実行することができます。

于 2010-03-09T22:06:14.347 に答える
8

プロトコルを型修飾子として使用する場合、プロトコルのコンマ区切りリストを提供できます。したがって、コンパイラの警告を取り除くために必要なのは、次のようにプロトコルリストにNSObjectを追加することだけです。

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

    // Create scheduled timers, etc...
}
于 2010-03-09T22:42:33.743 に答える
8

ステートマシンの非常に単純なObjective-C実装が必要な場合は、トランジションキットをリリースしました。これは、ステートマシンを実装するための適切に設計されたAPIを提供します。徹底的にテストされ、十分に文書化されており、非常に使いやすく、コード生成や外部ツールを必要としません。

于 2013-03-18T23:02:22.900 に答える
3

Statecをチェックすることをお勧めします。FSMを実行してObjCコードを出力するためのちょっとしたDSLがあります。これは、ステートマシンのモジェネレーターのようなものです。

于 2012-07-11T22:28:54.873 に答える
1

私はObjective-Cにかなり慣れていませんが、ステートマシン用の直接のANSIC実装を検討することをお勧めします。

Cocoaを使用しているからといって、ここでObjective-Cメッセージを使用する必要があるわけではありません。

ANSI Cでは、ステートマシンの実装は非常に簡単で読みやすいものになります。

FSMのCでの最後の実装では#define STATE_x、状態の型を指定または列挙し、各状態を実行する関数へのポインターのテーブルがありました。

于 2010-03-09T22:01:46.917 に答える