Constant NSDictionary/NSArray for class methods - objective-c

I am trying to code a global lookup table of sorts.
I have game data that is stored in character/string format in a plist, but which needs to be in integer/id format when it is loaded.
For instance, in the level data file, a "p" means player. In the game code a player is represented as the integer 1. This let's me do some bitwise operations, etc. I am simplifying greatly here, but trying to get the point across. Also, there is a conversion to coordinates for the sprite on a sprite sheet.
Right now this string->integer, integer->string, integer->coordinate, etc. conversion is taking place in several places in code using a case statement. This stinks, of course, and I would rather do it with a dictionary lookup.
I created a class called levelInfo, and want to define the dictionary for this conversion, and then class methods to call when I need to do a conversion, or otherwise deal with level data.
NSString *levelObjects = #"empty,player,object,thing,doohickey";
int levelIDs[] = [0,1,2,4,8];
// etc etc
#implementation LevelInfo
+(int) crateIDfromChar: (char) crateChar {
int idx = [[crateTypes componentsSeparatedByString:#","] indexOfObject: crateChar];
return levelIDs[idx];
}
+(NSString *) crateStringFromID: (int) crateID {
return [[crateTypes componentsSeparatedByString:#","] objectAtIndex: crateID];
}
#end
Is there a better way to do this? It feels wrong to basically build these temporary arrays, or dictionaries, or whatever for each call to do this translation. And I don't know of a way to declare a constant NSArray or NSDictionary.
Please, tell me a better way....

If you want an array to be available to all the code in your class, just declare it outside the #implementation context, and then initialize it in your class's +initialize method.
NSArray *levelObjects;
#implementation LevelInfo
+ (void) initialize
{
if (!levelObjects)
levelObjects = [[NSArray alloc]
initWithObjects:#"empty",#"player",#"object",#"thing",#"doohickey",nil];
}
// now any other code in this file can use "levelObjects"
#end

Declare it static so it only needs to be created once.

Related

Multidimensional array of NSInteger as instance variable

I'm attempting to understand Objective C arrays. What I want to do is create a multidimensional NSInteger instance array.
Does the code immediately below create an array equivalent to initiating an array and running a double for loop containing NSNull at each point?
#interface Builder: UIView {
NSInteger superDuperArray[5][4];
}
How do I add/replace specific data (NSIntegers) considering this is not an NSMutableArray? I need to do so elsewhere in the file, in several different methods.
The instance variable you've declared does indeed create a multidimensional array that is accessible from any method in the object. It does not, however, contain NSNulls, because that's an object and NSInteger is a primitive type. It contains 0 in every slot (all ivars are initialized to their appropriate zero-value at the object's allocation).
If you don't need to change the size of the array, you're pretty much done. You access the array inside your object just like you'd access an array anywhere else, by subscripting: superDuperArray[2][1], e.g.
Here's a full sample:
#import <Foundation/Foundation.h>
#interface Mutli : NSObject
- (NSInteger)numberAtRow:(NSInteger)row column:(NSInteger)col;
- (void)setNumber:(NSInteger)newNum atRow:(NSInteger)row column:(NSInteger)col;
#end
#implementation Mutli
{
NSInteger numbers[5][4];
}
-(void)setNumber:(NSInteger)newNum atRow:(NSInteger)row column:(NSInteger)col
{
numbers[row][col] = newNum;
}
- (NSInteger)numberAtRow:(NSInteger)row column:(NSInteger)col
{
return numbers[row][col];
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
Mutli * m = [Mutli new];
[m setNumber:10 atRow:2 column:1];
NSLog(#"%ld", [m numberAtRow:2 column:1]);
// This may not crash!
[m setNumber:10 atRow:100 column:34];
}
return 0;
}
N.B. That last line. There's no bounds checking inherent to primitive arrays. Accesses outside the bounds you've set may not crash or cause any immediately-noticeable problem. Instead, you'll get garbage for reads, and will corrupt memory when you write. (Technically, you're invoking Undefined Behavior.) You should really include bounds checking in your accessor methods.
Also, a style note: it's no longer good ObjC form to put ivars in an object's interface. Hide it in the #implementation block as I've done.
An NSInteger is simply a C 'int' (or 64-bit long on 64-bit platforms - the size is immaterial though).
So what you've declared is nothing but a C array, of integers. Since they're simple integers, there's no NSNull.
If you'd declared an NSArray (of NSArray for 2D) then you'd be looking at NSNull and NSNumber in the elements.

Passing an integer to a class, and then creating an array with a size of that integer [duplicate]

This question already has answers here:
declaring array of an object X with unknown size objective c
(2 answers)
Closed 8 years ago.
I have a class named Calculator. This class accepts a bunch of test scores, and will store each of them into an array. This array is called scoreArray.
I want to declare the array like this, but I'm having trouble with the scope:
int scoreArray[numTestScores];
If I put that code into the #implementation, it doesn't know what numTestScores is, because it hasn't been passed to the class yet.
And if I try to do this:
-(id)init:(int)numTestScores_
{
if (self = [super init])
{
int scoreArray[numTestScores_];
}
return self;
}
then the array gets created, but the rest of the class doesn't know what scoreArray is.
How can I make it so that scoreArray is created with length "numTestScores" and has the same scope as if I had put it in the implementation block?
Using a native C array is an unnecessary pain. I'd rather use a NSMutableArray, declaring it as a property.
#property (nonatomic, copy) NSMutableArray *scores;
NSMutableArray automatically manages its memory, so you don't need to declare its size in advance.
Just initialize it as
_scores = [NSMutableArray array];
and then add values to it
[self.scores addObject:#(aResult)]; //assuming that aResult is an integer expression
#(...) wraps the value in a NSNumber since NSArray can only hold objects.
To retrieve a score, you can do
int score = [self.scores[0] intValue];
VLAs (variable-length arrays) only work in contexts where... um... where they make sense. In this case, you will rather want to utilize dynamic memory allocation and an instance variable:
#interface MyClass: NSObject {
int *array;
size_t size;
}
// ... etc ...
- (id)initWithSize:(size_t)n
{
if (self = [super init]) {
size = n;
array = malloc(size * sizeof array[0]);
}
return self;
}
// free the allocated memory upon destruction
- (void)dealloc
{
// ...
free(array);
// ...
[super dealloc];
}
As to why it doesn't really make sense to use a variable-length array as an instance variable: instance variables are part of an object. If you declared a VLA inside an object, then the size of the instance would depend on its initialization. That is not something immediately easy to implement, and it is not the way the Objective-C runtime works. (I'm not saying it's impossible, but it would be very, very impractical.)
All classes have their instance size deduced at compile time (well, mostly... nowadays it's rather the initialization of the runtime system), and it can't be changed later. As a consequence, the size of an object cannot vary from initialization to initialization.
On the assumption that you're writing a properly contained object oriented class, the implementation of your set of scores is not important to the outside world. If that is indeed the case, don't create an int array, create an NSMutableArray instead (and if you want to pre-fill numTestScores_ entries to make things easier later, do that, but there shouldn't be a need for it really).
If you must have an array, you will have to allocate it dynamically by declaring scoreArray to be an int * and using malloc. Be careful here though -- you will have to create a dealloc method in your class to free() the array if it has been created.

Is it best to return NSArray or void and update self property?

I am working on a delegate class that controls several views, and find myself switching between updating properties in the delegate and returning values from methods. What is the proper way to do this?
-(NSArray)blah{
return myarray;
}
or
-(void)blah{
[self myarray:value]
}
--------------- Clarification of question below
if I have a helper method that converts an NSArray into a NSDictionary
should I call my helper method and expect a return of NSDictionary, or should I update a variable in memory and return void.
There's a case for each approach, depending on what you are really doing. The two choices are:
It is truly a helper method, that has use in many places in your application.
It is specific to a single class and the dictionary is a member of that class.
OPTION 1) If it is truly a helper method, I believe that you should return the NSDictionary from the method. I'm assuming it is newly allocated within that method.
In other words, prefer:
+ (NSDictionary *) dictFromArray:(NSArray *);
If it has utility outside of a single class, you could put it in a sensible class that collects related utility methods.
The alternative approach of passing in an empty dictionary to be filled is practiced in C because it creates symmetry around allocating and freeing and makes it clear who owns the memory.
In Objective-C, reference counting takes care of that, so you can avoid the extra code of allocating empty objects just to call the method.
For example:
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
dictFromArray(myArray, myDict);
When it comes to knowing who owns the object, you should stick to Objective-C conventions, where:
+ (NSDictionary *) dictFromArray:(NSArray *)array
returns an autorelease object, so the caller knows they need to retain it if they want to hold a reference.
OPTION 2) If the functionality is specific to a single class and that class has the dictionary as a member, then I would pass in the array, update the dictionary member variable using the array contents, and return void.
Something like:
- (void) setBlahFromArray:(NSArray *)array
The question is confusing as stated. If they are properties then you have accessor methods that usually include something like:
-(void) setMyValue: (NSString*) inNewValue;
-(NSString*) myValue;
but it seems like you are probably asking something else since these can be dynamically synthesized for you by the compiler... So try rephrasing the question and we'll try again to help.

Building Constructors for RPG game in Objective C

I have a character model class which has this structure:
#interface CharacterModel : NSObject
{
// parent of this character
CharacterModel *parentChar;
// basic details
NSString *fname, *sname, *nick;
NSString *char_type; // categories of characters: dwarf, etc
// health
int health;
// cash
double cash;
double graft;
// flags
bool is_cop, is_player, is_ai, is_playable;
// Skills
int skill_speed;
int skill_stamina;
int skill_aggr;
int skill_another;
int skill_somethingelse;
// Total = 100
// Hidden RPG skills
int corruption;
int greed;
// Rep skills
int reputation;
// Misc. flags
int active, picked, is_locked;
}
The problem are 2 things.
1) I would need to re-write this structure in the #property (nonotomic)... part of the .h file, and I would need to do it again for #synthesize part of the .m file
Is there a way to reduce the need to re-write stuff; can I put all this in a struct or something and then just #synthesize that?
2) The constructor will have a stupidly long function name.
I really, really do not want to be writing a constructor that has hundreds of variables/fields.
ie:
-(id)initCharacter:(NSString *)name, and every other class variable mentioned above ...
Is there are a way around this?
I was thinking of doing a NSMutableDictionary, but you would STILL need to write a constructor with every field you want somewhere.
Any help on this would be great.
Thanks
I really think you should take your design one step further. It is very inflexible to have explicitly defined all the skills and flags like that. Consider creating new classes called:
Skill
Attribute
Flag
Your character class will then have:
NSMutableArray* skills;
NSMutableArray* attributes;
NSMUtableArray* flags;
and obviously getters/setters and add/remove methods for each.
Not only will it make your class look neater but it will also save you a lot of typing.
If you can use the modern Objective-C runtime (available on the iPhone and for 64 bit OS X programs) you will have to write the property definitions twice. Just write the #property and the #synthesize parts, the instance variables will be created automatically. If you need to support the old runtime you just have to write it three times, there is nothing you can do about it.
To the constructor you probably should not pass values for every property. Initialize them to some sensible default values. Note that you don’t have to do anything if the default value is 0, 0.0, nil or NULL - alloc makes sure that all ivars are initialized to zero.
If you want to set all the properties from a NSDictionary you can use key-value-coding to set them instead of doing this manually:
for (NSString *key in dictionary) {
id value = [dictionary objectForKey: key];
[self setValue: value forKey: key];
}

Creating global array & iterator

I am attempting to load up an entire array of NSManagedObjects into an NSArray, then use an integer iterator to iterate through the array when a button is tapped. xCode seems to dislike declaring the integer and NSArray in the .h, then used throughout different methods in the .m.
I was wondering what the appropriate path an experienced developer would take in solving such a problem.
The flow would be:
1. Load data into array.
2. Set label using information at index 0. int i = 0;
3. User taps button; i++, retrieve element at index 1.
and so on until the end of the array, or the user stops tapping the button.
Edited:
This is the code that works, but I feel is incorrect:
XYZViewController.h
#interface XYZViewController : UIViewController <NSFetchedResultsControllerDelegate>{
int index;
}
XYZViewController.m
import "XYZViewController.h"
- (void)function1{
index = 0;
}
- (void)function2{
index++;
}
-(void)function3{
NSManagedObject *obj = [results objectAtIndex:index];
}
Is this actually correct? It works, but not elegant; not at all.
Did you declare the integer and NSArray in your .h file outside of a class? if so, it would be defined in every compilation module that includes that file, which results in multiple symbols at linking time => error.
Solution: If you need the NSArray / int only in one .m file, move them there. Otherwise declare them as extern in the .h, and define them in exactly 1 .m file, like this:
// 1.h
extern int myInt;
// 1.m
#include "1.h"
int myInt;
// Use myInt
// 2.m
#include "1.h"
// Use myInt
The code you wrote is correct since you want to keep the visibility of the variable as private as possible. In this case it seems like you only need this variable in the XYZViewController.m file. In fact, you may want to consider prefixing it with #private to make it even less visible to other units.