2012-11-15 2 views
0

현재 Cocos2d 게임의 설정을 저장하는 기능을 가지고 있으며 사용하는 방법은 XML 파서입니다.Cocos2d 게임에서 사용자 설정을 효과적으로 저장하는 방법

우선 NSUserDefaults를 다음과 같이 사용하는 것이 더 좋습니까?

EDIT : XML PARSER가 끝까지 건너 뛰는 지 이미 알고 있다면 미리 작성해 두십시오.

#import <Foundation/Foundation.h> 

@interface GlobalSettings : NSObject { 

// Declare variables with an underscore in front, for example: 
int _currency; 
BOOL _BankerBossDefeated; 
BOOL _BabyBossDefeated; 
BOOL _DuckBossDefeated; 
BOOL _BaseBallBossDefeated; 
BOOL _NewtonBossDefeated; 

BOOL _CatchExtender; 
BOOL _CatchExtenderEnabled; 
BOOL _SpeedBoost; 
BOOL _SpeedBoostEnabled; 
} 

// Declare your variable properties without an underscore, for example: 
@property (nonatomic, assign) int currency; 
@property (nonatomic, assign) BOOL BankerBossDefeated; 
@property (nonatomic, assign) BOOL BabyBossDefeated; 
@property (nonatomic, assign) BOOL DuckBossDefeated; 
@property (nonatomic, assign) BOOL BaseBallBossDefeated; 
@property (nonatomic, assign) BOOL NewtonBossDefeated; 
@property (nonatomic, assign) BOOL SpeedBoost; 
@property (nonatomic, assign) BOOL CatchExtender; 
@property (nonatomic, assign) BOOL SpeedBoostEnabled; 
@property (nonatomic, assign) BOOL CatchExtenderEnabled; 

// Put your custom init method interface here: 
-(id)initWithcurrency:(int)currency 
BankerBossDefeated:(BOOL)BankerBossDefeated 
BabyBossDefeated:(BOOL)BabyBossDefeated 
DuckBossDefeated:(BOOL)DuckBossDefeated 
BaseBallBossDefeated:(BOOL)BaseBallBossDefeated 
NewtonBossDefeated:(BOOL)NewtonBossDefeated 
CatchExtender:(BOOL)CatchExtender 
     SpeedBoost:(BOOL)SpeedBoost 
CatchExtenderEnabled:(BOOL)CatchExtenderEnabled 
     SpeedBoostEnabled:(BOOL)SpeedBoostEnabled; 

@end 

내 GlobalSettings.m은 다음과 같습니다 :

#import "GlobalSettings.h" 

@implementation GlobalSettings 

// Synthesize your variables here, for example: 
@synthesize currency = _currency; 
@synthesize BankerBossDefeated = _BankerBossDefeated; 
@synthesize BabyBossDefeated = _BabyBossDefeated; 
@synthesize DuckBossDefeated = _DuckBossDefeated; 
@synthesize BaseBallBossDefeated = _BaseBallBossDefeated; 
@synthesize NewtonBossDefeated = _NewtonBossDefeated; 
@synthesize SpeedBoost = _SpeedBoost; 
@synthesize CatchExtender = _CatchExtender; 
@synthesize SpeedBoostEnabled = _SpeedBoostEnabled; 
@synthesize CatchExtenderEnabled = _CatchExtenderEnabled; 

// put your custom init method here which takes a variable 
// for each class instance variable 
-(id)initWithcurrency:(int)currency 
BankerBossDefeated:(BOOL)BankerBossDefeated 
BabyBossDefeated:(BOOL)BabyBossDefeated 
DuckBossDefeated:(BOOL)DuckBossDefeated 
BaseBallBossDefeated:(BOOL)BaseBallBossDefeated 
NewtonBossDefeated:(BOOL)NewtonBossDefeated 
    CatchExtender:(BOOL)CatchExtender 
     SpeedBoost:(BOOL)SpeedBoost 
    CatchExtenderEnabled:(BOOL)CatchExtenderEnabled 
     SpeedBoostEnabled:(BOOL)SpeedBoostEnabled;{ 

if ((self = [super init])) { 

    // Set class instance variables based on values 
    // given to this method 
    self.currency = currency; 
    self.BankerBossDefeated = BankerBossDefeated; 
    self.BabyBossDefeated = BabyBossDefeated; 
    self.DuckBossDefeated = DuckBossDefeated; 
    self.BaseBallBossDefeated = BaseBallBossDefeated; 
    self.NewtonBossDefeated = NewtonBossDefeated; 
    self.CatchExtender = CatchExtender; 
    self.SpeedBoost = SpeedBoost; 
    self.CatchExtenderEnabled = CatchExtenderEnabled; 
    self.SpeedBoostEnabled = SpeedBoostEnabled; 
} 
return self; 
} 

- (void) dealloc { 
[super dealloc]; 
} 

@end 

내가 다음 SettingsParser.h으로 XML을 구문 분석 :

#import <Foundation/Foundation.h> 

@class GlobalSettings; 

@interface SettingsParser : NSObject {} 

+ (GlobalSettings *)loadData; 
+ (void)saveData:(GlobalSettings *)saveData; 

@end 

하고 여기에

내 GlobalSettings.h입니다 설정 매개 변수 :

#import "SettingsParser.h" 
#import "GlobalSettings.h" 
#import "GDataXMLNode.h" 

@implementation SettingsParser 

+ (NSString *)dataFilePath:(BOOL)forSave { 

NSString *xmlFileName = @"GlobalSettings"; 

/*************************************************************************** 
This method is used to set up the specified xml for reading/writing. 
Specify the name of the XML file you want to work with above. 
You don't have to worry about the rest of the code in this method. 
***************************************************************************/ 

NSString *xmlFileNameWithExtension = [NSString stringWithFormat:@"%@.xml",xmlFileName];  
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,  NSUserDomainMask, YES); 
NSString *documentsDirectory = [paths objectAtIndex:0]; 
NSString *documentsPath = [documentsDirectory  stringByAppendingPathComponent:xmlFileNameWithExtension]; 
if (forSave || [[NSFileManager defaultManager] fileExistsAtPath:documentsPath]) { 
    return documentsPath; 
    NSLog(@"%@ opened for read/write",documentsPath); 
} else { 
    NSLog(@"Created/copied in default %@",xmlFileNameWithExtension); 
    return [[NSBundle mainBundle] pathForResource:xmlFileName ofType:@"xml"]; 
}  
} 

+ (GlobalSettings *)loadData { 

/*************************************************************************** 
This loadData method is used to load data from the xml file 
specified in the dataFilePath method above. 

MODIFY the list of variables below which will be used to create 
and return an instance of TemplateData at the end of this method. 
***************************************************************************/ 

int currency; 
BOOL BankerBossDefeated; 
BOOL BabyBossDefeated; 
BOOL DuckBossDefeated; 
BOOL BaseBallBossDefeated; 
BOOL NewtonBossDefeated; 
BOOL CatchExtender; 
BOOL SpeedBoost; 
BOOL CatchExtenderEnabled; 
BOOL SpeedBoostEnabled; 


// Create NSData instance from xml in filePath 
NSString *filePath = [self dataFilePath:FALSE]; 
NSData *xmlData = [[NSMutableData alloc] initWithContentsOfFile:filePath]; 
NSError *error; 
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error]; 
if (doc == nil) { return nil; NSLog(@"xml file is empty!");} 
NSLog(@"Loading %@", filePath); 

/*************************************************************************** 
This next line will usually have the most customisation applied because 
it will be a direct reflection of what you want out of the XML file. 
***************************************************************************/ 

NSArray *dataArray = [doc nodesForXPath:@"//GlobalSettings" error:nil]; 
NSLog(@"Array Contents = %@", dataArray); 

/*************************************************************************** 
We use dataArray to populate variables created at the start of this 
method. For each variable you will need to: 
    1. Create an array based on the elements in the xml 
    2. Assign the variable a value based on data in elements in the xml 
***************************************************************************/ 

for (GDataXMLElement *element in dataArray) { 

    NSArray *currencyArray = [element elementsForName:@"currency"];  
    NSArray *BankerBossDefeatedArray = [element elementsForName:@"BankerBossDefeated"]; 
    NSArray *BabyBossDefeatedArray = [element elementsForName:@"BabyBossDefeated"]; 
    NSArray *DuckBossDefeatedArray = [element elementsForName:@"DuckBossDefeated"]; 
    NSArray *BaseBallBossDefeatedArray = [element  elementsForName:@"BaseBallBossDefeated"]; 
    NSArray *NewtonBossDefeatedArray = [element elementsForName:@"NewtonBossDefeated"]; 
    NSArray *CatchExtenderArray = [element elementsForName:@"CatchExtender"]; 
    NSArray *SpeedBoostArray = [element elementsForName:@"SpeedBoost"]; 
    NSArray *CatchExtenderEnabledArray = [element elementsForName:@"CatchExtenderEnabled"]; 
    NSArray *SpeedBoostEnabledArray = [element elementsForName:@"SpeedBoostEnabled"]; 

    // currency 
    if (currencyArray.count > 0) { 
     GDataXMLElement *currencyElement = (GDataXMLElement *) [currencyArray objectAtIndex:0]; 
     currency = [[currencyElement stringValue] intValue]; 
    } 

    // BankerBossDefeated  
    if (BankerBossDefeatedArray.count > 0) { 
     GDataXMLElement *BankerBossDefeatedElement = (GDataXMLElement *) [BankerBossDefeatedArray objectAtIndex:0]; 
     BankerBossDefeated = [[BankerBossDefeatedElement stringValue] boolValue]; 
    } 

    // DuckBossDefeated  
    if (DuckBossDefeatedArray.count > 0) { 
     GDataXMLElement *DuckBossDefeatedElement = (GDataXMLElement *) [DuckBossDefeatedArray objectAtIndex:0]; 
     DuckBossDefeated = [[DuckBossDefeatedElement stringValue] boolValue]; 
    } 

    // BabyBossDefeated  
    if (BabyBossDefeatedArray.count > 0) { 
     GDataXMLElement *BabyBossDefeatedElement = (GDataXMLElement *) [BabyBossDefeatedArray objectAtIndex:0]; 
     BabyBossDefeated = [[BabyBossDefeatedElement stringValue] boolValue]; 
    } 

    // BaseBallBossDefeated  
    if (BaseBallBossDefeatedArray.count > 0) { 
     GDataXMLElement *BaseBallBossDefeatedElement = (GDataXMLElement *) [BaseBallBossDefeatedArray objectAtIndex:0]; 
     BaseBallBossDefeated = [[BaseBallBossDefeatedElement stringValue] boolValue]; 
    } 

    // NewtonBossDefeated  
    if (NewtonBossDefeatedArray.count > 0) { 
     GDataXMLElement *NewtonBossDefeatedElement = (GDataXMLElement *) [NewtonBossDefeatedArray objectAtIndex:0]; 
     NewtonBossDefeated = [[NewtonBossDefeatedElement stringValue] boolValue]; 
    } 
    // CatchExtender 
    if (CatchExtenderArray.count > 0) { 
     GDataXMLElement *CatchExtenderElement = (GDataXMLElement *) [CatchExtenderArray objectAtIndex:0]; 
     CatchExtender = [[CatchExtenderElement stringValue] boolValue]; 
    } 
    // SpeedBoost 
    if (SpeedBoostArray.count > 0) { 
     GDataXMLElement *SpeedBoostElement = (GDataXMLElement *) [SpeedBoostArray objectAtIndex:0]; 
     SpeedBoost = [[SpeedBoostElement stringValue] boolValue]; 
    } 

    // CatchExtenderEnabled 
    if (CatchExtenderEnabledArray.count > 0) { 
     GDataXMLElement *CatchExtenderEnabledElement = (GDataXMLElement *) [CatchExtenderEnabledArray objectAtIndex:0]; 
     CatchExtenderEnabled = [[CatchExtenderEnabledElement stringValue] boolValue]; 
    } 
    // SpeedBoost 
    if (SpeedBoostEnabledArray.count > 0) { 
     GDataXMLElement *SpeedBoostEnabledElement = (GDataXMLElement *) [SpeedBoostEnabledArray objectAtIndex:0]; 
     SpeedBoostEnabled = [[SpeedBoostEnabledElement stringValue] boolValue]; 
    } 

} 

/*************************************************************************** 
Now the variables are populated from xml data we create an instance of 
TemplateData to pass back to whatever called this method. 

The initWithExampleInt:exampleBool:exampleString will need to be replaced 
with whatever method you have updaed in the TemplateData class. 
***************************************************************************/ 

//NSLog(@"XML value read for exampleInt = %i", exampleInt); 
//NSLog(@"XML value read for exampleBool = %i", exampleBool); 
//NSLog(@"XML value read for exampleString = %@", exampleString); 

GlobalSettings *Data = [[GlobalSettings alloc] initWithcurrency:currency 
                BankerBossDefeated:BankerBossDefeated 
               BabyBossDefeated:BabyBossDefeated  DuckBossDefeated:DuckBossDefeated BaseBallBossDefeated:BaseBallBossDefeated  NewtonBossDefeated:NewtonBossDefeated 
                CatchExtender:CatchExtender 
                SpeedBoost:SpeedBoost  CatchExtenderEnabled:CatchExtenderEnabled 
                SpeedBoostEnabled:SpeedBoostEnabled]; 

[doc release]; 
[xmlData release]; 
return Data; 
[Data release]; 
} 

+ (void)saveData:(GlobalSettings *)saveData { 


/*************************************************************************** 
This method writes data to the xml based on a TemplateData instance 
You will have to be very aware of the intended xml contents and structure 
as you will be wiping and re-writing the whole xml file. 

We write an xml by creating elements and adding 'children' to them. 

You'll need to write a line for each element to build the hierarchy // <-- MODIFY CODE ACCORDINGLY 
***************************************************************************/ 

GDataXMLElement *GlobalSettingsElement = [GDataXMLNode elementWithName:@"GlobalSettings"]; 

GDataXMLElement *currencyElement = [GDataXMLNode elementWithName:@"currency" 
                 stringValue:[[NSNumber numberWithInt:saveData.currency] stringValue]]; 

GDataXMLElement *BankerBossDefeatedElement = [GDataXMLNode elementWithName:@"BankerBossDefeated" 
                 stringValue:[[NSNumber numberWithBool:saveData.BankerBossDefeated] stringValue]]; 

GDataXMLElement *BabyBossDefeatedElement = [GDataXMLNode elementWithName:@"BabyBossDefeated" 
                   stringValue:[[NSNumber numberWithBool:saveData.BabyBossDefeated] stringValue]]; 

GDataXMLElement *DuckBossDefeatedElement = [GDataXMLNode elementWithName:@"DuckBossDefeated" 
                   stringValue:[[NSNumber numberWithBool:saveData.DuckBossDefeated] stringValue]]; 

GDataXMLElement *BaseBallBossDefeatedElement = [GDataXMLNode elementWithName:@"BaseBallBossDefeated" 
                   stringValue:[[NSNumber numberWithBool:saveData.BaseBallBossDefeated] stringValue]]; 

GDataXMLElement *NewtonBossDefeatedElement = [GDataXMLNode elementWithName:@"NewtonBossDefeated" 
                   stringValue:[[NSNumber numberWithBool:saveData.NewtonBossDefeated] stringValue]]; 
GDataXMLElement *CatchExtenderElement = [GDataXMLNode elementWithName:@"CatchExtender" 
                   stringValue:[[NSNumber numberWithBool:saveData.CatchExtender] stringValue]]; 
GDataXMLElement *SpeedBoostElement = [GDataXMLNode elementWithName:@"SpeedBoost" 
                   stringValue:[[NSNumber numberWithBool:saveData.SpeedBoost] stringValue]]; 
GDataXMLElement *CatchExtenderEnabledElement = [GDataXMLNode elementWithName:@"CatchExtenderEnabled" 
                  stringValue:[[NSNumber numberWithBool:saveData.CatchExtender] stringValue]]; 
GDataXMLElement *SpeedBoostEnabledElement = [GDataXMLNode elementWithName:@"SpeedBoostEnabled" 
                 stringValue:[[NSNumber numberWithBool:saveData.SpeedBoost] stringValue]]; 


// Using the elements just created, set up the hierarchy 
[GlobalSettingsElement addChild:currencyElement]; 
[GlobalSettingsElement addChild:BankerBossDefeatedElement]; 
[GlobalSettingsElement addChild:BabyBossDefeatedElement]; 
[GlobalSettingsElement addChild:DuckBossDefeatedElement]; 
[GlobalSettingsElement addChild:BaseBallBossDefeatedElement]; 
[GlobalSettingsElement addChild:NewtonBossDefeatedElement]; 
[GlobalSettingsElement addChild:CatchExtenderElement]; 
[GlobalSettingsElement addChild:SpeedBoostElement]; 
[GlobalSettingsElement addChild:CatchExtenderEnabledElement]; 
[GlobalSettingsElement addChild:SpeedBoostEnabledElement]; 

GDataXMLDocument *document = [[[GDataXMLDocument alloc] 
           initWithRootElement:GlobalSettingsElement] autorelease]; 

NSData *xmlData = document.XMLData; 

NSString *filePath = [self dataFilePath:TRUE]; 
NSLog(@"Saving data to %@...", filePath); 
[xmlData writeToFile:filePath atomically:YES]; 
} 

@end 

편집 : 실제 문제는 여기에서 시작됩니다 내 메뉴 클래스에서

나는 경우 CatchExtender을 마련하고 (그들은 게임의 상점에서 구입)를 활성화 될 SpeedBoost 두 개의 스위치가 있습니다. 이러한 스위치에서 스위치에 따라 SpeedBoostEnabled 및 CatchExtenderEnabled를 설정하려고합니다. MY INIT IN

:

if (GlobalSettings.CatchExtender == TRUE) { 
     if(GlobalSettings.SpeedBoost == TRUE){ 
      catchSwitch = [[ UISwitch alloc ] initWithFrame: CGRectMake(220, 400, 10, 10)]; 
      catchSwitch.center = CGPointMake(240, 450); 

      CCLabelTTF *catchLabel = [CCLabelTTF labelWithString:@"Catch Extender" fontName:@"Chalkduster" fontSize:15]; 
      catchLabel.color = ccWHITE; 
      catchLabel.position = CGPointMake(240, 60); 
      [self addChild: catchLabel]; 
     }else{ 
      catchSwitch = [[ UISwitch alloc ] initWithFrame: CGRectMake(160, 400, 10, 10)]; 
      catchSwitch.center = CGPointMake(160, 450); 
      CCLabelTTF *catchLabel = [CCLabelTTF labelWithString:@"Catch Extender" fontName:@"Chalkduster" fontSize:15]; 
      catchLabel.color = ccWHITE; 
      catchLabel.position = CGPointMake(160, 60); 
      [self addChild: catchLabel]; 
     } 

     catchSwitch.on = NO; //set to be OFF at start 
     catchSwitch.tag = 1; // this is not necessary - only to find later 
     [catchSwitch addTarget:self action:@selector(catchAction:) forControlEvents:UIControlEventValueChanged]; 
     [[[CCDirector sharedDirector] openGLView] addSubview:catchSwitch]; 


    } 

    if (GlobalSettings.SpeedBoost == TRUE) { 
     if(GlobalSettings.CatchExtender == TRUE){ 
      speedSwitch = [[ UISwitch alloc ] initWithFrame: CGRectMake(100, 400, 10, 10)]; 
      speedSwitch.center = CGPointMake(80, 450); 
      CCLabelTTF *speedLabel = [CCLabelTTF labelWithString:@"Speed Enhancer" fontName:@"Chalkduster" fontSize:15]; 
      speedLabel.color = ccWHITE; 
      speedLabel.position = CGPointMake(80, 60); 
      [self addChild: speedLabel]; 
     }else{ 
      speedSwitch = [[ UISwitch alloc ] initWithFrame: CGRectMake(160, 400, 10, 10)]; 
      speedSwitch.center = CGPointMake(160, 450); 
     } 

     speedSwitch.on = NO; //set to be OFF at start 
     speedSwitch.tag = 1; // this is not necessary - only to find later 
     [speedSwitch addTarget:self action:@selector(speedAction:) forControlEvents:UIControlEventValueChanged]; 
     [[[CCDirector sharedDirector] openGLView] addSubview:speedSwitch]; 
    } 

Actions (작업) :

은 스위치입니다

- (void)catchAction:(id)sender 
{ 
// Your logic when the switch it used 
// NSLog(@"switchAction: value = %d", [sender isOn]); 
if ([sender isOn]) { 
    GlobalSettings *GlobalSettings = [SettingsParser loadData]; 
    GlobalSettings.CatchExtenderEnabled = TRUE; 

    UIAlertView* dialog = [[UIAlertView alloc] init]; 
    [dialog setDelegate:self]; 
    [dialog setTitle:@"ON"]; 
    [dialog setMessage:@"Catch Extender is on"]; 
    [dialog addButtonWithTitle:@"Sweet!"]; 
    [dialog show]; 
    [dialog release]; 
    [SettingsParser saveData:GlobalSettings]; 

}else{ 
    GlobalSettings *GlobalSettings = [SettingsParser loadData]; 
    GlobalSettings.CatchExtenderEnabled = FALSE; 
    UIAlertView* dialog = [[UIAlertView alloc] init]; 
    [dialog setDelegate:self]; 
    [dialog setTitle:@"OFF"]; 
    [dialog setMessage:@"Catch Extender is off"]; 
    [dialog addButtonWithTitle:@"Thanks"]; 
    [dialog show]; 
    [dialog release]; 
    [SettingsParser saveData:GlobalSettings]; 
} 
} 

- (void)speedAction:(id)sender 
{ 
// Your logic when the switch it used 
// NSLog(@"switchAction: value = %d", [sender isOn]); 
if ([sender isOn]) { 
    GlobalSettings *GlobalSettings = [SettingsParser loadData]; 
    GlobalSettings.SpeedBoostEnabled = TRUE; 

    UIAlertView* dialog = [[UIAlertView alloc] init]; 
    [dialog setDelegate:self]; 
    [dialog setTitle:@"ON"]; 
    [dialog setMessage:@"Speed Enhancer is on"]; 
    [dialog addButtonWithTitle:@"Sweet!"]; 
    [dialog show]; 
    [dialog release]; 
    [SettingsParser saveData:GlobalSettings]; 

}else{ 
    GlobalSettings *GlobalSettings = [SettingsParser loadData]; 
    GlobalSettings.SpeedBoostEnabled = FALSE; 
    UIAlertView* dialog = [[UIAlertView alloc] init]; 
    [dialog setDelegate:self]; 
    [dialog setTitle:@"OFF"]; 
    [dialog setMessage:@"Speed Enhancer is off"]; 
    [dialog addButtonWithTitle:@"Thanks"]; 
    [dialog show]; 
    [dialog release]; 
    [SettingsParser saveData:GlobalSettings]; 
    } 
} 

게임 수준에 대한 데이터를 보유하고있는 클래스에서, 나는이를 확인 일반적으로 간단한 if 문을 사용하여 불리언 소리를냅니다. 하지만 작동하지 않는 것 같습니다. 로그에서 XML 파일의 값이 변경된 것처럼 보이지 않기 때문에 설정이 저장되지 않는 것처럼 보입니다.

오랜 소식이지만 이번 호에 대해 사과드립니다. 좀 곤란 해.

답변

2

나는 전에적인 Cocos2D를 사용한 적이 있지만이이 같은 전형적인 iOS 앱에 대한 NSUserDefaults을 사용하는 것이 정확히 무엇처럼 보인다 ... [[NSUserDefaults standardUserDefaults] setBool:YES ForKey:@"Key"];

+0

을 내가 NSUserDefaults를 사용하는 것 같네요이 쉽게 NSUserDefaults를 사용하여 정수 및 기타 등등을 추가 할 때? 그리고 앱이 닫힐 때 NSUserDefaults가 데이터 저장에 얼마나 신뢰할 수 있습니까? 사람들이 게임에서 벌어들이는 통화와 같은 것을 저장하고 앱을 닫으면 잃어 버리는 것을 원하지 않습니다. @MaxGabriel – tyler53

+0

NSUserDefaults는 bool, float, 정수 및 double과 같은 프리미티브를 지원합니다. 또한 NSCoding 프로토콜을 구현하여 NSData로 해당 개체를 먼저 인코딩하면 개체를 저장할 수 있습니다. NSUserDefaults는 디스크에 저장된 내용을 자동으로 업데이트하지만 기본값을 설정 한 후에는 언제나'synchronize'를 호출하여이를 보장 할 수 있습니다. – MaxGabriel

+0

P. 이 모든 내용은 문서에서도 다루고 있습니다. -> https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html – borrrden

관련 문제