I have loaded several images into a view and set states for different objects. I would like to remove multiple objects at one time. How can I reference PC20 and PC21 and others with less code?
Currently I have this, but I assume there is a way to call out several Objectforkeys at once and set the state.
{
TransformViewP *state = [states objectForKey:#"PC20"];
[state removeFromSuperview];
state = nil;
}
{
TransformViewP *state = [states objectForKey:#"PC21"];
[state removeFromSuperview];
state = nil;
}
In this case you could use a list of keys and a loop
NSArray<NSString*>* statesToRemove = #[#"PC20",#"PC21"]; // Can keep adding more keys if you want
for(NSString* stateKey in statesToRemove)
{
TransformViewP *state = [states objectForKey:stateKey];
[state removeFromSuperview];
}
Also you do not need to explicitly state state = nil; because once you leave those curly braces that scope is left and state will be destroyed.
Related
I've searched other questions and can't seem to find a similar problem. Either I am something completely wrong or I am blind. But here goes the code:
#autoreleasepool {
NSMutableString *sense = [[NSMutableString alloc] init];
NSMutableArray *senses = [[NSMutableArray alloc] init];
....... other code which initializes rL and count/length .......
for (index=0;index<count;index++) {
for (j=0;j<length;j++) {
c = [rL characterAtIndex:j];
switch (c) {
case '.':
[senses addObject:sense];
[sense setString:#""];
break;
default:
[sense appendFormat:#"%c",c];
break;
}
}
}
}
When I do this, and iterate, in debug mode, I see that all objects in senses are same as whatever the last value of sense was.
what am I doing wrong?
"sense" is always the same object. It is a mutable string, so the contents can change, but it is always the same object. So senses will contain that single object, multiple times. You could instead use
[senses addObject:[sense copy]];
The immediate solution could be to change:
[senses addObject:sense];
to:
[senses addObject:[NSString stringWithString:sense]];
This will add unique instances instead of adding the same mutable string over and over.
But it appears you are splitting a string up using the "." characters as a delimiter.
There's an easier way:
NSArray *senses = [rl componentsSeparatedByString:#"."];
That's it - one line.
I have a cocos2d game setup which uses basic inheritance setup which is similar to this;
> Property (Base class)
> - Office : Property
> - Warehouse : Property
> - Bank : Property
All the properties live in an array listOfProperties and I am trying to print out each property in a NSLog, but I am not sure how to do it.
For example,
// Create an office
Office *o = [Office alloc] init];
[o setName:#"Some office"];
[city.listOfProperties addObject:o];
[o release];
// Debug output all the properties in the game
// city.listOfProperties in an array of Property objects
for (Property *prop in city.listOfProperties) {
// I want to print out all the different properties here
if ([prop isKindOfClass:[Office class]]==YES)
{
NSLog(#"Office");
NSLog(#"office.name = %#", prop.name); // Prop name does not work
}
} // next
The problem is that some properties do not share the same attributes. For example, an Office might have "floors" but a Warehouse has "capacity".
What I'm needing is to print out all the different properties, but I am not sure how to change the focus from the pointer prop to a pointer for the specific class (ie: Office).
I need them all to live in listOfProperties so that I can use it later on in a CCMenu and want to avoid splitting them up into seperate arrays which will be very hard for me to manage.
Is there a way to do this?
Thanks
Do typecasting like this.
for (Property *prop in city.listOfProperties) {
if ([prop isKindOfClass:[Office class]])
{
Office* officeObject = (Office*) prop;
NSLog(#"office.name = %#", officeObject.name);
}
if ([prop isKindOfClass:[Warehouse class]])
{
Warehouse* WarehouseObject = (Warehouse*) prop;
NSLog(#"Warehouse.name = %#", WarehouseObject.name);
}
if ([prop isKindOfClass:[Bank class]])
{
Bank* BankObject = (Bank*) prop;
NSLog(#"Bank.name = %#", BankObject.name);
}
}
You can have whatever variables you want to log. using name for example.
I've set up a model with a few string fields and a few array fields. The model saves as such:
- (void)saveLevel:(NSString*)level traps:(NSArray*)traps whirls:(NSArray*)whirls accels:(NSArray*)accels walls:(NSArray*)walls dest:(NSString*)dest jupiter:(NSString*)jupiter rating:(NSNumber*)pRating;
{
if (m_pMOC == nil)
// Retrieves the managed object context
NSManagedObject* pNewLevel = [NSEntityDescription insertNewObjectForEntityForName:#"Level" inManagedObjectContext:m_pMOC];
NSDate* pDate = [NSDate date];
// Must have these four attributes
[pNewLevel setValue:level forKey:#"Level_ID"];
[pNewLevel setValue:jupiter forKey:#"Ball"];
[pNewLevel setValue:pDate forKey:#"Creation_Date"];
[pNewLevel setValue:dest forKey:#"Destination"];
[pNewLevel setValue:pRating forKey:#"Rating"];
// Optional attributes
if ([traps count] != 0)
[pNewLevel setValue:traps forKey:#"Traps"];
if ([whirls count] != 0)
[pNewLevel setValue:whirls forKey:#"Whirls"];
if ([accels count] != 0)
[pNewLevel setValue:accels forKey:#"Accelerators"];
if ([walls count] != 0)
[pNewLevel setValue:walls forKey:#"Walls"];
NSError* pError;
if (![m_pMOC save: &pError])
// etc...
}
I have a DataManager class which handles the fetching/saving from/to Core Data, and inside of the Manager I've verified that when I fetch an entity, the arrays are fetched, but when it's returned to the class that's calling it, the arrays are nil (the strings, on the other hand, arrive just fine). Here's the fetch code and the code that parses out the returned value from the fetch:
- (NSArray*) getLevelWithID:(NSString*)level
{
NSEntityDescription *pEntityDescription = [NSEntityDescription entityForName:#"Level" inManagedObjectContext:m_pMOC];
NSFetchRequest *pRequest = [[[NSFetchRequest alloc] init] autorelease];
[pRequest setEntity:pEntityDescription];
NSPredicate* pPredicate = [NSPredicate predicateWithFormat:#"Level_ID == %#", level];
[pRequest setPredicate: pPredicate];
NSError* pError;
NSArray* pLevels = [m_pMOC executeFetchRequest:pRequest error:&pError];
if (pLevels == nil)
{
NSLog(#"Could not find the level with Level_ID = %#",level);
abort();
}
NSArray* pAccels = [pLevels valueForKey:#"Accelerators"];
NSArray* pTraps = [pLevels valueForKey:#"Traps"];
NSArray* pWalls = [pLevels valueForKey:#"Walls"];
NSArray* pWhirls = [pLevels valueForKey:#"Whirls"];
return pLevels;
}
I set a break point on the last four Arrays, and they have objects in them, but in the function that retrieves them (shown below), they are nil.
- (void) initLevel:(NSArray*)pLevelObjects
{
Level* pLevel = [pLevelObjects objectAtIndex:0];
NSString* pJupiter = pLevel.Ball;
NSString* pDest = pLevel.Destination;
NSArray* pAccels = [pLevel valueForKey:#"Accelerators"];
NSArray* pTraps = [pLevel valueForKey:#"Traps"];
NSArray* pWalls = [pLevel valueForKey:#"Walls"];
NSArray* pWhirls = [pLevel valueForKey:#"Whirls"];
... (other initialization of the level) ...
}
I'm perplexed by this. The string values are there, but the arrays are not. I tried using the dot notation originally (NSArray* pAccels = pLevel.Accelerators, etc), but with the same result.
Ideas?
EDIT: initLevel is called from the view controller:
- (void) startGameWithLevelID:(NSString*)pLevelID
{
NSLog(#"MyViewController - beginGameWithLevelID");
NSArray* pLevel = [[DataManager getDataManager] getLevelWithID:pLevelID];
if (pLevel == nil || [pLevel count] == 0)
{
NSLog(#"Didn't retrieve any level with id %#", pLevelID);
abort();
}
else
{
CGRect newFrame = makeScreen([UIScreen mainScreen].applicationFrame);
GameBoard* pGB = [[GameBoard alloc] initWithFrame: newFrame];
pGB.m_pMyVC = self;
[pGB initLevel: pLevel];
[self.view addSubview: (UIView*)pGB];
[pGB release];
}
}
EDIT: I've newly re-factored the arrays into separate entities with a to-one relationship with the Level; the level has a to-many relationship with these entities. I also changed the names to better adhere to conventions: all attributes and relationships now start with a lowercased letter. I've prefixed the attributes and relationships with a_ and r_ respectively. I'm getting an error when saving "-[NSCFString _isKindOfEntity:]: unrecognized selector sent to instance 0x4d83120". It isn't terribly helpful but I'll let you know when I find the issue. For now, the code for saving looks as such:
- (void)saveLevel:(NSString*)level traps:(NSArray*)traps whirls:(NSArray*)whirls accels:(NSArray*)accels walls:(NSArray*)walls dest:(NSString*)dest jupiter:(NSString*)jupiter rating:(NSNumber*)pRating;
{
if (m_pMOC == nil)
{ // Code to get the managed object context from the delegate
}
NSManagedObject* pNewLevel = [NSEntityDescription insertNewObjectForEntityForName:#"Level" inManagedObjectContext:m_pMOC];
NSManagedObject* pNewBall = [NSEntityDescription insertNewObjectForEntityForName:#"Ball" inManagedObjectContext:m_pMOC];
[pNewBall setValue:jupiter forKey:#"a_Bounds"];
NSManagedObject* pNewDest = [NSEntityDescription insertNewObjectForEntityForName:#"Dest" inManagedObjectContext:m_pMOC];
[pNewDest setValue:dest forKey:#"a_Bounds"];
NSDate* pDate = [NSDate date];
// Must have these four attributes
[pNewLevel setValue:level forKey:#"a_Level_ID"];
[pNewLevel setValue:pDate forKey:#"a_Creation_Date"];
[pNewLevel setValue:pRating forKey:#"a_Rating"];
[pNewLevel setValue:#"Bob Dole" forKey:#"a_CreatedBy"];
[pNewLevel setValue:pNewBall forKey:#"r_Ball"];
[pNewLevel setValue:pNewDest forKey:#"r_Dest"];
// Optional attributes
if ([traps count] != 0)
[[pNewLevel mutableSetValueForKey: #"r_Trap"] addObjectsFromArray: traps];
if ([whirls count] != 0)
[[pNewLevel mutableSetValueForKey: #"r_Whirl"] addObjectsFromArray: whirls];
if ([accels count] != 0)
[[pNewLevel mutableSetValueForKey: #"r_Accel"] addObjectsFromArray: accels];
if ([walls count] != 0)
[[pNewLevel mutableSetValueForKey: #"r_Wall"] addObjectsFromArray: walls];
NSError* pError;
if (![m_pMOC save: &pError])
{ // Error saving
}
else
{ // Successfully saved
}
}
Again, thanks for the help, sorry for the lengthy post but I'm hoping this will help other Core Data newbies (like me) learn something.
FINAL EDIT: Ok, so after refactoring, I finally found a solution to save the arrays as objects (though whether it's the most efficient way I'm not sure). Below is just the code for adding a bunch of "Traps". The array passed into the function is the bounds of the Trap, which will be stored. The relationship is then formed with the Level (note it doesn't have to be set by both the Trap and the Level, just one of them, since Core Data takes care of the other way).
if ([traps count] != 0)
{
for (NSString* pBounds in traps)
{
NSManagedObject* pNewTrap = [NSEntityDescription insertNewObjectForEntityForName:#"Trap" inManagedObjectContext:m_pMOC];
[pNewTrap setValue:pBounds forKey:#"a_Bounds"];
[pNewTrap setValue:pNewLevel forKey:#"r_Level"];
}
}
Possible Array Misuse
In your first snippet, you are calling valueForKey: on an NSArray, and the result is an array, because that code iterates over pLevels, passes your valueForKey: argument to each object inside and combines the values.
It is how NSArray implements Key-Value Coding.
In your second piece of code, where you first fetch one pLevel from the array, you perform your valueForKey: selector on an entity, not on an array. So the result is what's in that particular object.
Probably some of your entities have those fields, so a combined result from all of those has some values, but some particular ones do not.
Possible Core Data Misuse
You say that you keep an array in your Core Data entity. How do you do that? There isn't a special type to hold an array, so normally it has to be stored as binary data with performing encoding using [NSKeyedArchiver archivedDataWithRootObject:array]. It might be that your storing and fetching is incorrect because you're trying to store arrays in non-array fields.
Do you also experience any crashes?
I'm using the MKPlacemark class to populate a label with location specifics. When calling the AdministrativeArea property, the entire name of the US state is returned (e.g. West Virginia). Is there a way to return ONLY the initials (e.g. WV)?
Apple's docs for that property suggest that there's no real definition for what it can contain. Your best bet is probably to create a function to map from the full state name to the 2 letter code, and pass the result of the property through it before display. I would default to the original string if you don't get a match.
-(NSString *)codeFromState:(NSString *)state {
NSArray *map = [NSArray arrayWithObjects:#"Alabama",#"AL", #"Alaska",#"AK", ... #"Wyoming", #"WY", nil];
for (int i = 0; i <[map count]; i+=2) {
if ([state compare:[map objectAtIndex:i]] == NSOrderedSame) {
return [map objectAtIndex:i+1];
}
}
return state;
}
I'm working on a roguelike using Objective-C/Cocoa to learn more. I've gotten most of the basic functionality out of the way, but I still have one problem I've been trying to figure out.
Here's a breakdown of the process:
First, the map is loaded:
NSString* mapPath = [[NSBundle mainBundle] pathForResource:mapFileName ofType:mapFileType];
NSURL* mapURL = [NSURL fileURLWithPath: mapPath];
currentMap_ = [[Map alloc] initWithContentsOfURL: mapURL];
worldArray = [[NSMutableArray alloc] init];
itemArray = [[NSMutableArray alloc] init];
[self populateMap];
return;
Then, in the populateMap function, it goes through each cell of the loaded map, using NSPoints and a loop, and creates objects based on the data from the map in WorldArray. For items, normal floor is put in where the item is, and an item is then made in itemArray. Both arrays are 30x30, as determined by the height of the map.
Here is the populateMap code:
- (void)populateMap
{
NSPoint location;
for ( location.y = 0; location.y < [currentMap_ height]; location.y++ )
{
for ( location.x = 0; location.x < [currentMap_ width]; location.x++ )
{
char mapData = [currentMap_ dataAtLocation: location];
for ( GameObject *thisObject in worldDictionary )
{
//NSLog(#"char: <%c>", [thisObject single]);
if ( mapData == [thisObject single])
{
NSString* world = [thisObject className];
//NSLog(#"(%#) object created",thisObject);
[self spawnObject:world atLocation:location];
}
}
for ( Item *thisObject in itemDictionary )
{
//NSLog(#"char: <%c>", [thisObject single]);
if ( mapData == [thisObject single] )
{
NSString* item = [thisObject className];
NSString* floor = [NormalFloor className];
//NSLog(#"(%#) object created",thisObject);
[self spawnItem:item atLocation:location];
[self spawnObject:floor atLocation:location];
}
}
if ( mapData == '1'
&& [player_ stepsTaken] <= 0)
{
//NSLog(#"player spawned at (%f, %f)",location.x,location.y);
player_ = [[Player alloc] initAtLocation: location];
}
if ( mapData == '1' )
{
//NSLog(#"floor created at (%f, %f)",location.x,location.y);
[worldArray addObject:[[NormalFloor alloc] initAtLocation: location]];
}
}
}
[self setNeedsDisplay:YES];
}
This is what is called when things are spawned:
- (void)spawnObject: (NSString*) object atLocation: (NSPoint) location
{
//NSLog(#"(%#) object created",thisObject);
[worldArray addObject:[[NSClassFromString(object) alloc] initAtLocation: location]];
}
- (void)spawnItem: (NSString*) item atLocation: (NSPoint) location
{
//NSLog(#"(%#) object created",thisObject);
[itemArray addObject:[[NSClassFromString(item) alloc] initAtLocation: location]];
}
worldArray and itemArray are what the game works on from that moment onwards, including the drawing. The player is inside of worldArray as well. I'm considering splitting the player into another array of characterArray, to make it easier when I add things like monsters in the not so distant future.
Now, when I load a new level, I had first considered methods like saving them to data and loading them later, or some sort of savestate function. Then I came to the realization that I would need to be able to get to everything at the same time, because things can still happen outside of the player's current scope, including being chased by monsters for multiple floors, and random teleports. So basically, I need to figure out a good way to store worldArray and itemArray in a way that I will be able to have levels of them, starting from 0 and going onward. I do need a savestate function, but there's no point touching that until I have this done, as you shouldn't actually be allowed to save your game in roguelikes.
So to reiterate, I need to have one set of these arrays per level, and I need to store them in a way that is easy for me to use. A system of numbers going from 0-upward are fine, but if I could use something more descriptive like a map name, that would be much better in the long run.
I've figured out my problem, I'm using an NSMutableDictionary for each and storing them with the keys that correspond to each level. Works like a charm. Bigger problems elsewhere now.
I figured it out, I'm using NSMutableDictionaries, one for each array (objects, items, eventually characters). They're stored using the name of the level. Works like a charm.