Somehow my singleton properties are getting released - iphone

I have a singleton object called PoolManager that loads and saves some data in a plist. Throughout my program when something needs to know about my pool, it asks the [PoolManager sharedPoolManager] for it's properties. I have a single view that's responsible for setting these properties and all others just read from it. It was all working fine, and then for no reason I can tell, it started crashing. I set NSZombieEnabled = YES and can see that when I access one of the two NSString properties, they appear to have been released. The debugger message is: *** -[CFString respondsToSelector:]: message sent to deallocated instance 0x5a336d0
I tried going back to a previous snapshot where everything worked and it still does this. I even used TimeMachine to go back to the project from yesterday and it does it too. I'm baffled.
Here is the singleton object code... It's the surface and shape strings that are apparently zombies. Sorry for all the NSLogs
// MyPoolSingleton.h
#import <Foundation/Foundation.h>
#define kFileName #"data.plist"
#interface PoolManager : NSObject {
float volume;
float length;
float width;
float depth;
NSString *surface;
NSString *shape;
BOOL isMetric;
int fcTarget;
int cyaTarget;
int taTarget;
int chTarget;
int saltTarget;
}
#property float volume;
#property float length;
#property float width;
#property float depth;
#property (nonatomic, retain) NSString *surface;
#property (nonatomic, retain) NSString *shape;
#property BOOL isMetric;
#property int fcTarget;
#property int cyaTarget;
#property int taTarget;
#property int chTarget;
#property int saltTarget;
+ (PoolManager*)sharedPoolManager;
- (void)retrieveState;
- (void)saveState;
- (NSString*)dataFilePath;
#end
// MyPoolSingleton.m
#import "PoolManager.h"
#implementation PoolManager
#synthesize volume;
#synthesize length;
#synthesize width;
#synthesize depth;
#synthesize surface;
#synthesize shape;
#synthesize isMetric;
#synthesize fcTarget;
#synthesize cyaTarget;
#synthesize taTarget;
#synthesize chTarget;
#synthesize saltTarget;
static PoolManager* _sharedPoolManager = nil;
+ (PoolManager*)sharedPoolManager {
#synchronized([PoolManager class])
{
if (!_sharedPoolManager)
[[self alloc] init];
return _sharedPoolManager;
}
return nil;
}
+ (id)alloc {
#synchronized([PoolManager class])
{
NSAssert(_sharedPoolManager == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedPoolManager = [super alloc];
return _sharedPoolManager;
}
return nil;
}
- (id)init {
self = [super init];
return self;
}
- (void)retrieveState {
NSLog(#"--retrieveState");
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSLog(#" fileExistsAtPath: reading array from plist");
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
volume = [[array objectAtIndex:0] floatValue];
NSLog(#" reading array: volume = %1.1f", volume);
length = [[array objectAtIndex:1] floatValue];
NSLog(#" reading array: length = %1.1f", length);
width = [[array objectAtIndex:2] floatValue];
NSLog(#" reading array: width = %1.1f", width);
depth = [[array objectAtIndex:3] floatValue];
NSLog(#" reading array: depth = %1.1f", depth);
self.surface = [array objectAtIndex:4];
NSLog(#" reading array: surface = %#", surface);
self.shape = [array objectAtIndex:5];
NSLog(#" reading array: shape = %#", shape);
isMetric = [[array objectAtIndex:6] boolValue];
NSLog(#" reading array: isMetric = %d", isMetric);
fcTarget = [[array objectAtIndex:7] intValue];
NSLog(#" reading array: fcTarget = %d", fcTarget);
cyaTarget = [[array objectAtIndex:8] intValue];
NSLog(#" reading array: cyaTarget = %d", cyaTarget);
taTarget = [[array objectAtIndex:9] intValue];
NSLog(#" reading array: taTarget = %d", taTarget);
chTarget = [[array objectAtIndex:10] intValue];
NSLog(#" reading array: chTarget = %d", chTarget);
saltTarget = [[array objectAtIndex:11] intValue];
NSLog(#" reading array: saltTarget = %d", saltTarget);
[array release];
}
else {
NSLog(#" !fileExistsAtPath: intitializing values to nil/zero");
volume = 0.0;
length = 0.0;
width = 0.0;
depth = 0.0;
surface = #"";
shape = #"";
isMetric = NO;
fcTarget = 0.0;
cyaTarget = 0.0;
taTarget = 0.0;
chTarget = 0.0;
saltTarget = 0.0;
}
}
- (void)saveState {
NSLog(#"--saveState");
NSMutableArray *array = [[NSMutableArray alloc] init];
NSLog(#" building array: volume = %1.1f", volume);
[array addObject:[NSNumber numberWithFloat:volume]];
NSLog(#" building array: length = %1.1f", length);
[array addObject:[NSNumber numberWithFloat:length]];
NSLog(#" building array: width = %1.1f", width);
[array addObject:[NSNumber numberWithFloat:width]];
NSLog(#" building array: depth = %1.1f", depth);
[array addObject:[NSNumber numberWithFloat:depth]];
NSLog(#" building array: surface = %#", surface);
[array addObject:surface];
NSLog(#" building array: shape = %#", shape);
[array addObject:shape];
NSLog(#" building array: isMetric = %d", isMetric);
[array addObject:[NSNumber numberWithBool:isMetric]];
NSLog(#" building array: fcTarget = %d", fcTarget);
[array addObject:[NSNumber numberWithInt:fcTarget]];
NSLog(#" building array: cyaTarget = %d", cyaTarget);
[array addObject:[NSNumber numberWithInt:cyaTarget]];
NSLog(#" building array: taTarget = %d", taTarget);
[array addObject:[NSNumber numberWithInt:taTarget]];
NSLog(#" building array: chTarget = %d", chTarget);
[array addObject:[NSNumber numberWithInt:chTarget]];
NSLog(#" building array: saltTarget = %d", saltTarget);
[array addObject:[NSNumber numberWithInt:saltTarget]];
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
}
- (NSString*)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFileName];
}
- (void)dealloc {
[shape release], shape = nil;
[surface release], surface = nil;
[super dealloc];
}
#end

objectAtIndex: gives an autoreleased object. You should either retain it, or use the property accessor self.surface = ... and self.shape = ... when setting those.

Personally I prefer the following pattern for singletons:
+ (id) sharedPoolManager
{
static id sharedPoolManager = nil;
#synchronized (self) {
if (sharedPoolManager == nil) {
sharedPoolManager = [self new];
}
}
return sharedPoolManager;
}
- (id) init
{
if ((self = [super init]) != nil) {
// Initialize ... nothing special here ...
}
return self;
}
(Note that self in a class method is equivalent to [SomeClass class])
The above is more concise and I prefer to keep any singleton code outside of init and alloc since I can then also create multiple instances if needed. For example in unit tests.
Personally I don't think you have to protect the programmer from creating multiple instances. The contract is clear: sharedPoolManager returns a singleton instance. If that is what you want/need then use that. Otherwise create instances the usual way.

Related

print reverse of a string

I am writing a program which prints the reverse of a string in a textfield, taking input text from the other textfield. When I press enter after entering text into one textfield the result(reverse) should be dispalyed in the other text field.
I have tried like this, but am getting weird results.
.h file:
#import <UIKit/UIKit.h>
#interface reverseVC : UIViewController {
UITextField *textEntered;
UITextField *textDisplay;
}
#property (nonatomic,retain) IBOutlet UITextField *textDisplay;
#property (nonatomic,retain) IBOutlet UITextField *textEntered;
- (NSMutableArray*) stringReverse;
- (IBAction) enter;
#end
.m file
- (NSMutableArray *) stringReverse
{
NSString *value = textEntered.text;
NSArray *tempArray = [[[NSArray alloc] initWithObjects:value,nil] autorelease];
NSMutableArray *arr = [[[NSMutableArray alloc] init] autorelease];
for (int i=[tempArray count]-1; i>=0; i--)
{
[arr addObject:[tempArray objectAtIndex:i]];
NSLog(#"the object is:%c",arr);
}
return arr;
}
-(IBAction)enter
{
textDisplay.text = [NSString stringWithFormat:#"%c",[self stringReverse]];
}
Earlier got warnings like SIG_ABT and EXE_BAD_ACCESS before placing nil and autorelease in array initialisations. Now the warnings are solved but results undesiredly.
Where am I going wrong?
You insert the NSString object in your array. Its count is 1. You have to go from the end of the string to the beginning and append the character to a new string. You ca do it like this:
-(NSString*)reverseString:(NSString*)string {
NSMutableString *reversedString;
int length = [string length];
reversedString = [NSMutableString stringWithCapacity:length];
while (length--) {
[reversedString appendFormat:#"%C", [string characterAtIndex:length]];
}
return reversedString;
}
Another way to reverse strings is to use the reverse string enumerator.
- (NSString *)reverseString:(NSString *)string {
NSMutableString *reversedString = [[NSMutableString alloc] init];
NSRange fullRange = [string rangeOfString:string];
NSStringEnumerationOptions enumerationOptions = (NSStringEnumerationReverse | NSStringEnumerationByComposedCharacterSequences);
[string enumerateSubstringsInRange:fullRange options:enumerationOptions usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
return reversedString;
}
use this
NSString *str=textEntered.text;//
NSMutableArray *temp=[[NSMutableArray alloc] init];
for(int i=0;i<[str length];i++)
{
[temp addObject:[NSString stringWithFormat:#"%c",[str characterAtIndex:i]]];
}
temp = [NSMutableArray arrayWithArray:[[temp reverseObjectEnumerator] allObjects]];
NSString *reverseString=#"";
for(int i=0;i<[temp count];i++)
{
reverseString=[NSString stringWithFormat:#"%#%#",reverseString,[temp objectAtIndex:i]];
}
NSLog(#"%#",reverseString);
I revers my strings with a few lines of code. Rather late, but just putting it out there :)
- (NSString*) reverseMyString:(NSString*)theString
{
NSString *final = #"";
for(int i = theString.length-1; i>=0; i--)
final = [NSString stringWithFormat:#"%#%c", final, [theString characterAtIndex:i]];
return final;
}
NSString *str = #"hello";
int length = [str length];
NSLog(#"%d",length);
NSMutableString *rev = [[NSMutableString alloc] initWithCapacity:[str length]];
while (length > 0) {
--length;
[rev appendString:[NSString stringWithFormat:#"%c", [str characterAtIndex:length]]];
}
NSLog(#"%#", rev);
Using characterAtIndex doesn't work well with non-ascii characters.
- (NSString *)reverseMyString:(NSString *)string {
NSString *output = [[NSString alloc] init];
for(int i = string.length-1; i>=0; i--) {
output = [output stringByAppendingString:[string substringWithRange:NSMakeRange(i, 1)]];
}
return output;
}

why is the NSMutableArray is being destroyed in this loop?

It is the arrayOfPerformances that is getting broken.
here is the .h for the array:
NSMutableArray * arrayOfPerformances;
}
#property (nonatomic, retain) NSMutableArray * arrayOfPerformances;
and the .m that has the loop:
[dataArray release];
[dataDictionary release];
dataArray = [[NSMutableArray alloc] init];
dataDictionary = [[NSMutableDictionary alloc] init];
NSDate * CurrentDate = start;
int i = 0;
NSMutableArray * arrayOfPerformancesForCurrentDate = [[NSMutableArray alloc] init];
while(YES)
{
i = 0;
if ([arrayOfPerformancesForCurrentDate count] > 0)
{
[arrayOfPerformancesForCurrentDate removeAllObjects];
}
for (i; i < self.arrayOfPerformances.count; i++)
{
Performance * performanceItem = [[Performance alloc] init];
performanceItem = [self.arrayOfPerformances objectAtIndex:i];
NSString * sPerformanceDate = [performanceItem.sDate substringToIndex:10];
NSString * sCurrentDate = [CurrentDate dateDescription];
if([sPerformanceDate isEqualToString:sCurrentDate])
{
[arrayOfPerformancesForCurrentDate addObject:performanceItem];
}
[performanceItem release];
}
if ([arrayOfPerformancesForCurrentDate count] >= 1)
{
[dataDictionary setObject:arrayOfPerformancesForCurrentDate forKey:CurrentDate];
[dataArray addObject:[NSNumber numberWithBool:YES]];
}
else
{
[dataArray addObject:[NSNumber numberWithBool:NO]];
}
TKDateInformation info = [CurrentDate dateInformation];
info.day++;
CurrentDate = [NSDate dateFromDateInformation:info];
if([CurrentDate compare:end]==NSOrderedDescending) break;
}
Any help would be appreciated. I dont understand why this is happening?
This part doesn't look right:
Performance * performanceItem = [[Performance alloc] init]; <--
performanceItem = [self.arrayOfPerformances objectAtIndex:i]; <--
NSString * sPerformanceDate = [performanceItem.sDate substringToIndex:10];
NSString * sCurrentDate = [CurrentDate dateDescription];
if([sPerformanceDate isEqualToString:sCurrentDate])
{
[arrayOfPerformancesForCurrentDate addObject:performanceItem];
}
[performanceItem release]; <--
You alloc+init performanceItem but then set it to an object in arrayOfPerformances and then you release it (while it's pointing to the object in arrayOfPerformances).
Change that section to this:
Performance *performanceItem = [self.arrayOfPerformances objectAtIndex:i];
NSString * sPerformanceDate = [performanceItem.sDate substringToIndex:10];
NSString * sCurrentDate = [CurrentDate dateDescription];
if([sPerformanceDate isEqualToString:sCurrentDate])
{
[arrayOfPerformancesForCurrentDate addObject:performanceItem];
}
//don't release performanceItem
I don't think arrayOfPerformances of being destroyed. It appears you aren't even initializing it anywhere.

NSCoding with as NSString inside a object

My issue is then I retrieve my NSArray of Store objects, all my NSString properties are causing BadAccess errors. The int and double properties work fine!
store.h
#interface Store : NSObject<NSCoding> {
NSString *Name;
NSString *Address;
NSString *Phone;
double GeoLong;
double GeoLat;
int ID;
}
#property (nonatomic, retain) NSString *Name;
#property (nonatomic, retain) NSString *Address;
#property (nonatomic, retain) NSString *Phone;
#property (nonatomic) double GeoLat;
#property (nonatomic) double GeoLong;
#property (nonatomic) int ID;
#end
store.m
#implementation Store
#synthesize Name;
#synthesize ID;
#synthesize Address;
#synthesize Phone;
#synthesize GeoLat;
#synthesize GeoLong;
/** Implentation of the NSCoding protocol. */
-(void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeInt:ID forKey:#"ID"];
[encoder encodeDouble:GeoLat forKey:#"GeoLat"];
[encoder encodeDouble:GeoLong forKey:#"GeoLong"];
NSLog(#"Name in encode: %#", Name); //WORKS!
[encoder encodeObject:Name forKey:#"Name"];
[encoder encodeObject:Phone forKey:#"Phone"];
[encoder encodeObject:Address forKey:#"Address"];
}
-(id)initWithCoder:(NSCoder *)decoder
{
// Init first.
if(self = [self init]){
ID = [decoder decodeIntForKey:#"ID"];
GeoLat = [decoder decodeDoubleForKey:#"GeoLat"];
GeoLong = [decoder decodeDoubleForKey:#"GeoLong"];
Name = [decoder decodeObjectForKey:#"Name"];
NSLog(#"Name in decode: %#", Name); //WORKS! logs the name
Address = [decoder decodeObjectForKey:#"Address"];
Phone = [decoder decodeObjectForKey:#"Phone"];
}
return self;
}
- (void)dealloc
{
[Name release];
[ID release];
[Address release];
[Phone release];
[super dealloc];
}
#end
Here is my code for storing and retriving the array.
//streams contains the data i will populate my array with.
for (ndx = 0; ndx < streams.count; ndx++) {
NSDictionary *stream = (NSDictionary *)[streams objectAtIndex:ndx];
Store *item = [[Store alloc] init] ;
item.Name = [stream valueForKey:#"Name"];
item.Address = [stream valueForKey:#"Address"];
item.Phone = [stream valueForKey:#"Phone"];
item.GeoLat = [[stream valueForKey:#"GeoLat"] doubleValue];
item.GeoLong = [[stream valueForKey:#"GeoLong"] doubleValue];
item.ID = [[stream valueForKey:#"ID"] intValue];
[listToReturn addObject:item];
}
}
//test to check if it works
for(int i = 0; i < [listToReturn count]; i++){
Store *item = (Store *)[listToReturn objectAtIndex:i];
NSLog(#"Name: %#", item.Name); //works
}
//save
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:listToReturn] forKey:#"stores"];
// retrieve
NSMutableArray *stores = [NSMutableArray new];
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"stores"];
if (dataRepresentingSavedArray != nil)
{
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil)
stores = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
stores = [[NSMutableArray alloc] init];
}
if ([stores count] > 0) {
NSMutableArray * annotations = [[NSMutableArray alloc] init];
for(int i = 0;i< [stores count]; i++){
Store *store = [stores objectAtIndex: i];
CLLocationCoordinate2D location;
if(store.GeoLat != 0 && store.GeoLong != 0){
location.latitude = store.GeoLat;
location.longitude = store.GeoLong; //works
NSLog(#"Adding store: %#", store.Name); //DONT WORK!! <-- MAIN PROBLEM
}
}
}
Feels like I tried everything but can't figure out how it works in the decode but not when in loop the array after I put it into a array.
Anyone have any ideas?
You're not retaining the properties in initWithCoder.
Name = [decoder decodeObjectForKey:#"Name"];
is not using the setter of the (retaining) property you've defined. You're just setting the ivar. That means you don't acquire ownership and it can be deallocated.
Here are two ways you can retain the properties in your case:
self.Name = [decoder decodeObjectForKey:#"Name"];
Name = [[decoder decodeObjectForKey:#"Name"] retain];

Memory leaks in NSMutableDictionary

My coding contains a memory leak, and somehow I can't find the leak.
Leaks points me in the direction of the way I create "ReportDetailItems"
e.g. areaContainer = [[[ReportDetailItem alloc] init] autorelease];
I've been looking at this for hours and I am at a total loss, the objects reported leaking are "ReportDetailItem", and the NSMutableDictionary contained in those objects.
Please advice.
------[ReportDetailItem.h
#interface ReportDetailItem : NSObject
{
NSNumber *total;
NSMutableDictionary *items;
}
#property (nonatomic, retain) NSNumber *total;
#property (nonatomic, retain) NSMutableDictionary *items;
- (NSString *)description;
#end
------[ReportDetailItem.m
#synthesize items, total;
- (id)init {
if (self = [super init]) {
self.items = [NSMutableDictionary dictionaryWithCapacity:0];
DLog("Alloc: %d", [items retainCount]);
}
return self;
}
- (NSString *)description {
return #"ReportDetailItem";
}
- (void)release {
[super release];
}
- (void)dealloc {
[self.items release];
[self.total release];
items = nil;
total = nil;
[super dealloc];
}
#end
------[Leaking code
NSError *error;
NSArray *data = [self.managedObjectContext executeFetchRequest:request error:&error];
if (data == nil || [data count] == 0) {
DLog(#"No data.")
} else {
for (int i=0; i < [data count]; i++) {
TaskEntity *task = [data objectAtIndex:i];
NSString *areaKey = task.activity.project.area.title.text;
NSString *projectKey = task.activity.project.title.text;
NSString *activityKey = task.activity.title.text;
ReportDetailItem *areaContainer;
if (![dataSource objectForKey:areaKey]) {
areaContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
areaContainer = [dataSource objectForKey:areaKey];
}
areaContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [areaContainer.total intValue])];
[dataSource setObject:areaContainer forKey:areaKey];
ReportDetailItem *projectContainer;
if (![areaContainer.items objectForKey:projectKey]) {
projectContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
projectContainer = [areaContainer.items objectForKey:projectKey];
}
projectContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [projectContainer.total intValue])];
[areaContainer.items setObject:projectContainer forKey:projectKey];
ReportDetailItem *activityContainer;
if (![projectContainer.items objectForKey:activityKey]) {
activityContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
activityContainer = [projectContainer.items objectForKey:activityKey];
}
activityContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [activityContainer.total intValue])];
[projectContainer.items setObject:activityContainer forKey:activityKey];
}
}
I found it, the leak was located in the way I allocated the "dataSource"
---[Leak
- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = [[NSMutableDictionary alloc] init];
[self fetchData];
}
---[No leak
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
self.dataSource = dict;
[dict release];
[self fetchData];
}
I'm pretty skeptic about the two ways u assign pointers to the ReportDetailItem. Why are you trying to autorelease the object in the first place? If not try this
ReportDetailItem *projectContainer;
if (![areaContainer.items objectForKey:projectKey]) {
projectContainer = [[ReportDetailItem alloc] init];
} else {
projectContainer = [[areaContainer.items objectForKey:projectKey] retain];
}
projectContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [projectContainer.total intValue])];
[areaContainer.items setObject:projectContainer forKey:projectKey];
if(projectContainer) {
[projectContainer release];
projectContainer = nil;
}

iPhone SDK: loading UITableView from SQLite - creating array from SQLite

this is a follow up forr iPhone SDK: loading UITableView from SQLite
I am planning to use following code to load SQL data to the array. Each element of the array will be a class represening each database entry:
#interface Row : NSObject {
int PK;
NSString *desc;
}
#property int PK;
#property (nonatomic, retain) NSString *desc;
#end
the loading operation will be similar to this:
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:1];
Row *myRow = [[Row alloc] init];
for (int i=0; i<10; i++)
{
myRow.PK = i;
myRow.desc = [[NSString alloc] initWithFormat:#"Hello: %d", i];
[array addObject:myRow];
}
[myRow release];
for (int i=0; i < [array count]; i++)
{
Row *myNrow = [array objectAtIndex:i] ;
NSLog (#"%# %d", [myNrow desc], [myNrow PK]);
myNrow = nil;
}
Of course first for loop will be a loop from SELECT statement. The other loop (or elements of that loop) will be in the cellInRowIndex method to render data.
I have a question about memory leaks. Does the code above have memory leaks? The decs string property of the Row class is declared as (retain). SHould not it be released somewhere?
Thanks
You should release the string that you're putting into myRow.desc. You could change
myRow.desc = [[NSString alloc] initWithFormat:#"Hello: %d", i];
to either
myRow.desc = [[[NSString alloc] initWithFormat:#"Hello: %d", i] autorelease];
or
myRow.desc = [NSString stringWithFormat:#"Hello: %d", i];
EDIT: If you want to use an intermediate NSString (as you mentioned in the comment), you could either do it like this:
NSString *foo = [[NSString alloc] initWithFormat:#"Hello: %d", i];
myRow.desc = foo;
[foo release];
or:
NSString *foo = [NSString stringWithFormat:#"Hello: %d", i];
myRow.desc = foo;
Note that in the second example foo is already autoreleased, so you mustn't release it.