-1

The goal, to create a class which contains an array of data to be used throughout the application by other classes.

I have this GlobalObject.h
It declares the array to be used to store the data.

#import <Foundation/Foundation.h>

@interface GlobalObjects : NSObject
@property (retain) NSMutableArray *animals;


-(id)init;
@end

I have this GlobalObject.m.
It contains the NSDictionary data and stores in to the array.

#import <Foundation/Foundation.h>

@interface GlobalObjects : NSObject
@property (retain) NSMutableArray *animals;


-(id)init;
@end


#import "GlobalObjects.h"

@implementation GlobalObjects

@synthesize animals;

-(id)init{
    self = [super init];
    if (self) {
        // Define the data
        NSArray *imagesValue = [[[NSArray alloc] initWithObjects:@"dog.wav",@"cat.png",@"bird.png",nil] autorelease];
        NSArray *audioValue =[[[NSArray alloc] initWithObjects:@"dog.wav",@"cat.wav",@"bird.wav",nil] autorelease];
        NSArray *descriptionValue = [[[NSArray alloc] initWithObjects:@"Dog",@"Cat",@"Bird",nil] autorelease];

        // Store to array
        for (int i=0; i<8; i++) {
            NSDictionary *tempArr = [NSDictionary dictionaryWithObjectsAndKeys:[imagesValue objectAtIndex:i],@"image", [audioValue objectAtIndex:i],@"audio", [descriptionValue objectAtIndex:i], @"description", nil];
            [self.animals addObject:tempArr];
        }
    }
    return self;
}
@end

Here's how I call it.

// someOtherClass.h
#import "GlobalObjects.h"
@property (nonatomic, retain) GlobalObjects *animalsData;

// someOtherClass.m
@synthesize animalsData;
self.animalsData = [[[GlobalObjects alloc] init] autorelease];
NSLog(@"Global Object %@ ",self.animalsData.animals);

Now the problem is, when I call this array in another class, it always returns null.

I'm new to iOS programming. So probably my method is wrong?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
resting
  • 16,287
  • 16
  • 59
  • 90

2 Answers2

1

You forgot to allocate the animals array in the init method of "GlobalObjects":

self.animals = [[NSMutableArray alloc] init];

If you don't do this, self.animals is nil and addObject has no effect.

Since you do not use ARC, remember to release the array in dealloc.

EDIT: As @H2CO3 and @Bastian have noticed, I forgot my pre-ARC lessons. So the correct way to allocate self.animals in your init method is

self.animals = [[[NSMutableArray alloc] init] autorelease];

and in dealloc you have to add

self.animals = nil;

before calling [super dealloc]. I hope that I got it right now!

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • That"s a good find too! However, I discourage using `self.animals = [[NSMutableArray alloc] init];` - with that, you're leaking memory. Either autorelease it, or use a temporary variable: `NSMutableArray *a = [[NSMutableArray alloc] init]; self.animals = a; [a release];` then set the property to `nil` in dealloc. –  Sep 09 '12 at 08:57
  • the array is retained twice now. it is retained by the property and by your alloc/init. you should add autorelease to this line. – Bastian Sep 09 '12 at 09:03
  • @H2CO3: setting the property to `nil` in dealloc is discouraged by apple. you should just release it. If you set the property to `nil` it will call the setter function in the property during deallocation and that's undefined behavior. – Bastian Sep 09 '12 at 09:06
  • @H2CO3, Bastian: You are of course both right. It is a long time that I have used MRC (and I hope that this remark will not start a new discussion whether ARC or MRC is better :-). – Martin R Sep 09 '12 at 09:07
  • @Bastian well, no. Since it's happening inside the `dealloc˙ method (and you **haven't** yet called `[super dealloc]`, have you?) then the object is still alive. And I read the exact opposite - when there's a property, you're not at all supposed to access its backing ivar directly, only through getters and setters. –  Sep 09 '12 at 09:07
  • @Bastian also, your "the array is retained twice now. it is retained by the property and by your alloc/init. you should add autorelease to this line" statement is wrong - I alloc-init the array, that's refcount = 1. Then I assign it to the property, that's refcount 2. Then I release the temporary object (which points to the same object), so we're also at refcount 1. That's fine. In dealloc, we set the property to nil, the setter releases the ivar, refcount = 0 and it's deallocated. –  Sep 09 '12 at 09:10
  • @H2CO3 I'm not wrong. There was a discussion on this years wwdc and it's mentioned in one of the videos you can access at apple. If apple is wrong is an other question. Of course it's not a problem with an array but a other kind of property might access other properties in it's setter, and those might or might not be already deallocated in your dealloc – Bastian Sep 09 '12 at 09:23
  • @Bastian you should have started with this. But well, still... If one property accesses another property of its owning object, that's already a bad design. –  Sep 09 '12 at 09:25
  • @H2CO3 see also this question: http://stackoverflow.com/questions/5621139/is-there-any-problem-using-self-property-nil-in-dealloc they even discourage using setter functions in init ;) – Bastian Sep 09 '12 at 09:26
  • @Bastian they even say that using the new runtime you don't have any other choice than using setters :) –  Sep 09 '12 at 09:34
  • no .. there was a bug where you could not use ivars when synthesizing, but that bug is long fixed – Bastian Sep 09 '12 at 10:13
  • i have no idea what's going on in the comments. but i was missing the alloc in my app. that's solved for now. thanks for helping a newbie. now...on to more bug crushing. – resting Sep 09 '12 at 16:45
1

Yes, it's wrong - an instance variable isn't tied to a class itself, but to a particular instance of the class. The Cocoa-standard solution to this problem is creating a shared instance of the class - instead of

elf.animalsData = [[[GlobalObjects alloc] init] autorelease];

write

elf.animalsData = [GlobalObjects sharedInstance];

and implement the + sharedInstance method like this:

+ (id)sharedInstance
{
    static shared = nil;
    if (shared == nil)
        shared = [[self alloc] init];

    return shared;
}

As @MartinR pointed out, you make another mistake: you don't create the array you're adding objects to - then it remains nil, cancelling out the effect of all method calls on itself. You have to alloc-init a mutable array for it in the - init method.