Static array in objective C - objective-c

I use the following code to create a public static array in C#
public class A{
public static array[] obj;
}
I have another class B.
From class B I call
A.ArrayName and I get the array I use in class A.
I wanted to know, what is the equivalent of this in objective C

There is no special syntax for this. You just define a class method to return the static array.
For example:
#implementation A // note this is in the implementation
static NSArray *array;
+ (NSArray *)array
{
if (!array)
array = [[NSArray alloc] init];
return array;
}
#end
Or for messier code, but slightly better performance (a good idea in a tight loop, but usually not worthwhile):
#implementation A
static NSArray *array;
+ (void)initialize // this method is called *once* for every class, before it is used for the first time (not necessarily when the app is first launched)
{
[super initialize];
array = [[NSArray alloc] init];
}
+ (NSArray *)array
{
return array;
}
#end
To access it from class B you just do:[A array]

I want to propose using a Category on NSArray. I changed your requirement a bit to use an NSMutableArray as shared object.
interface file:
#import <Foundation/Foundation.h>
#interface NSArray (StaticArray)
+(NSMutableArray *)sharedInstance;
#end
implementation file
#import "NSArray+StaticArray.h"
#implementation NSArray (StaticArray)
+(NSMutableArray *)sharedInstance{
static dispatch_once_t pred;
static NSMutableArray *sharedArray = nil;
dispatch_once(&pred, ^{ sharedArray = [[NSMutableArray alloc] init]; });
return sharedArray;
}
#end
Now you can use it as:
[[NSArray sharedInstance] addObject:#"aa"];
[[NSArray sharedInstance] addObject:#"bb"];
[[NSArray sharedInstance] addObject:#"cc"];
and somewhere else:
NSLog(#"%#", [NSArray sharedInstance]);

Related

Singleton How to Declare NSMutableArray

I am trying to implement a singleton which contains an NSMutableArray* what I've done so far is below.
Singleton.h
#import <Foundation/Foundation.h>
#interface Singleton : NSObject{
NSMutableArray* ClassArray;
}
#property NSMutableArray* ClassArray;
+(Singleton*) getInstance;
- (void) SetClassArray:(NSMutableArray *)InputClassArray;
- (NSMutableArray*) GetClassArray;
Singleton.m
#import "Singleton.h"
#implementation Singleton
#synthesize ClassArray;
static Singleton *singletonInstance;
+ (Singleton*)getInstance{
if (singletonInstance == nil) {
singletonInstance = [[super alloc] init];
}
return singletonInstance;
}
- (void) SetClassArray:(NSMutableArray *)InputClassArray{
ClassArray = InputClassArray;
}
- (NSMutableArray*) GetClassArray{
return ClassArray;
}
#end
what I'm wondering is where does [[NSMutableArray alloc]init]; go? or do i not need it. Also when ClassArray is initialised i want some default values, (the return from another function,[DatabaseFunctions GetClassDefaultArray] ) again where does this go?. I'm assuming the line below singletonInstance = [[super alloc] init]; however it doesn't accept ClassArray.
I have an application consisting of four view controllers, what I need is an nsmutablearray that can be accessed and written to from any of these views. There are three others in total.
Thanks
I'm wondering is where does [[NSMutableArray alloc]init]; go? or do i not need it
That depends on what you expect in your getter. Is reasonable for your use case to return nil in GetClassArray?
You could guard against it by returning an empty array if it is nil:
- (NSMutableArray*) GetClassArray{
if ( ! ClassArray ) {
ClassArray = [[NSMutableArray alloc] init];
}
return ClassArray;
}
i want some default values
You can populate your array as you see fit whenever you want, the best place to start would probably be your init method:
+ (Singleton*)getInstance{
if (singletonInstance == nil) {
singletonInstance = [[super alloc] init];
NSMutableArray *array = ...;
[singletoneinstance SetClassArray:array];
}
return singletonInstance;
}
As a side note, I would recommend reading up on some naming conventions so you don't mixed up between class names and variable names in the future.

Still confused by Objective C variable scope

I am trying to get some employee data from a JSON service. I am able to get the data and load it into an NSMutableArray, but I cannot access that array outside the scope of the method that gets the data.
TableViewController h filed
#import <UIKit/UIKit.h>
#import "employee.h"
#interface ViewController : UITableViewController
{
//NSString *test;
//NSMutableArray *employees;
}
#end
And here is my m file:
#define kBgQueue dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define scoularDirectoryURL [NSURL URLWithString: #"https://xxxx"]
#import "ViewController.h"
#interface ViewController()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
scoularDirectoryURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSMutableArray *jsonArray = [NSJSONSerialization JSONObjectWithData: responseData options: NSJSONReadingMutableContainers error: &error];
id jsonObject = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSMutableArray *employees = [[NSMutableArray alloc ]init];
if (!jsonArray) {
} else {
for (jsonObject in jsonArray){
employee *thisEmployee = [employee new];
thisEmployee.fullName = [jsonObject objectForKey:#"$13"];
thisEmployee.state = [jsonObject objectForKey:#"state"];
thisEmployee.city = [jsonObject objectForKey:#"city"];
[employees addObject:thisEmployee];
}
}
}
Any help would be appreciated.
Bryan
You were on the right track. All you have to do is uncomment the NSMutableArray declaration in your #interface, and then change this line:
NSMutableArray *employees = [[NSMutableArray alloc] init];
to this
employees = [[NSMutableArray alloc] init];
Declaring the array in your interface will allow it to be accessed from anywhere within your implementation, or even from other classes and files if you declare it as a public property. When you make a declaration inside a function, that variables scope does not extend to outside of the function.
Just to elaborate a little on the scope of the variables, you have several ways of declaring them. The most used are:
Instance variables, which are declared in your interface and they can be accessed from any method inside the class or inside any method from it's subclasses. For example:
#interface MyObject : NSObject { //this can be any class
NSString *instanceVariable;
}
#implementation MyObject
-(void)someStrangeMethod {
instanceVariable = #"I'm used here";
NSLog(#"%#",instanceVariable);
}
//from subclasses
#interface MySubclassObject: MyObject {
//see that the variable is not declared here;
}
#implementation MySubclassObject
-(void)anotherStrangeMethod {
[super someStrangeMethod]; // this will print the value "I'm used here"
instanceVariable = #"I'm changing my value here"; //here we access the variable;
}
If you want the instance variable to be accessed only from the "owner" class you can declare it after the #private tag. You also have the #protected tag, though that isn't used so much.
If you want to have a variable that can be accessed outside the class, declare it as a property in your interface.
Also you can make the properties private using #private but this will contradict the purpose of the properties.

Checking NSMutableArray for 0 elements causes a crash; why?

I have declared a NSMutableArray as a singleton; when I try to check for the array count, the app crashes! Here is the code:
// clear array that holds selected servcies
SingletonArrayOfSelectedRows *arrayOfSelectedRows = [SingletonArrayOfSelectedRows sharedArrayOfSelectedRows];
if([arrayOfSelectedRows count] > 0)
[arrayOfSelectedRows removeAllObjects];
This code is the same code I have found all over SO and Google. Using XCode5, I have checked to make sure the singleton is allocated (and it is), and there is a valid count (0) for the singleton.
UPDATE
Here is the code for the singleton.h file:
#interface SingletonArrayOfSelectedRows : NSMutableArray {
}
#property (nonatomic, retain) NSMutableArray *arrayOfSelectedRows;
+ (id)sharedArrayOfSelectedRows;
#end
Here is the code for the singleton.m file:
#implementation SingletonArrayOfSelectedRows {
}
#synthesize arrayOfSelectedRows; // rename
// sharedSelectedCellIndexes
+ (id)sharedArrayOfSelectedRows {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-(id) init {
self = [super init];
if (self) {
arrayOfSelectedRows = [[NSMutableArray alloc] init];
}
return self;
}
#end
Don't subclass NSMutableArray to do this. NSMutableArray is a class cluster. All of the actual array implementation is inside subclasses of NSMutableArray. If you subclass NSMutableArray then your subclass won't actually implement any array behavior unless you write it yourself.
According to the documentation :
Any subclass of NSArray must override the primitive instance methods count and objectAtIndex:.
Since you are subclassing NSMutableArray you will need to override the following NSMutableArray primitive methods as well:
insertObject:atIndex:
removeObjectAtIndex:
addObject:
removeLastObject
replaceObjectAtIndex:withObject:

Static variable in superclass

I have two classes which inherit from the same class. Each class has a corresponding JSON file with the the same name as the class. To avoid loading the JSON every time an instance is created, I added a class method and static variable:
static NSArray *map = nil;
+(NSArray *)map {
if (!map) {
map = [NSJSONSerialization JSONObjectWithData:
[NSData dataWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:NSStringFromClass([self class])
ofType:#"json"]]
options:0
error:nil];
}
return map;
}
I added this method (literally copied and pasted) to both subclasses.
I'd like to move this up to the superclass, however if I do then the static variable will be shared between instances of both subclasses, and only the JSON map corresponding to the class that has an instance created first will be load and all subsequent instances of the other class will be returned the wrong map.
So how can I load the corresponding JSON file only once for each subclass and each subclass has its own map? (Ideally without copying and pasting code as I have)
In the base class keep a static NSMutableDictionary. Use as a key the name of the class (ie with NSStringFromClass(childClass)).
#interface BaseClass : NSObject
+(NSArray*)map;
#end
#interface OneChild : BaseClass
#end
#interface TwoChild : BaseClass
#end
#implementation BaseClass
+(NSArray*)map
{
static NSMutableDictionary *_mapStore;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_mapStore = [[NSMutableDictionary alloc]init];
});
NSString *name = NSStringFromClass([self class]);
NSArray *map = [_mapStore objectForKey:name];
if(map == nil)
{
map = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:#"json"]] options:0 error:nil];
[_mapStore setObject:map forKey:name];
}
return map;
}
#end
#implementation OneChild
#end
#implementation TwoChild
#end
IMO, the cleanest way to do this is to declare the static variable in each subclass, like you already are. It is duplication of the code that loads the map, but the map needs to be different for each class scope so I don't find that to be too much of an inconvenience.
If you really want to put the loading logic and storage in the superclass, make the static variable a dictionary rather than just a array, like this:
static NSMutableDictionary *maps = nil;
+(NSArray *)map {
if (!maps) {
maps = [[NSMutableDictionary alloc] initWithCapacity:2];
}
if (![maps objectForKey:[self class]]) {
[maps setObject:[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:NSStringFromClass([self class]) ofType:#"json"]] options:0 error:nil] forKey:[self class]];
}
return [maps objectForKey:[self class]];
}

Save / Write NSMutableArray of objects to disk?

Initially I thought this was going to work, but now I understand it won't because artistCollection is an NSMutableArray of "Artist" objects.
#interface Artist : NSObject {
NSString *firName;
NSString *surName;
}
My question is what is the best way of recording to disk my NSMutableArray of "Artist" objects so that I can load them the next time I run my application?
artistCollection = [[NSMutableArray alloc] init];
newArtist = [[Artist alloc] init];
[newArtist setFirName:objFirName];
[newArtist setSurName:objSurName];
[artistCollection addObject:newArtist];
NSLog(#"(*) - Save All");
[artistCollection writeToFile:#"/Users/Fgx/Desktop/stuff.txt" atomically:YES];
EDIT
Many thanks, just one final thing I am curious about. If "Artist" contained an extra instance variable of NSMutableArray (softwareOwned) of further objects (Applications) how would I expand the encoding to cover this? Would I add NSCoding to the "Applications" object, then encode that before encoding "Artist" or is there a way to specify this in "Artist"?
#interface Artist : NSObject {
NSString *firName;
NSString *surName;
NSMutableArray *softwareOwned;
}
#interface Application : NSObject {
NSString *appName;
NSString *appVersion;
}
many thanks
gary
writeToFile:atomically: in Cocoa's collection classes only works for property lists, i.e. only for collections that contain standard objects like NSString, NSNumber, other collections, etc.
To elaborate on jdelStrother's answer, you can archive collections using NSKeyedArchiver if all objects the collection contains can archive themselves. To implement this for your custom class, make it conform to the NSCoding protocol:
#interface Artist : NSObject <NSCoding> {
NSString *firName;
NSString *surName;
}
#end
#implementation Artist
static NSString *FirstNameArchiveKey = #"firstName";
static NSString *LastNameArchiveKey = #"lastName";
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
firName = [[decoder decodeObjectForKey:FirstNameArchiveKey] retain];
surName = [[decoder decodeObjectForKey:LastNameArchiveKey] retain];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:firName forKey:FirstNameArchiveKey];
[encoder encodeObject:surName forKey:LastNameArchiveKey];
}
#end
With this, you can encode the collection:
NSData* artistData = [NSKeyedArchiver archivedDataWithRootObject:artistCollection];
[artistData writeToFile: #"/Users/Fgx/Desktop/stuff" atomically:YES];
Take a look at NSKeyedArchiver. Briefly :
NSData* artistData = [NSKeyedArchiver archivedDataWithRootObject:artistCollection];
[artistData writeToFile: #"/Users/Fgx/Desktop/stuff" atomically:YES];
You'll need to implement encodeWithCoder: on your Artist class - see Apple's docs
Unarchiving (see NSKeyedUnarchiver) is left as an exercise for the reader :)