Objective-C에서 기본 유한 상태 머신을 만드는 방법
-
18-09-2019 - |
문제
(iphone SDK) 목표 c에서 타이머를 제어하기 위해 FSM을 구축하려고 합니다.그렇지 않으면 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>
'에스.
다음은 상태 클래스 중 하나의 예입니다.
#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에 대한 이론적 설계라고 생각하세요.
도움이 되는 의견을 보내주셔서 미리 감사드립니다.
(이것은 별로 도움이 되지 않았습니다. Objective-C의 유한 상태 머신)
해결책
프로토콜을 유형 수정자로 사용하는 경우 쉼표로 구분된 프로토콜 목록을 제공할 수 있습니다.따라서 컴파일러 경고를 제거하기 위해 해야 할 일은 NSObject를 다음과 같이 프로토콜 목록에 추가하는 것뿐입니다.
- (void)setupTimer:(id<TimerState,NSObject>) timerState {
// Create scheduled timers, etc...
}
다른 팁
사용하는 것이 좋습니다 상태 기계 컴파일러, 출력됩니다 대상 c 암호. 나는 이것을 사용하여 Java와 Python에서 좋은 성공을 거두었습니다.
State Machine Code를 손으로 작성해서는 안되며, 코드를 생성하기 위해 무언가를 사용해야합니다. SMC는 깨끗한 명확한 코드를 생성하여 배우고 싶을 때 볼 수 있거나 사용하여 사용하여 수행 할 수 있습니다.
방금 출시 한 상태 머신의 매우 간단하고 객관적인 C 구현을 원한다면 TransitionKit, 상태 기계 구현을위한 잘 설계된 API를 제공합니다. 철저하게 테스트되고 문서화되어 있으며 사용하기 쉽고 코드 생성 또는 외부 도구가 필요하지 않습니다.
확인하는 것이 좋습니다 스테이크 FSM을 수행하고 OBJC 코드를 출력하기위한 멋진 작은 DSL이 있습니다. 그것은 State Machines의 Mogenerator와 비슷합니다.
나는 Objective-C에서는 다소 새롭지 만 State Machine의 직접적인 ANSI 구현을 살펴 보는 것이 좋습니다.
Cocoa를 사용한다고해서 여기에서 Objective-C 메시지를 사용해야한다는 의미는 아닙니다.
ANSI C에서는 상태 머신 구현이 매우 간단하고 읽을 수 있습니다.
FSM의 C에서 마지막 구현이 지정되었습니다 #define STATE_x
또는 상태에 대한 유형을 열거하고 각 상태를 실행하기위한 기능에 대한 포인터 테이블이있었습니다.