How do a populate an NSArray const? Or more generically how can I fix my code below to have an array constant (created in Constants.h & Constants.m) to be available to other parts of my code.
Was hoping to be able to access the constant as a static type object (i.e. as opposed to having to create an instance of constants.m and then access it) is this is possible.
I note the approach works OK for a string, but for NSArray the issue is populating the array.
Code:
constants.h
#interface Constants : NSObject {
}
extern NSArray * const ArrayTest;
#end
#import "Constants.h"
#implementation Constants
NSArray * const ArrayTest = [[[NSArray alloc] initWithObjects:#"SUN", #"MON", #"TUES", #"WED", #"THUR", #"FRI", #"SAT", nil] autorelease];
// ERROR - Initializer element is not a compile time constant
#end
The standard approach is to supply a class method that creates the array the first time it is requested and thereafter returns the same array. The array is never released.
A simple, example solution is this:
/* Interface */
+ (NSArray *)someValues;
/* Implementation */
+ (NSArray *)someValues
{
static NSArray *sSomeValues;
if (!sSomeValues) {
sSomeValues = [[NSArray alloc]
initWithObjects:/*objects*/, (void *)nil];
}
return sSomeValues;
}
You can of course fancy this up with GCD instead of using an if:
/* Implementation */
+ (NSArray *)someValues
{
static NSArray *sSomeValues;
static dispatch_once_t sInitSomeValues;
dispatch_once(&sInitSomeValues, ^{
sSomeValues = [[NSArray alloc]
initWithObjects:/*objects*/, (void *)nil];
});
return sSomeValues;
}
Related
I had declared constant NSArray like the below way.
static NSArray* const myArray = [NSArray arrayWithObjects:#"obj1",#"obj2",#"ibj3",#"obj4",#"obj5",nil];
But it shows an error "Initiliser elements are Not Compile time constants".
So I declared like this.But I got the error.
Please help This Case.
Thanks.
Since NSArrays are heap-allocated objects, you cannot create them in a static context. You can declare the variables, and then initialize them in a method.
So, you can do this way :-
static NSArray *myStaticArray = nil;
- (void) someMethod
{
if (myStaticArray == nil)
{
myStaticArray = [[NSArray alloc] init...];
}
}
Hope it helps you..
I don't see why you cannot use this to solve your problem. it is 100% constant, because the NSArray is immutable, and it is also 100% static.
GlobalConstant.h file:
#import <Foundation/Foundation.h>
// EDITED : __unused static NSArray *_myGlobalArray;
#interface GlobalConstant : NSObject
+ (NSArray *)myGlobalArray;
#end
GlobalConstant.m file:
#import "GlobalConstant.h"
#implementation GlobalConstant
+ (NSArray *)myGlobalArray {
static NSArray *_myGlobalArray = nil; // EDITED
#synchronized (_myGlobalArray) {
if (_myGlobalArray == nil) {
_myGlobalArray = [NSArray arrayWithObjects:#"1", #"2", nil]; // here is your array...
}
return _myGlobalArray;
}
}
#end
...and your array will be available in every class which includes the GlobalConstant.h file, you can reach the array like
NSArray *_globalArray = [GlobalConstant myGlobalArray];
To create the static array you can make use of the initialize method. This is a special class method that every class has. It is called the first time a class is referenced in any way and before any other code in the class is executed.
static NSArray* myArray = nil;
#implementation SomeClass
+ (void)initialize {
if (self == [SomeClass class]) {
myArray = #[ #"obj1", #"obj2", #"obj3", #"obj4", #"obj5" ];
}
}
// rest of methods
#end
and, to save typing, you can use NSArray* myArray = #[ #"obj1", #"obj2", #"ibj3", #"obj4", #"obj5" ];
I have an API class, lets call it MyClass, which I want to be available thruout my whole application. Hence I put the #import into MyProject-Prefix.pch file.
Now, when I (in my appDelegate) initiate the class, I get the error message cannot init a class object..
I understand that I am initiating my class incorrectly, but I have no idea on how I should do it... and since this is not common for development (as I see it) there ain't a lot of information to find thrue google (or I am searching wrong ;) )
So, I have two questions:
Any links for called "Creating Objective-C API's for Dummys"? ;)
Having a quick look at my code, what am I doing wrong?
This is my class:
MyClass.h
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>
#import "NSStringAdditions.h"
#import "XMLRPCResponse.h"
#import "XMLRPCRequest.h"
#import "XMLRPCConnection.h"
/**
* END required libs
*/
extern int EID;
extern NSString * SHARED_SECRET;
extern NSString * MODE;
#interface MyClass : NSObject
+ (id)initWithEid:(int)eid secret:(NSString*)secret mode:(NSString*)mode;
+ (NSArray*)web_invoice_infoWithOCR:(NSString*)ocr pno:(NSString*)pno;
+ (NSString*)the_digest:(NSString*)source;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
int EID;
NSString * SHARED_SECRET;
NSString * MODE;
NSString * URL_LIVE;
NSString * URL_BETA;
#pragma mark init / dealloc
+ (id)init {
self = [super init];
if (self)
{
}
return self;
}
+ (id)initWithEid:(int)eid secret:(NSString*)secret mode:(NSString*)mode
{
self = [super init];
if (self)
{
if (![[mode lowercaseString] isEqualToString:#"beta"] && ![[mode lowercaseString] isEqualToString:#"live"])
{
#throw ([NSException exceptionWithName:#"Invalid mode" reason:[NSString stringWithFormat:#"Invalid mode '%#' selected. Should either be 'beta' or 'live'", mode] userInfo:nil]);
}
EID = eid;
SHARED_SECRET = secret;
MODE = [mode lowercaseString];
}
return self;
}
+ (NSArray*)web_invoice_infoWithOCR:(NSString*)ocr pno:(NSString*)pno {
NSArray *params = [NSArray arrayWithObjects:ocr, EID, pno, [MyClass the_digest:[NSString stringWithFormat:#"%d:%#:%#", EID, ocr, SHARED_SECRET]], nil];
NSLog(#"Array: %#", params);
return params;
}
- (id)xmrpc_call_function:(NSString*)method parameters:(NSArray*)params
{
// Not finished yet, Warning exists
}
[...]
Having a look at my code, you'll notice the +(id)init function. I have tried -(id)init, -(id)initialize, +(id)initialize, +(void)initialize, -(void)initialize.
This is how I "load" my class in my AppDelegate:
[MyClass initWithEid:1234 secret:#"1234" mode:#"BETA"];
EDIT
I am trying to initiate my class the same way e.g. Flurry does. Example:
[FlurryAnalytics startSession:#"1234"];
You need to alloc it first:
MyClass *myClass = [[MyClass alloc] initWithEid:1234 secret:#"1234" mode:#"BETA"];
How do I build up a global like integer array?
I tried variations of the following:
#interface
int *iArray; //this space will vary depending upon need in the implementation
#implementation
...
int iArrayInit[4] = {1,2,3,4};
iArray = iArrayInit;
-bottom line: I need to keep index values in array that I can access easily, and use of [NSArray intValue] maybe to slow.
thanks
If it needs to be static you can declare an NSMutableArray as static in the implementation file and expose static methods to access it. When using an NSArray the values need to be of type id which NSNumber can do. Here is an example which currently is not thread safe.
//.h file
#interface Foo : NSObject
{
}
+(NSArray*)iArray;
+(void)addiArrayValue:(NSNumber*)value;
#end
//.m file
#implementation Foo
static NSMutableArray *_iArray;
+(void)initialize
{
if([Foo class] == self)
{
_iArray = [[NSMutableArray alloc] init];
}
}
+(NSArray*)iArray
{
return [[_iArray copy] autorelease];
}
+(void)addiArrayValue:(NSNumber*)value
{
[_iArray addObject:value];
}
#end
//Use
[Foo addiArrayValue:[NSNumber numberWithInt:10]];
[Foo addiArrayValue:[NSNumber numberWithInt:12]];
NSLog(#"%#", [Foo iArray]);
I'm storing a bunch of data in a .plist file (in the application documents folder), and it's structured like this:
Dictionary {
"description" = "String Value",
"sections" = Array (
Array (
Number,
...
Number
),
Array (
Number,
...
Number
)
),
"items" = Array (
Array (
Number,
...
Number
),
Array (
Number,
...
Number
)
)
}
If I just retrieve it with
NSMutableDictionary *d = [[NSMutableDictionary alloc] initWithContentsOfFile:plistFile]
I won't be able to replace the number objects, correct?
So I'm recursing through the data right now and forming a mutable version of the whole thing, and it worked in one instance, but now it's telling me mutating method sent to immutable object when the whole thing is mutable.
Is there an easier/better way to do this? If it makes a difference, my data is just integers and booleans.
Instead of writing all that custom class junk, you should use NSPropertyListSerialization. Specifically, see the propertyListWithData:options:format:error: method. Example usage:
NSMutableDictionary *d = [NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfFile:#"path/to/file"]
options:NSPropertyListMutableContainers
format:NULL
error:NULL];
This will make all the containers mutable, but keep the leaf nodes (e.g. NSStrings) immutable. There's also an option to make the leaves mutable too.
I usually find it easier to create one or more custom classes to handle loading and saving. This lets you convert the arrays to mutableArrays explicitly:
MyThing.h
#interface MyThing : NSObject
{
NSString * description;
NSMutableArray * sections;
NSMutableArray * items;
}
#property (copy) NSString * description;
#property (readonly) NSMutableArray * sections;
#property (readonly) NSMutableArray * items;
- (void)loadFromFile:(NSString *)path;
- (void)saveToFile:(NSString *)path;
#end
MyThing.m
#implementation MyThing
#synthesize description;
#synthesize sections
#synthesize items;
- (id)init {
if ((self = [super init]) == nil) { return nil; }
sections = [[NSMutableArray alloc] initWithCapacity:0];
items = [[NSMutableArray alloc] initWithCapacity:0];
return self;
}
- (void)dealloc {
[items release];
[sections release];
}
- (void)loadFromFile:(NSString *)path {
NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile:path];
[self setDescription:[dict objectForKey:#"description"]];
[sections removeAllObjects];
[sections addObjectsFromArray:[dict objectForKey:#"sections"]];
[items removeAllObjects];
[items addObjectsFromArray:[dict objectForKey:#"items"]];
}
- (void)saveToFile:(NSString *)path {
NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
description, #"description",
sections, #"sections",
items, #"items",
nil];
[dict writeToFile:path atomically:YES];
}
#end;
With that done, you can encapsulate all of the packaging and unpackaging code in your loadFromFile and saveToFile methods. The major benefit of this approach is that your main program gets a lot simpler, and it allows you to access the elements of your data structure as properties:
MyThing * thing = [[MyThing alloc] init];
[thing loadFromFile:#"..."];
...
thing.description = #"new description";
[thing.sections addObject:someObject];
[thing.items removeObjectAtIndex:4];
...
[thing saveToFile:#"..."];
[thing release];
What you want is a deep mutable copy. Cocoa doesn't include a way to do it. A few people have written such deep-copy implementations before (example).
However, Core Foundation includes the CFPropertyList API, which does have support both for creating deep mutable copies of property list objects as well as reading in property lists from disk as mutable datatypes. (And, of course, Core Foundation's property list types are toll-free bridged with Cocoa's, meaning you don't have to convert between them — an NSArray is a CFArray and vice-versa.)
In Java, it would look like this:
class Foo
{
float[] array;
}
Foo instance = new Foo();
instance.array = new float[10];
You can just use a pointer:
float *array;
// Allocate 10 floats -- always remember to multiple by the object size
// when calling malloc
array = (float *)malloc(10 * sizeof(float));
...
// Deallocate array -- don't forget to do this when you're done with your object
free(array);
If you're using Objective-C++, you could instead do:
float *array;
array = new float[10];
...
delete [] array;
Here's another way to do it. Create a NSMutableArray object and add NSNumber objects to it. It's up to you to decide whether or not this is sensible.
NSMutableArray *array;
array = [[NSMutableArray alloc] init];
[array addObject:[NSNumber numberWithFloat:1.0f]];
[array release];
Another way to do this in Objective-C is to use indexed instance variables:
#interface ArrayOfFloats : NSObject {
#private
NSUInteger count;
float numbers[0];
}
+ (id)arrayOfFloats:(float *)numbers count:(NSUInteger)count;
- (float)floatAtIndex:(NSUInteger)index;
- (void)setFloat:(float)value atIndex:(NSUInteger)index;
#end
#implementation ArrayOfFloats
+ (id)arrayOfFloats:(float *)numbers count:(NSUInteger)count {
ArrayOfFloats *result = [NSAllocateObject([self class], count * sizeof(float), NULL) init];
if (result) {
result->count = count;
memcpy(result->numbers, numbers, count * sizeof(float));
}
return result;
}
...
#end
For more see the documentation for NSAllocateObject(). A limitation of indexed instance variables is that you can't subclass a class that uses them.