Reputation: 489
I have read the posts I found here and in many other places - no answers.
I have a simple class that contains NSString:
MyItem.h
#import <Foundation/Foundation.h>
@interface MyItem : NSObject
{
NSString * ItemName;
NSNumber * TestID;
NSMutableArray * Items;
}
//property
@property (nonatomic,retain) NSString * ItemName;
@property (nonatomic,retain) NSNumber * TestID;
@property (nonatomic,retain) NSMutableArray * Items;
// methods
-(id)initWithName:(NSString*)theName;
@end
MyItem.M
#import "MyItem.h"
@implementation MyItem
@synthesize Items;
@synthesize TestID;
@synthesize ItemName;
-(id)initWithName:(NSString*)theName
{
ItemName=theName;
Items=[[NSMutableArray alloc]init];
return self;
}
@end
It is very simple, as the class is created, the name is retained and the array allocated. In order to have view controllers sharing this class, I have created this protocol:
MasterPager.h
#import <Foundation/Foundation.h>
@class MyItem;
@protocol MasterPager <NSObject>
@required
@property (nonatomic,retain) MyItem * currentItem;
-(void)dumpItems;
@end
which I then use in my appdelegate:
ArrayTestAppDelegate.h
#import <UIKit/UIKit.h>
#import "MasterPager.h"
@class ArrayTestViewController;
@interface ArrayTestAppDelegate : NSObject <UIApplicationDelegate,MasterPager>
{
//MyItem * currentItem;
}
@property (nonatomic,retain) MyItem * currentItem;
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet ArrayTestViewController *viewController;
@end
I'm instanciating this property in the application didFinishLaunchingWithOptions as so:
@synthesize currentItem;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
// Override point for customization after application launch.
currentItem=[[MyItem alloc] initWithName:@"main stuff"];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
self.viewController.mainPage=self;
return YES;
}
and here is the dumpItem method:
-(void)dumpItems
{
NSLog(@"Dumping %@",currentItem.ItemName);
for(int i=[currentItem.Items count]-1;i>=0;i--)
{
MyItem * item=[currentItem.Items objectAtIndex:i];
NSLog(@"current item id:%@", item.TestID );
NSLog(@"current item name:%@", item.ItemName );
}
}
(Sorry for all this text, but it is probably required).
Now, I have a view controller that I use in order to test this. This view controller has 2 buttons, each of them triggers different function. the first function to create some (4) sub items in this object is working fine:
-(IBAction)onCreate:(id)sender
{
for(int i=0;i<4;i++)
{
MyItem * item=[[MyItem alloc] initWithName :[NSString stringWithFormat:@"Test number %d",i]];
item.TestID=[NSNumber numberWithInt:i];
[mainPage.currentItem.Items addObject:item];
}
[mainPage dumpItems];
}
As you can see the dumpItems is called and it does what its suppose to do, dumping the objects.
********NOW... here is the thing!*************
There is a second button, as mentioned, that execute the same function:
- (IBAction)onDump:(id)sender
{
NSLog(@"executing dump on the protocol");
[mainPage dumpItems];
}
After creation, clicking the second button is calling this method which in turn calls the same dumpItems! BUT, when this is executed, an exc_bad_access is thrown when the line
NSLog(@"current item name:%@", item.ItemName );
is reached. comment the line and it's all working. un-commenting the //MyItem * currentItem; will do nothing. So, how could it be? NSZombieEnabled ? Tried that, did nothing. There is no release call in sight, and if there were, how come the NSNumber dump working just fine? Also, nothing happen between the first button clicked and the second one. but still, the strings somehow disappears!
Upvotes: 0
Views: 326
Reputation: 4667
I had the same problem, you should change the code from:
@property (nonatomic,retain) NSString * ItemName;
@property (nonatomic,retain) NSNumber * TestID;
@property (nonatomic,retain) NSMutableArray * Items;
to:
@property (nonatomic,copy) NSString * ItemName;
@property (nonatomic,copy) NSNumber * TestID;
@property (nonatomic,copy) NSMutableArray * Items;
Upvotes: 0
Reputation: 90117
is this ARC? If not, it's not that hard, and not that cool ;-)
You pass an autoreleased NSString to your init method
MyItem * item=[[MyItem alloc] initWithName :[NSString stringWithFormat:@"Test number %d",i]];
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ autoreleased object
Unfortunately you don't retain that autoreleased string in your init.
-(id)initWithName:(NSString*)theName {
ItemName=theName; // <- no retain
}
the code steps out of the init method and you run dumpItems on the newly created object
[mainPage dumpItems]; // <- within same runloop iteration. AutoreleasePool has not be drained.
since you call dumpItems before the end of the current runloop the autoreleased object still exists.
But the IBAction method happens after the autoreleased object has been deallocated (the object was deallocated when the autorelease pool was drained at the end of the current runloop).
- (IBAction)onDump:(id)sender
{
NSLog(@"executing dump on the protocol");
[mainPage dumpItems]; // <- not in the same runloop. AutoreleasePool has been drained. Autoreleased object has been deallocated
}
the fix:
-(id)initWithName:(NSString *)theName
{
if ((self = [super init])) {
itemName = [theName retain]; // to match your @property
items = [[NSMutableArray alloc] init];
}
return self;
}
By the objective-c code style guidelines only Class names (e.g. NSString, MyItem) should start with a capital letter. You should fix this to improve readability (and the code formatting on stackoverflow)
Upvotes: 1