5

I've reviewed (and tried) a bunch of the threads here regarding Singletons and NSMutableArrays. I'm new to Objective-C so please bear with me.

I simply want to create a few arrays that can be accessed from any view/.m file.

What is the best (or most concise) coding for a Singleton?

Below is what I have now and I get 1 warning at .m '@implementation' - "Incomplete implementation" 1 error at usage in a view .m file - "initializer element is not a compile-time constant"

This is the code I have now - my GlobalData.h file:

#import <Foundation/Foundation.h>

@interface GlobalData : NSObject {    
    NSMutableArray *listOfHeadings;
    NSMutableArray *listOfItems1;
    NSMutableArray *listOfItems2;
}    
@property(nonatomic,retain)NSMutableArray *listOfHeadings;
@property(nonatomic,retain)NSMutableArray *listOfItems1; 
@property(nonatomic,retain)NSMutableArray *listOfItems2; 
+(GlobalData*)getInstance;  
@end

My GlobalData.m file:

#import "GlobalData.h"

@implementation GlobalData
@synthesize listOfHeadings;
@synthesize listOfItems1;
@synthesize listOfItems2;
static GlobalData *instance=nil; 

+(GlobalData *)getInstance    
{    
    @synchronized(self)    
    {    
        if(instance==nil)    
        {    
            instance= [GlobalData new];    
        }    
    }    
    return instance;    
}    
@end

And in a view .m file (simplified):

#import GlobalData.h

GlobalData *globDat=[GlobalData getInstance]; //error occurs here

Can someone point out the trouble and if there's better coding, please enlighten me - thanks!

EDIT

Here's a few links I've tried to use:

Can i have a single NSMutableArray in my multiple views application?

iPhone help with singleton class

Community
  • 1
  • 1
wayneh
  • 4,393
  • 9
  • 35
  • 70
  • Use Matt Galaghers [singleton skeleton](http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html). – Till Jan 19 '12 at 19:41

4 Answers4

8

In this case, you might be doing more than you have to. Granted this certainly isn't always the best solution - but you can put your NSMutableArray as a property in your App Delegate and then easily refer to it from any view. By doing it this way - you aren't locking it in as a 'singleton' but there is a 'singleton instance' of it (this helps a great deal for testability).

I have simplified this process here:

YourAppDelegate.h

@property (nonatomic,retain) NSMutableArray *myArray;

YourAppDelegate.m

@synthesize myArray;

YourViewController.m

YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *myArrayFromAppDelegate = appDelegate.myArray;

From this point - you can do any manipulation on this value.

dtuckernet
  • 7,817
  • 5
  • 39
  • 54
  • I've tried that a few times with no success. Here's the link http://stackoverflow.com/questions/8918396/how-to-share-a-variable-between-multiple-views-xibs – wayneh Jan 19 '12 at 19:18
  • In this case, if it isn't working - it is due to a coding error. It does work. – dtuckernet Jan 19 '12 at 19:24
  • I expanded on this sample above. – dtuckernet Jan 19 '12 at 19:27
  • OK - thanks - this worked. I'm confused about two things though: 1) The answer from Rob Mayoff above also worked - how do I give credit to both of you? 2) Where is the proper place to load data - the AppDelegate.m or the ViewController.m ? Thanks. – wayneh Jan 19 '12 at 19:48
  • You can upvote both answers - but have to pick one for the 'correct' answer. I would load data in the ViewController unless it is application-wide data - when it might make more sense in the App Delegate. – dtuckernet Jan 19 '12 at 19:51
  • OMG YOU ARE GOD YOU SAVED ME YOU ROCK MAN!! – chandhooguy Dec 24 '12 at 23:16
6

Here's the "modern" version of a single method to turn any class into a Singleton (in this case formatted as a code snippet). It works in iOS4.x or higher:

+(<#SingletonClassName#> *) sharedInstance 
{
    static <#SingletonClassName#> *_sharedClient = nil;

    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _sharedClient = [[self alloc] init];
    });

    return _sharedClient;
}

But, do you really need a singleton of a single NSMutableArray? You could use the built-on singleton - your application delegate, which is got to by calling:

MyAppDelegate * appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.myMutableArray addObject:...];
Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • Do I need a Singleton? I'm not sure - I've seen all kinds of threads and there is never agreement - some say it's better, others disagree... I've tried similar calling code to what you suggest with no luck. See this thread: http://stackoverflow.com/questions/8918396/how-to-share-a-variable-between-multiple-views-xibs – wayneh Jan 19 '12 at 19:22
  • I'm not talking so much about needing a singleton and choosing to create one instead of just using the already existing AppDelegate... though you could also have accessors for the mutable array as a class level method on a class. In the link you posted one of the answers recommends using a singleton for managing global variableS, as in plural... if you have just one array floating around, no harm is just placing it in the app delegate which everything can easily reach. – Kendall Helmstetter Gelner Jan 19 '12 at 22:36
  • @KendallHelmstetterGelner I'm almost in the same situation. I need to create an `NSMutableArray` (for dictionaries and strings), that i can use in 3-4 different view controllers. I started to create a singleton, but i'm not really sure, that it's the smartest idea. I've read a lot of opinion about storing data in the `AppDelegate` is bad, but after your comment i'm a little bit confused. What do you think, one array won't hurt nothing? I also have to able to change it's content dynamically, based on downloaded data. So far the `AppDelegate` version looks easier for me. – rihekopo Jul 02 '14 at 19:09
  • 1
    The reason why you might want a singleton is that you can then also have management and metadata functions related to the mutable array. But if truly all you need is the mutable array, less code is nice so putting it in the app delegate would not hurt. – Kendall Helmstetter Gelner Jul 02 '14 at 21:36
5

The error initializer element is not a compile-time constant is not related to how you create your singleton. The error is how you are accessing your singleton. You are doing this outside of a function:

GlobalData *globDat=[GlobalData getInstance];

This means that you are trying to initialize a global variable (globDat) as the value of the expression [GlobalData getInstance]. You can only initialize global variables to expressions that are "compile-time constants". That means things like 0 or "fred" or 8/2. The value of [GlobalData getInstance] cannot be computed at compile-time, so it cannot be used to initialize the global variable.

Instead, you need to just use [GlobalData getInstance] inside your function bodies wherever you are currently trying to use the globDat variable.

As for the warning, Incomplete implementation, I don't see what's missing. Perhaps you didn't post all of the code from GlobalData.h. Anyway, you should be able to click the warning (where it appears on the right side of the editor window) and have Xcode show you what's missing.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Thanks - you were right, I was not in a method. It works now. How do I share credit for the answer as dtuckernet below also provided a correct answer? – wayneh Jan 19 '12 at 19:49
  • You can only accept one answer per question. But you can upvote any number of answers. – rob mayoff Jan 19 '12 at 20:01
  • Sorry - to bug you, but now that I've got the Singleton working, it seems I can't modify my array! I don't get any compiler errors or warnings. My 'addObject' efforts don't seem to do anything. – wayneh Jan 19 '12 at 21:48
  • The code in your question doesn't actually create an NSMutableArray object for any of the GlobalData properties. Do you do that somewhere? – rob mayoff Jan 19 '12 at 22:07
  • I've moved this portion of my issue to a new question - please check here: http://stackoverflow.com/questions/8934083/unable-to-modify-array-in-singleton – wayneh Jan 19 '12 at 22:10
3

This is the way I create my Singleton:

Singleton.h

#import <Foundation/Foundation.h>

@interface Singleton : NSObject {
    NSMutableArray *firstMutableArray;
    NSMutableArray *secondMutableArray;
}

@property (nonatomic, retain) NSMutableArray *firstMutableArray;
@property (nonatomic, retain) NSMutableArray *secondMutableArray;

+ (id)sharedSingleton;

@end

Sigleton.m

#import "Singleton.h"

static Singleton *sharedMySingleton = nil;

@implementation Singleton

@synthesize firstMutableArray;
@synthesize secondMutableArray;

#pragma mark Singleton Methods

+ (id)sharedSingleton {
    @synchronized(self) {
    if (sharedMySingleton == nil) {
        sharedMySingleton = [[super allocWithZone:NULL] init];
    }
    return sharedMySingleton;
}

+ (id)allocWithZone:(NSZone *)zone {
    return [[self sharedSingleton] retain];
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)retain {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;
}

- (oneway void)release {
    // Never release
}

- (id)autorelease {
    return self;
}

- (id)init {
    if (self = [super init]) {
        firstMutableArray = [[NSMutableArray alloc] initWithObjects:nil];
        secondMutableArray = [[NSMutableArray alloc] initWithObjects:nil];
    }
    return self;
}

- (void)dealloc {
    [firstMutableArray release];
    [secondMutableArray release];
    [super dealloc];
}

@end

Then, when you want to call your Singleton:

#import "Singleton.h"

Singleton *singleton = [Singleton sharedSingleton];
singleton.firstMutableArray = ...
singleton.secondMutableArray = ...
Giuseppe Garassino
  • 2,272
  • 1
  • 27
  • 47
  • But you're using Singleton.h which is a user created class, correct? – wayneh Jan 19 '12 at 19:20
  • Yes, it is. If you have just few NSMutableArray you probably do not need a class to manage them. But I think a Singleton class is cleaner code: you can add as many objects as you like at any moment and it is reusable in any project. – Giuseppe Garassino Jan 19 '12 at 19:28
  • I'll have to figure this out...Singletons seem to take more initial code, but implement much easier. – wayneh Jan 19 '12 at 20:02
  • @Beppe if i have to access to the same `NSMutableArray` only in 3 or 4 `ViewControllers`, do you recommend this solution? I'm just wondering what will be the better, store it in the `AppDelegate` or in a singleton. – rihekopo Jul 02 '14 at 19:18
  • 1
    @vv88 Thinking at memory management, as long as I know both solutions are the same. Technically speaking, appDelegate is created at startup, while singletons at first use. What actually changes between this 2 methods it is the possibility for singleton to be easily reusable, and if you have a big project, its readability. – Giuseppe Garassino Jul 03 '14 at 11:44
  • @vv88 Anyway this code should be updated... Kendall's solution is now better than this! – Giuseppe Garassino Jul 03 '14 at 11:48