Creating global array & iterator - objective-c

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.

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.

Using an "extern" global before it is properly initialized

I'm trying to determine if there is an elegant solution to this issue.
Say I have a global defined in some header:
Constants.h:
extern NSString *someGlobal;
And then I wish to use this global in some other class:
Foo.m
NSString *localVariable = someGlobal;
This all works just fine if I initialize the global like this:
Constants.m:
NSString *someGlobal = #"Some String Literal";
But lets say I need to initialize the global to something that isn't a compile-time constant. In such cases I typically do this:
Constants.m:
#implementation Constants
+ (void)initialize {
someGlobal = ... // some non-trivial initialization
}
#end
Now I have a potential problem in Foo.m. If no reference has been made to the Constants class when I try to use someGlobal, the result is nil. A workaround is to do:
Foo.m (or in some app startup code):
[Constants class];
That will trigger the initialize method of the Constants class and someGlobal will be properly initialized. As long as this is done before any runtime use of someGlobal, things work fine.
Is there a better way to initialize extern globals with non-compile time constants without the need to call code such as [Constants class] at app startup?
A more idiomatic way in Objective-C is using a singleton instead of multiple globals. Here is how:
#interface Globals
#property (readwrite,nonatomic) NSString *myString;
#property (readwrite,nonatomic) int myInt;
+(Globals*) instance;
#end
+(Globals*) instance {
static dispatch_once_t once;
static Globals *inst;
dispatch_once(&once, ^{
inst = [[Globals alloc] init];
inst.myString = #"Some String Literal";
inst.myInt = 42;
});
return inst;
}
Now you can use your globals like this:
NSLog(#"Global string: %#", [Globals instance].myString);
NSLog(#"Global string: %d", [Globals instance].myInt);
No, there is no better way. Logically, if some piece of code must execute before a variable is intialized, you have to take steps to make sure that happens.
You could arrange the flow of your program's code so as to guarantee that the Constants class get initialized before any other piece of code executes which needs it. For example, by tweaking the order in which things are initialized in your program and following the order of code execution from main() on down to prove to yourself that it works. But short of that (and the safest thing in any case), you would use your technique to force it to be made valid right before you use it.
Like dasblinkenlight's answer, this may not be exactly what you are looking for but it's another approach.
I would make class methods that returns the value you are looking for like this:
+(NSString *)someConstant {
static NSString *constant;
if(constant == nil)
constant = //your initialization here;
return constant;
}
Then where you need to use it just call [Constants someConstant];
Other random thoughts:
A constant that isn't some compile time value isn't really what extern variables are for and this method insures that the variable is initialized every time you use it. The class using the constant has to know about your class anyway or it wouldn't have imported its header file

Large Hidden Constant in Objective C

I have a large constant (an NSString with 10^6 values). Because of its size I would like to declare it at the end of the source file (so I don't have to scroll through it every time I want to edit my code). Also because of its size I would like it to be a constant so I can load it at compile time instead of runtime. Also, because I do not want it accessible to outside users I do not want to declare it as extern in the header file.
I have it declared as a constant using the code below in the implementation file, however it is giving me a "Use of undeclared identifier 'hugeConstantString'" if I move it past the #end of the implementation (for obvious reasons).
NSString *const hugeConstantString = #"a_whooooooole_lotta_characters";
I've checked this out: Constants in Objective-C but it didn't tell me anything I didn't know already. Maybe my brain is fried, but: is there any way that I can define this huge constant AFTER my implementation and still have it accessible? Or if I declare it in another header file and import it, will it then be accessible to others?
Thanks!
I'm not sure such a large string is a good idea, but if you're going to use it, I suggest putting it in its own header file.
MyLongStringConstant.h
#define kLongString #"..."
MyClass.h
....
#import "MyLongStringConstant.h"
...
//Do something with kLongString
...
If you want to have it accessible in every file of your app, import the header inside your apps myApp_Prefix.pch file, which is imported into every file.
I am going to save the conversation of Why are you doing that and just post a simple solution for you. Thanks to Tommy in the comments here is a simpler version.
#import "LargeStringTest.h"
#implementation LargeStringTest
//Declare the string
static NSString *hugeConstantString;
- (id)init {
self = [super init];
if (self) {
NSLog(#"Large String %#", hugeConstantString);
}
return self;
}
//Place all other code here
//Assign the string
static NSString *hugeConstantString = #"a_whooooooole_lotta_characters";
#end

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];
}

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.