Building Constructors for RPG game in Objective C - 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];
}

Related

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.

NSNumber constants in Obj-C

I want to make some NSNumber constants via the same style used for NSStrings in this topic. That is, I'm creating separate constants.h/.m files and importing them into classes that need to access them.
The trouble with doing this is that there isn't such a thing as a compile-time constant NSNumber. Only NSString gets that distinction. NSNumbers are always created dynamically. You can fake it by using a function that runs at your program's startup to initialize the variables. Your options:
Create a class with a +load method that performs the initialization.
In the file with the constants, include a function with __attribute__((constructor)). So, for example:
// Constants.m
NSNumber *someGlobalNumber;
__attribute__((constructor))
static void InitGlobalNumber() {
someGlobalNumber = [[NSNumber numberWithInteger:1] retain];
}
But of course then you can't reliably use these numbers in any other functions which are run that early in the startup process. This usually isn't a problem, but is worth keeping in mind.
The other option, which I've seen crop up a few times, is to have a class with accessors for the numbers instead of giving raw access to the variables. It's a bit of a heavier design, but it also feels less voodooish, which has its charms.
Unfortunately you cannot currently generate NSNumber constants in the same way you can generate NSString constants. When you try to do you will get a compiler error
NSNumber * const kNumberConstant = #2; // This doesn't work.
However, you can use primitives instead.
NSInteger const kSomeIntValue = 10;
You can basically achieve close to what you want in three parts:
.h file:
extern NSNumber *MyFirstConstant;
.m file
NSNumber *MyFirstConstant;
AppDelegate.m
+(void)initialize
{
MyFirstConstant = #5;
...
}
AppDelegate is guaranteed to run before any of your other code, and the initialize is the first method that would be called on AppDelegate, so you can essentially insure all your constants are setup for you before your app runs.
update:
Years later, I just realized it is possible to create a NSNumber constant for integers... but it's a hack:
#define CONST_INT_NSNUMBER( x ) ((__bridge NSNumber * const)(void * const)(( x << 8 ) | 0x27))
NSNumber * const number = CONST_INT_NSNUMBER(123) ;
This works because certain integer NSNumbers are stored as tagged pointers.
original answer:
You can't do it.
NSNumber * const mynumber = #5.5;
gives:
Initializer element is not a compile-time constant
Implying the compiler has a special feature specifically for creating compile-time constant NSString objects, but not any other type of object.
You could do this, however:
.h:
extern NSNumber * kConstantNumber ;
.m:
NSNumber * kConstantNumber ;
#implementation NSNumber (InitializeConstants)
+(void)load
{
kConstantNumber = #42;
// ... and the rest ...
}
#end

How to set retain for properties nested to struct?

There is really something in objc I cannot understand. I know how to make retainable properties but cannot understand how to make retainable "sub" struc fields which are not "public" properties but only "private" properties (the private properties are only managed by my methods).
Here is my code:
struct _device_descriptor {
NSInteger accessoryNumber; // Accessory the device belongs to
NSInteger slotNumber; // Slot number used for the device
NSString* slotName; // Slot name
};
typedef struct _device_descriptor device_descriptor_t;
#define NUMBER_MAX_CARD_READERS 10
#define NUMBER_MAX_ACCESSORIES 10
#interface CardDeviceManager : NSObject {
// Card devices (among accessories)
NSInteger m_numberOfCardDevices; // Number of devices.
NSMutableArray* m_accessoryList; // List of all accessories
#private
// Accessories
NSInteger m_numberOfAccessories; // Number of accessories
NSInteger m_numberOfAcceptedAccessories;
device_descriptor_t m_cardDevice[NUMBER_MAX_CARD_READERS]; // Array of card devices.
}
I want the slot name (slotName in the struct) to be retained each time I assign it in my methods, but this is not a property since not visible from outside.
For example, each time I initialize it with another NSString, I do this:
//_tmpName is a NSString
// Warning: slotName must be released later since we retain it.
m_cardDevice[i].slotName = [[NSString stringWithString: _tmpName] retain];
I really feel this is not a "good" (not elegant) way of doing.
I think I should remove the _device_descriptor struct and have something like this:
NSInteger accessoryNumber[NUMBER_MAX_CARD_READERS]; // Accessory the device belongs to
NSInteger slotNumber[NUMBER_MAX_CARD_READERS]; // Slot number used for the device
NSString* slotName[NUMBER_MAX_CARD_READERS];
But this is not better since I do not gather common things in a struct...
Is there a smarter way ?
Regards,
Franz
You're right; you can't do this via property syntax. I'd probably write C functions for getting and setting the struct fields, and then just make an arbitrary rule that accessing data from the structs in any way other than the functions is improper.
For example, I'd have:
device_descriptor_t* DeviceDescriptorCreate(NSInteger accessoryNumber,
NSInteger slotNumber,
NSString* slotName); //malloc()
void DeviceDescriptorDestroy(device_descriptor_t* device); //free()
void DeviceDescriptorSetSlotName(device_descriptor_t* device, NSString* slotName); //release old value, set new one
NSString* DeviceDescriptorGetSlotName(device_descriptor_t* device);
Et cetera.

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.

Constant NSDictionary/NSArray for class methods

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.