NSCoding with as NSString inside a object - iphone

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];

Related

How to save non property values in iOS using NSUserDefaults?

I am saving the AccessToken that I got from one Social Networking website.when I save this then I come to know that We can't directly save the non property values in iOS SDK.
Then from tutorial I came to know that I should implement the NSCoding class. Then I did this.
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:self.accessToken];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:myEncodedObject forKey:#"myEncodedObjectKey"];
[defaults synchronize];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *myEncodedObject = [defaults objectForKey:#"myEncodedObjectKey"];
LOAToken *obj = (LOAToken *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];
I implemented the NSCoding delegate but I don't know how to implement the delegate methods. Then when I run this code I get the error
`"-[LOAToken encodeWithCoder:]: unrecognized selector sent to instance 0xa2bb970"
I am unable to implement the NSCoding along with my code. Any Suggestions? Also Is there any other way to store the non-property values like AccessToken for further use.
Edit:
I am getting this access token of LinkedIn and want to store like this:
self.accessToken = [[LOAToken alloc] initWithHTTPResponseBody:responseBody];
// The accessToken Printed....Here I have the AccessToken Value.
NSLog(#"Access===%#",self.accessToken);
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:self.accessToken];
NSLog(#"myEncodedObject===%#",myEncodedObject);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:myEncodedObject forKey:#"myEncodedObjectKey"];
[defaults synchronize];
This was getting crashed since I have not used NSCoding implementation which I did in LOAToken class which you suggested.
I made one variable value in LAToken class and implemented these two methods.
-(void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:value forKey:#"Value"];
}
-(id)initWithCoder:(NSCoder *)decoder
{
self.value = [decoder decodeObjectForKey:#"Value"];
return self;
}
Then while retrieving I am using this.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *myEncodedObject = [defaults objectForKey:#"myEncodedObjectKey"];
LOAToken *obj = (OAToken *)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];
When I Printed the data both at the time of archiving and Unarchiving then the data printed is same.This may be due to two reasons.
The data which I have unarchived is right and the way of retrieving is wrong which you can suggest through my code above.
The data which I am saving at the time of Archiving is null.So the null is saved.
Is there any limitation in saving the Access Token.May be this can be a reason for null output.
The output which I got is this:
oauth_token "(null)" oauth_token_secret "(null)" oauth_verifier "(null)"
The text "oauth_token" "oauth_token_secret" "oauth_verifier" is coming from AccessToken but their values are null.
Edit 2: This is OAToken Class of LinkedIn where I am getting the Access Token.The same token I am passing in encoding method
OAToken.h
#import <Foundation/Foundation.h>
#interface OAToken : NSObject {
#protected
NSString *key;
NSString *secret;
NSString *session;
NSString *verifier;
NSNumber *duration;
NSMutableDictionary *attributes;
NSDate *created;
BOOL renewable;
BOOL forRenewal;
OAToken *value;
}
#property(retain, readwrite) NSString *key;
#property(retain, readwrite) NSString *secret;
#property(retain, readwrite) NSString *session;
#property(retain, readwrite) NSString *verifier;
#property(retain, readwrite) NSNumber *duration;
#property(retain, readwrite) NSMutableDictionary *attributes;
#property(readwrite, getter=isForRenewal) BOOL forRenewal;
#property (nonatomic,retain) OAToken *value;
- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret;
- (id)initWithKey:(NSString *)aKey
secret:(NSString *)aSecret
session:(NSString *)aSession
verifier:(NSString *)aVerifier
duration:(NSNumber *)aDuration
attributes:(NSMutableDictionary *)theAttributes
created:(NSDate *)creation
renewable:(BOOL)renew;
- (id)initWithHTTPResponseBody:(NSString *)body;
- (id)initWithUserDefaultsUsingServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;
- (int)storeInUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;
- (BOOL)isValid;
- (void)setAttribute:(NSString *)aKey value:(NSString *)aValue;
- (NSString *)attribute:(NSString *)aKey;
- (void)setAttributesWithString:(NSString *)aAttributes;
- (NSString *)attributeString;
- (BOOL)hasExpired;
- (BOOL)isRenewable;
- (void)setDurationWithString:(NSString *)aDuration;
- (void)setVerifierWithUrl:(NSURL *)aURL;
- (BOOL)hasAttributes;
- (NSMutableDictionary *)parameters;
- (BOOL)isEqualToToken:(OAToken *)aToken;
+ (void)removeFromUserDefaultsWithServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix;
#end
OAToken.m
#import "NSString+URLEncoding.h"
#import "OAToken.h"
#interface OAToken (Private)
+ (NSString *)settingsKey:(const NSString *)name provider:(const NSString *)provider prefix:(const NSString *)prefix;
+ (id)loadSetting:(const NSString *)name provider:(const NSString *)provider prefix:(const NSString *)prefix;
+ (void)saveSetting:(NSString *)name object:(id)object provider:(const NSString *)provider prefix:(const NSString *)prefix;
+ (NSNumber *)durationWithString:(NSString *)aDuration;
+ (NSMutableDictionary *)attributesWithString:(NSString *)theAttributes;
#end
#implementation OAToken
#synthesize key, secret, session, verifier, duration, attributes, forRenewal;
#synthesize value;
#pragma mark Encode
-(void)encodeWithCoder:(NSCoder *)encoder
{
// This prints the value....
NSLog(#"value===%#",self.value);
[encoder encodeObject:self.value forKey:#"Value"];
}
-(id)initWithCoder:(NSCoder *)decoder
{
OAToken *hell= [decoder decodeObjectForKey:#"Value"];
// This don't have the value.It is null.
NSLog(#"hell===%#",hell);
return self;
}
#pragma mark init
- (id)init {
return [self initWithKey:nil secret:nil];
}
- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret {
return [self initWithKey:aKey secret:aSecret session:nil verifier:nil duration:nil
attributes:nil created:nil renewable:NO];
}
- (id)initWithKey:(NSString *)aKey
secret:(NSString *)aSecret
session:(NSString *)aSession
verifier:(NSString *)aVerifier
duration:(NSNumber *)aDuration
attributes:(NSMutableDictionary *)theAttributes
created:(NSDate *)creation
renewable:(BOOL)renew
{
[super init];
self.key = aKey;
self.secret = aSecret;
self.session = aSession;
self.verifier = aVerifier;
self.duration = aDuration;
self.attributes = theAttributes;
created = [creation retain];
renewable = renew;
forRenewal = NO;
return self;
}
- (void)setVerifierWithUrl:(NSURL *)aURL
{
NSString *query = [aURL query];
NSArray *pairs = [query componentsSeparatedByString:#"&"];
for (NSString *pair in pairs)
{
NSArray *elements = [pair componentsSeparatedByString:#"="];
if ([[elements objectAtIndex:0] isEqualToString:#"oauth_verifier"])
{
self.verifier = [elements objectAtIndex:1];
}
}
}
- (id)initWithHTTPResponseBody:(const NSString *)body
{
NSString *aKey = nil;
NSString *aSecret = nil;
NSString *aSession = nil;
NSString *aVerifier = nil;
NSNumber *aDuration = nil;
NSDate *creationDate = nil;
NSMutableDictionary *attrs = nil;
BOOL renew = NO;
NSArray *pairs = [body componentsSeparatedByString:#"&"];
for (NSString *pair in pairs)
{
NSArray *elements = [pair componentsSeparatedByString:#"="];
if ([[elements objectAtIndex:0] isEqualToString:#"oauth_token"])
{
aKey = [elements objectAtIndex:1];
}
else if ([[elements objectAtIndex:0] isEqualToString:#"oauth_token_secret"])
{
aSecret = [elements objectAtIndex:1];
}
else if ([[elements objectAtIndex:0] isEqualToString:#"oauth_verifier"])
{
aVerifier = [elements objectAtIndex:1];
}
else if ([[elements objectAtIndex:0] isEqualToString:#"oauth_session_handle"])
{
aSession = [elements objectAtIndex:1];
}
else if ([[elements objectAtIndex:0] isEqualToString:#"oauth_token_duration"])
{
aDuration = [[self class] durationWithString:[elements objectAtIndex:1]];
creationDate = [NSDate date];
}
else if ([[elements objectAtIndex:0] isEqualToString:#"oauth_token_attributes"])
{
attrs = [[self class] attributesWithString:[[elements objectAtIndex:1] decodedURLString]];
}
else if ([[elements objectAtIndex:0] isEqualToString:#"oauth_token_renewable"])
{
NSString *lowerCase = [[elements objectAtIndex:1] lowercaseString];
if ([lowerCase isEqualToString:#"true"] || [lowerCase isEqualToString:#"t"]) {
renew = YES;
}
}
}
value=[self initWithKey:aKey
secret:aSecret
session:aSession
verifier:aVerifier
duration:aDuration
attributes:attrs
created:creationDate
renewable:renew];
return [self initWithKey:aKey
secret:aSecret
session:aSession
verifier:aVerifier
duration:aDuration
attributes:attrs
created:creationDate
renewable:renew];
}
- (id)initWithUserDefaultsUsingServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix {
[super init];
self.key = [OAToken loadSetting:#"key" provider:provider prefix:prefix];
self.secret = [OAToken loadSetting:#"secret" provider:provider prefix:prefix];
self.session = [OAToken loadSetting:#"session" provider:provider prefix:prefix];
self.verifier = [OAToken loadSetting:#"verifier" provider:provider prefix:prefix];
self.duration = [OAToken loadSetting:#"duration" provider:provider prefix:prefix];
self.attributes = [OAToken loadSetting:#"attributes" provider:provider prefix:prefix];
created = [OAToken loadSetting:#"created" provider:provider prefix:prefix];
renewable = [[OAToken loadSetting:#"renewable" provider:provider prefix:prefix] boolValue];
if (![self isValid]) {
[self autorelease];
return nil;
}
return self;
}
#pragma mark dealloc
- (void)dealloc {
self.key = nil;
self.secret = nil;
self.duration = nil;
self.attributes = nil;
[super dealloc];
}
#pragma mark settings
- (BOOL)isValid {
return (key != nil && ![key isEqualToString:#""] && secret != nil && ![secret isEqualToString:#""]);
}
- (int)storeInUserDefaultsWithServiceProviderName:(const NSString *)provider prefix:(const NSString *)prefix {
[OAToken saveSetting:#"key" object:key provider:provider prefix:prefix];
[OAToken saveSetting:#"secret" object:secret provider:provider prefix:prefix];
[OAToken saveSetting:#"created" object:created provider:provider prefix:prefix];
[OAToken saveSetting:#"duration" object:duration provider:provider prefix:prefix];
[OAToken saveSetting:#"session" object:session provider:provider prefix:prefix];
[OAToken saveSetting:#"verifier" object:verifier provider:provider prefix:prefix];
[OAToken saveSetting:#"attributes" object:attributes provider:provider prefix:prefix];
[OAToken saveSetting:#"renewable" object:renewable ? #"t" : #"f" provider:provider prefix:prefix];
[[NSUserDefaults standardUserDefaults] synchronize];
return(0);
}
#pragma mark duration
- (void)setDurationWithString:(NSString *)aDuration {
self.duration = [[self class] durationWithString:aDuration];
}
- (BOOL)hasExpired
{
return created && [created timeIntervalSinceNow] > [duration intValue];
}
- (BOOL)isRenewable
{
return session && renewable && created && [created timeIntervalSinceNow] < (2 * [duration intValue]);
}
#pragma mark attributes
- (void)setAttribute:(const NSString *)aKey value:(const NSString *)aAttribute {
if (!attributes) {
attributes = [[NSMutableDictionary alloc] init];
}
[attributes setObject: aAttribute forKey: aKey];
}
- (void)setAttributes:(NSMutableDictionary *)theAttributes {
[attributes release];
if (theAttributes) {
attributes = [[NSMutableDictionary alloc] initWithDictionary:theAttributes];
}else {
attributes = nil;
}
}
- (BOOL)hasAttributes {
return (attributes && [attributes count] > 0);
}
- (NSString *)attributeString {
if (![self hasAttributes]) {
return #"";
}
NSMutableArray *chunks = [[NSMutableArray alloc] init];
for(NSString *aKey in self->attributes) {
[chunks addObject:[NSString stringWithFormat:#"%#:%#", aKey, [attributes objectForKey:aKey]]];
}
NSString *attrs = [chunks componentsJoinedByString:#";"];
[chunks release];
return attrs;
}
- (NSString *)attribute:(NSString *)aKey
{
return [attributes objectForKey:aKey];
}
- (void)setAttributesWithString:(NSString *)theAttributes
{
self.attributes = [[self class] attributesWithString:theAttributes];
}
- (NSMutableDictionary *)parameters
{
NSMutableDictionary *params = [[[NSMutableDictionary alloc] init] autorelease];
if (key)
{
[params setObject:key forKey:#"oauth_token"];
if ([self isForRenewal])
{
[params setObject:session forKey:#"oauth_session_handle"];
}
}
else
{
if (duration)
{
[params setObject:[duration stringValue] forKey: #"oauth_token_duration"];
}
if ([attributes count])
{
[params setObject:[self attributeString] forKey:#"oauth_token_attributes"];
}
}
if (verifier)
{
[params setObject:verifier forKey:#"oauth_verifier"];
}
return params;
}
#pragma mark comparisions
- (BOOL)isEqual:(id)object {
if([object isKindOfClass:[self class]]) {
return [self isEqualToToken:(OAToken *)object];
}
return NO;
}
- (BOOL)isEqualToToken:(OAToken *)aToken {
/* Since ScalableOAuth determines that the token may be
renewed using the same key and secret, we must also
check the creation date */
if ([self.key isEqualToString:aToken.key] &&
[self.secret isEqualToString:aToken.secret]) {
/* May be nil */
if (created == aToken->created || [created isEqualToDate:aToken->created]) {
return YES;
}
}
return NO;
}
#pragma mark class_functions
+ (NSString *)settingsKey:(NSString *)name provider:(NSString *)provider prefix:(NSString *)prefix {
return [NSString stringWithFormat:#"OAUTH_%#_%#_%#", provider, prefix, [name uppercaseString]];
}
+ (id)loadSetting:(NSString *)name provider:(NSString *)provider prefix:(NSString *)prefix {
return [[NSUserDefaults standardUserDefaults] objectForKey:[self settingsKey:name
provider:provider
prefix:prefix]];
}
+ (void)saveSetting:(NSString *)name object:(id)object provider:(NSString *)provider prefix:(NSString *)prefix {
[[NSUserDefaults standardUserDefaults] setObject:object forKey:[self settingsKey:name
provider:provider
prefix:prefix]];
}
+ (void)removeFromUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix {
NSArray *keys = [NSArray arrayWithObjects:#"key", #"secret", #"created", #"duration", #"session", #"verifier", #"attributes", #"renewable", nil];
for(NSString *name in keys) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[OAToken settingsKey:name provider:provider prefix:prefix]];
}
}
+ (NSNumber *)durationWithString:(NSString *)aDuration {
NSUInteger length = [aDuration length];
unichar c = toupper([aDuration characterAtIndex:length - 1]);
int mult;
if (c >= '0' && c <= '9') {
return [NSNumber numberWithInt:[aDuration intValue]];
}
if (c == 'S') {
mult = 1;
} else if (c == 'H') {
mult = 60 * 60;
} else if (c == 'D') {
mult = 60 * 60 * 24;
} else if (c == 'W') {
mult = 60 * 60 * 24 * 7;
} else if (c == 'M') {
mult = 60 * 60 * 24 * 30;
} else if (c == 'Y') {
mult = 60 * 60 * 365;
} else {
mult = 1;
}
return [NSNumber numberWithInt: mult * [[aDuration substringToIndex:length - 1] intValue]];
}
+ (NSMutableDictionary *)attributesWithString:(NSString *)theAttributes {
NSArray *attrs = [theAttributes componentsSeparatedByString:#";"];
NSMutableDictionary *dct = [[NSMutableDictionary alloc] init];
for (NSString *pair in attrs) {
NSArray *elements = [pair componentsSeparatedByString:#":"];
[dct setObject:[elements objectAtIndex:1] forKey:[elements objectAtIndex:0]];
}
return [dct autorelease];
}
#pragma mark description
- (NSString *)description {
return [NSString stringWithFormat:#"oauth_token \"%#\" oauth_token_secret \"%#\" oauth_verifier \"%#\"", key, secret, verifier];
}
#end
You don't need any delegate methods. Instead, LOAToken should implement the initWithCoder: and encodeWithCoder: methods to actually save and restore the information that it contains. This is the meaning of implementing the NSCoding protocol.
You may want to have a read of the archiving docs here.
Once you have initially created your token from a web response or similar you should be using storeInUserDefaultsWithServiceProviderName: to store it into user defaults. Then, when you want to use it again call initWithUserDefaultsUsingServiceProviderName:. You don't need to do any encoding and decoding yourself.
To unarchive data
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:#"key"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];
to archive data
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
[defaults setValue:data forKey:#"key"];
[defaults synchronize];

I can't save NSUserdefaults from custom class

I have a problem with saving from a custom class. I tried to save errors from some place in my code in NSUserDefaults but it doesn't work.
H file:
#import <Foundation/Foundation.h>
#interface Errors : NSObject<NSCoding>
#property (nonatomic,strong) NSString* type;
#property (nonatomic,strong) NSString* verNum;
#property (nonatomic,strong) NSString* title;
#property (nonatomic,strong) NSString* content;
#property (nonatomic,strong) NSString* source;
#property (nonatomic,strong) NSString* userId;
#property (nonatomic,strong) NSNumber* videoId;
#end
M file:
#import "Errors.h"
#implementation Errors
#synthesize verNum;
#synthesize type;
#synthesize title;
#synthesize content;
#synthesize source;
#synthesize userId;
#synthesize videoId;
- (void)encodeWithCoder:(NSCoder *)encoder{
[encoder encodeObject:self.verNum forKey:#"verNum"];
[encoder encodeObject:self.type forKey:#"type"];
[encoder encodeObject:self.title forKey:#"title"];
[encoder encodeObject:self.content forKey:#"content"];
[encoder encodeObject:self.source forKey:#"source"];
[encoder encodeObject:self.userId forKey:#"userId"];
[encoder encodeObject:self.videoId forKey:#"videoId"];
}
- (id)initWithCoder:(NSCoder *)decoder{
self = [super init];
if( self != nil ) {
self.verNum = [decoder decodeObjectForKey:#"verNum"];
self.type = [decoder decodeObjectForKey:#"type"];
self.title = [decoder decodeObjectForKey:#"title"];
self.content = [decoder decodeObjectForKey:#"content"];
self.source = [decoder decodeObjectForKey:#"source"];
self.userId = [decoder decodeObjectForKey:#"userId"];
self.videoId = [decoder decodeObjectForKey:#"videoId"];
}
return self;
}
#end
Saving:
errorsDitalis = [[Errors alloc]init];
errorsDitalis.verNum = (NSString *) [[request userInfo] objectForKey:#"verNum"];
errorsDitalis.type =(NSString *) [[request userInfo] objectForKey:#"type"];
errorsDitalis.title =(NSString *) [[request userInfo] objectForKey:#"title"];
errorsDitalis.content =(NSString *) [[request userInfo] objectForKey:#"content"];
errorsDitalis.source =(NSString *) [[request userInfo] objectForKey:#"source"];
errorsDitalis.userId =(NSString *) [[request userInfo] objectForKey:#"userId"];
errorsDitalis.videoId =(NSNumber *) [[request userInfo] objectForKey:#"videoId"];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:errorsDitalis forKey:#"ErrorList"];
[userDefaults synchronize];
Loading:
Errors * newError = [[Errors alloc]init];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
newError = [userDefaults objectForKey:#"ErrorList"];
It just returns nil for newErrors. I tried to use this help but it didn't work. Does someone have an idea?
Try
[self setVerNum:[decoder decodeObjectForKey:#"verNum"]];
instead of
self.verNum = [decoder decodeObjectForKey:#"verNum"];
I believe you should use the setters when you call initWithCoder
i think i should do :
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:self.errorsDitalis] forKey:#"ErrorList"];
[[NSUserDefaults standardUserDefaults] synchronize];
and not:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:errorsDitalis forKey:#"ErrorList"];
[userDefaults synchronize];
and load with:
newError = (Errors *)[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:#"ErrorList"]];

How can I retrieve an object from NSDictionary to set up table

I guys,
I am fighting with a problem since some days and wanted to get your assistance.
I have an RSS file on a server which gets parsed normally. Basically it's about shows, concerts, gigs of a band. That's why I have created the Show-Class:
Show.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface Show : NSObject {
NSString* gigName;
NSString* date;
NSString* place;
NSString* door;
NSString* time;
NSString* bands;
NSString* entry;
NSString* query;
NSString* flyer;
NSString* type;
}
- (id)init:(NSString*)gigName date:(NSString*)date place:(NSString*)place door:(NSString*)door time:(NSString*)time bands:(NSString*)bands
entry:(NSString*)entry query:(NSString*)query flyer:(NSString*)flyer type:(NSString*)type;
#property (nonatomic, retain) NSString* gigName;
#property (nonatomic, retain) NSString* date;
#property (nonatomic, retain) NSString* place;
#property (nonatomic, retain) NSString* door;
#property (nonatomic, retain) NSString* time;
#property (nonatomic, retain) NSString* bands;
#property (nonatomic, retain) NSString* entry;
#property (nonatomic, retain) NSString* query;
#property (nonatomic, retain) NSString* flyer;
#property (nonatomic, retain) NSString* type;
#end
and the Show.m:
#import "Show.h"
#implementation Show
#synthesize gigName, date , place, door, time, bands, entry, query, flyer, type;
- (id)init:(NSString*)_gigName date:(NSString*)_date place:(NSString*)_place door:(NSString*)_door time:(NSString*)_time bands:(NSString*)_bands
entry:(NSString*)_entry query:(NSString*)_query flyer:(NSString*)_flyer type:(NSString*)_type {
//if (self = [super init]) {
self = [super init];
if(self) {
gigName = _gigName;
date = _date;
place = _place;
door = _door;
time = _time;
bands = _bands;
entry = _entry;
query = _query;
flyer = _flyer;
type = _type;
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
In my TourViewController (which is a table view) i have created this:
- (void)viewDidLoad {
[super viewDidLoad];
//Initialize the array.
listOfItems = [[NSMutableArray alloc] init];
pastShowArray = [NSMutableArray arrayWithCapacity:[stories count]]; // needs to be mutable
futureShowArray = [NSMutableArray arrayWithCapacity:[stories count]]; // needs to be mutable
for(int i = 0; i < [stories count]; i++) {
// get fields from story parser object
gigName = [[stories objectAtIndex:i] objectForKey:#"gig"];
type = [[stories objectAtIndex:i] objectForKey:#"type"];
date = [[stories objectAtIndex:i] objectForKey:#"date"];
place = [[stories objectAtIndex:i] objectForKey:#"place"];
door = [[stories objectAtIndex:i] objectForKey:#"door"];
time = [[stories objectAtIndex:i] objectForKey:#"time"];
bands = [[stories objectAtIndex:i] objectForKey:#"bands"];
entry = [[stories objectAtIndex:i] objectForKey:#"entry"];
query = [[stories objectAtIndex:i] objectForKey:#"query"];
flyer = [[stories objectAtIndex:i] objectForKey:#"flyer"];
// remove unnecessary whitespaces
gigName = [gigName stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
type = [type stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
date = [date stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
place = [place stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
door = [door stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
time = [time stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
bands = [bands stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
entry = [entry stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
query = [query stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
flyer = [flyer stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(gigName);
NSLog(type);
Show* show = [[Show alloc] init:gigName date:date place:place door:door time:time bands:bands entry:entry query:query flyer:flyer type:type];
if([type isEqualToString:#"past"]) {
// fill past show array
NSLog(#"adding past object now");
[pastShowArray addObject:show];
} else {
// fill future show array
NSLog(#"adding future object now");
[futureShowArray addObject:show];
}
}
NSMutableDictionary *pastShowsArrayDict = [NSMutableDictionary dictionaryWithObject:pastShowArray forKey:#"Show"];
NSLog(#"added past show array to pastshowarraydict");
NSMutableDictionary *futureShowsArrayDict = [NSMutableDictionary dictionaryWithObject:futureShowArray forKey:#"Show"];
NSLog(#"added future show array to futureshowarraydict");
[listOfItems addObject:pastShowsArrayDict];
[listOfItems addObject:futureShowsArrayDict];
//Set the title
self.navigationItem.title = #"Tour";
}
As far as I can tell, that worked all without problems. Now I want to show the "gigName" of the show in the table. For this I need to get the show object from the NSDictionary. I have this but it crashes:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
NSLog(#"try to set up cell");
//First get the dictionary object
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"Show"];
Show *show = [array objectAtIndex:indexPath.row];
NSLog(show.gigName);
cell.text = show.gigName;
return cell;
}
Error Output:
2011-02-14 16:32:44.462 All in Vain [405:307] try to set up cell
terminate called after throwing an instance of 'NSException'
Program received signal: “SIGABRT”.
Can you guys tell me how I can get the show object and set the table text to show.gigName?
Any help is appreaciated. Thank you,
cheers,
doonot
In your init:... for the Show object you need to make it:
self.gigName = _gigName;
self.date = _date;
self.place = _place;
self.door = _door;
self.time = _time;
self.bands = _bands;
self.entry = _entry;
self.query = _query;
self.flyer = _flyer;
self.type = _type;
so that each of them are retained (or do gigName = [_gigName retain]; but then you loose the release if it was not empty).
You may also have the same problem in the view controller and the way the variables are being set there but without seeing more I can't be sure.
Try to change this :
cell.text = show.gigName;
into :
cell.textLabel.text = show.gigName;

Somehow my singleton properties are getting released

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.

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;
}