Simple addObject to NSMutableArray method gives lldb error - objective-c

I'm adding objects to a NSMutableArray stack in my model. Here's the interface:
#interface calcModel ()
#property (nonatomic, strong) NSMutableArray *operandStack;
#end
And the implementation:
#implementation calcModel
#synthesize operandStack = _operandStack;
- (NSMutableArray *)operandStack;
{
if (_operandStack == nil) _operandStack = [[NSMutableArray alloc]init];
return _operandStack;
}
This addobject method works fine:
- (void)pushValue:(double)number;
{
[self.operandStack addObject:[NSNumber numberWithDouble:number]];
NSLog(#"Array: %#", self.operandStack);
}
but this one crashes the app and just says 'lldb' in the log:
- (void)pushOperator:(NSString *)operator;
{
[self.operandStack addObject:operator];
NSLog(#"Array: %#", self.operandStack);
}
What's causing this error?

The NSString you're adding is probably nil. Do this:
- (void)pushOperator:(NSString *)operator {
if (operator) {
[self.operandStack addObject:operator];
NSLog(#"Array: %#", self.operandStack);
} else {
NSLog(#"Oh no, it's nil.");
}
}
If that's the case, figure out why it's nil and fix that. Or check it before adding it.
The reason why the first method doesn't crash is, because there is no double value that can't be used to init an NSNumber, so it will never be nil.

Related

Shallow copying in this program

This is code from an addition calculator that does operations by entering the two operands first and then the operation; like "5 enter 2 enter +" would result in "7". When the user taps on a number a double will be sent to pushOperand: When a user taps on the addition button the string #"+" will be sent like to performOperation:. My question is what is the point of making those copies in program and runProgram: if they're all shallow copies and their elements all end up pointing to the same elements of NSNumber and NSString objects as _programStack, program, and stack?
#import <Foundation/Foundation.h>
#interface CalculatorBrain : NSObject
#property (nonatomic, readonly) id program;
+(double)runProgram:(id)program;
-(double)performOperation:(NSString *)operation;
#end
#import "CalculatorBrain.h"
#interface CalculatorBrain ()
#property (nonatomic, strong) NSMutableArray *programStack;
#end
#implementation CalculatorBrain
#synthesize programStack = _programStack;
-(NSMutableArray *) programStack {
if (!_programStack)
_programStack = [[NSMutableArray alloc] init];
return _programStack;
}
-(void)pushOperand:(double)operand {
[self.programStack addObject: [NSNumber numberWithDouble: operand]];
}
-(double)performOperation:(NSString *)operation {
[self.programStack addObject: operation];
double result = [CalculatorBrain runProgram: self.program];
return result;
}
-(id)program {
return [self.programStack copy];
}
+(double)runProgram:(id)program {
NSMutableArray *stack;
if ([program isKindOfClass: [NSArray class]])
stack = [program mutableCopy];
return [self popOperandOffProgramStack: stack];
}
+(double)popOperandOffProgramStack:(NSMutableArray *)stack {
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack)
[stack removeLastObject];
if ([topOfStack isMemberOfClass: [NSNumber class]])
result = [topOfStack doubleValue];
if ([topOfStack isKindOfClass: [NSString class]]) {
NSString *operation = topOfStack;
if ([operation isEqualToString: #"+"]) {
result = [self popOperandOffProgramStack: stack] + [self popOperandOffProgramStack: stack];
}
return result;
}
#end
NSNumber and NSString are immutable so making a shallow copy of a collection of objects that can't changee is safe.
In program it is important to return a copy of programStack rather than the the actual mutable array. This is because programStack is a private internal property declared in a class extension so it is not externally visible. If you returned programStack directly an external user could change it since it is an NSMutableArray. The program method returns an NSArray since copies of mutable objects are immutable, which has the right semantics. You want to give the external user a snapshot of the programStack array, not access to your class internals.
In runProgram the situation is different. The external user passes CalculatorBrain an NSArray to process and the class' internal logic requires that the stack have elements popped off the array as it is processed. Thus you need to make a mutableCopy so that it can be mutated for processing.

NSMutableDictionary crashes with "mutating message sent to immutable object"

I have a class that has a NSMutableDictionary as a property:
#interface Alibi : NSObject <NSCopying>
#property (nonatomic, copy) NSMutableDictionary * alibiDetails;
#end
With the following constructor:
- (Alibi *)init
{
self = [super init];
_alibiDetails = [NSMutableDictionary dictionary];
return self;
}
and copy method:
- (Alibi *)copyWithZone:(NSZone *)zone
{
Alibi *theCopy = [[Alibi alloc] init];
theCopy.alibiDetails = [self.alibiDetails mutableCopy];
return theCopy;
}
When I try to call setObject:ForKey: I get a runtime error mutating method sent to immutable object.
I have the Alibi object declared in the view controller as #property (copy, nonatomic) Alibi * theAlibi; and I initialize it with self.theAlibi = [[Alibi alloc] init]; in viewDidLoad.
The line which crashes is:
NSString * recipient;
recipient = #"Boss";
[self.theAlibi.alibiDetails setObject:recipient forKey:#"Recipient"];
Please let me know what I am doing wrong here. I am coding for iOS 5 on iPhone.
You have a 'copy' property, which means exactly that - your NSMutableDictionary will get the -copy method called and return a regular NSDictionary before being assigned to the synthesized instance variable. This thread provides some information on some of your options as to solving this.
For the sake of completing this thread I will include my revised Alibi class below, this works as I require it to. If anyone notices any memory leaks or other issues, that would be appreciated.
#implementation Alibi
NSMutableDictionary *_details;
- (Alibi *)init
{
self = [super init];
_details = [NSMutableDictionary dictionary];
return self;
}
- (NSMutableDictionary *)copyDetails
{
return [_details mutableCopy];
}
- (NSMutableDictionary *)setDetails:(NSMutableDictionary *)value
{
_details = value;
return value;
}
- (void)addDetail:(id)value forKey:(id)key
{
[_details setObject:value forKey:key];
}
- (id)getDetailForKey:(id)key
{
return [_details objectForKey:key];
}
- (Alibi *)copyWithZone:(NSZone *)zone
{
Alibi *theCopy = [[Alibi alloc] init];
theCopy.serverId = [self.serverId copyWithZone:zone];
theCopy.user = [self.user copyWithZone:zone];
theCopy.startTime = [self.startTime copyWithZone:zone];
theCopy.endTime = [self.endTime copyWithZone:zone];
[theCopy setDetails:[self copyDetails]];
return theCopy;
}
#end

Custom Object becoming _NSCFString upon entry into NSMutableArray

I'm having issues placing a custom object (WSWCMPost) into an NSMutableArray and then accessing the data stored in it later. Below is the relevant code.
Here is "WSWCMPost.h"
#import <Foundation/Foundation.h>
#interface WSWCMPost : NSObject
{
NSString *postBody;
NSString *postTitle;
NSString *postID;
}
#property (nonatomic, retain) NSString *postBody, *postTitle, *postID;
- init;
- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title;
- (NSString*)postBody;
- (NSString*)postTitle;
- (NSString*)postID;
Here is "WSWCMPost.m"
#import "WSWCMPost.h"
#implementation WSWCMPost
#synthesize postBody, postTitle, postID;
- (id)init {
self = [super init];
if(self) {
postID = #"none";
postBody = #"none";
postTitle = #"none";
}
}
- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title {
postTitle = title;
postID = ID;
postBody = body;
}
#end
And here is the "viewDidLoad" method that is causing my issues
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.detailViewController = (WSWCMDetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
// getting an NSString
NSLog(#"Pulling saved blogs...");
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"wswcmt1"];
if (dataRepresentingSavedArray != nil)
{
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil)
_objects = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
_objects = [[NSMutableArray alloc] init];
}
NSLog(#"Pulled saved blogs...");
NSLog(!_objects ? #"Yes" : #"No");
#try {
NSLog(#"_objects description: %#",[_objects description]);
NSLog(#"_objects[0] postID: %#",[[_objects objectAtIndex:0] postID]);
}
#catch (NSException *exception) {
NSLog(#"Caught exception %#", exception);
NSLog(#"Objects doesnt exist, allocating memory...");
_objects = [[NSMutableArray alloc] init];
WSWCMPost *testPost = [[WSWCMPost alloc] initWithID:#"noID" AndBody:#"noBody" AndTitle:#"noTitle"];
[_objects insertObject:testPost atIndex:0];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_objects] forKey:#"wswcmt1"];
}
if (!_objects ) {
NSLog(#"Objects doesnt exist...");
_objects = [[NSMutableArray alloc] init];
WSWCMPost *testPost = [[WSWCMPost alloc] initWithID:#"dne" AndBody:#"Dne" AndTitle:#"DNe"];
[_objects insertObject:testPost atIndex:0];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_objects] forKey:#"wswcmt"];
}
[self refreshButton:nil];
}
And finally, here is the output
2012-06-25 22:39:49.345 WSWCM[4406:907] Pulling saved blogs...
2012-06-25 22:39:49.352 WSWCM[4406:907] Pulled saved blogs...
2012-06-25 22:39:49.355 WSWCM[4406:907] Yes
2012-06-25 22:39:49.356 WSWCM[4406:907] _objects description: (null)
2012-06-25 22:39:49.358 WSWCM[4406:907] _objects[0] postID: (null)
2012-06-25 22:39:49.360 WSWCM[4406:907] Objects doesnt exist...
2012-06-25 22:39:49.363 WSWCM[4406:907] Refresh Triggered...
I think that is all of the relevant code. If i forgot anything let me know please. This issue has been bothering me for hours...
While I'm not positive why it's giving you NSStrings instead of just blowing up normally, the problem seems to stem from the fact that your custom class, WSWCMPost, does not conform to the NSCoding protocol. Make sure that your custom objects implement this protocol if you want to store them in NSUserDefaults, since it doesn't know how to serialize the data otherwise.
To be more exact, you'll have to add these methods to your class implementation:
- (id)initWithCoder:(NSCoder *)coder {
self = [self initWithID:[coder decodeObjectForKey:#"id"] AndBody:[coder decodeObjectForKey:#"body"] AndTitle:[coder decodeObjectForKey:#"title"]];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[encoder encodeObject:postID forKey:#"id"];
[encoder encodeObject:postBody forKey:#"body"];
[encoder encodeObject:postTitle forKey:#"title"];
}
This will allow the data to be serialized by NSCoder. Once you've done this, you should clear all the information currently stored by NSUserDefaults to make sure that it doesn't contain any more NSStrings, but then everything should work properly. Of course, you'll have to update these two methods if you change the data stored by your WSWCMPost object.
Another thing to mention, you're having collisions with your getters/setters and their respective instance variables. So your implementation is:
interface
#interface WSWCMPost : NSObject
{
NSString *postBody; // don't need to do these anymore for properties
NSString *postTitle;
NSString *postID;
}
#property (nonatomic, retain) NSString *postBody, *postTitle, *postID;
implementation
#implementation WSWCMPost
#synthesize postBody, postTitle, postID;
- (id)init {
self = [super init];
if(self) {
postID = #"none"; // not prefixing your variables with 'self' so they are not getting retained
postBody = #"none";
postTitle = #"none";
}
}
#end
Here's how you should be writing those out:
interface
/** NOTE: No need to specify your instance variables here anymore, just the properties */
#interface WSWCMPost : NSObject
#property (nonatomic, retain) NSString *postID;
#property (nonatomic, retain) NSString *postTitle;
#property (nonatomic, retain) NSString *postBody;
implementation
#implementation WSWCMPost
/** Now you specify the corresponding instance variable name alongside the property name */
#synthesize postBody=_postBody, postTitle=_postTitle, postID=_postID;
- (id)init {
self = [super init];
if(self) {
self.postID = #"none"; //getting retained
self.postBody = #"none";
self.postTitle = #"none";
}
}
That would definitely cause data to be released too soon.
So the previous way you could type in self.postID or postID and the compiler wouldn't complain. The difference is when you type postID it is actually setting the member variable and not retaining it... where self.postID will release whatever it is currently set to and retain the new value if it's different.
By declaring your properties the new way, you have to either call the setter as self.postID or set the underlying instance variable as _postID. A lot of early iPhone books had you bang out properties that way and it just ends up causing all sorts of memory issues.
Hope this helps!
UPDATE!!!
You forgot to return self in your constructor ;) I bet that's it
- (id)init {
self = [super init];
if(self) {
self.postID = #"none"; //getting retained
self.postBody = #"none";
self.postTitle = #"none";
}
return self; // THIS IS WHY, you're constructor doesn't return an instance of the class... add this please
}
- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title {
if(( self = [super init] ))
{
self.postTitle = title;
self.postID = ID;
self.postBody = body;
}
return self;
}
Your output definitely shows what was wrong in your code.
2012-06-25 21:51:07.691 WSWCM[4049:907] -[__NSCFString postID]: unrecognized selector sent to instance 0x1d003e80
2012-06-25 21:51:07.696 WSWCM[4049:907] Caught exception -[__NSCFString postID]: unrecognized selector sent to instance 0x1d003e80
These two lines tell you that NSString object does not recognize selector postID. This hint should be enough to find out where you need to see in depth.
See this Storing custom objects in an NSMutableArray in NSUserDefaults for more information.

cannot access mutable array in singleton

singleton.h
#import <Foundation/Foundation.h>
#interface CrestronControllerValues : NSObject {
NSString* ipAddress;
NSString* portNumber;
NSString* phoneAddress;
NSString* cameleonVersion;
NSString* systemName;
NSString* iPID;
NSString* systemFeedBackName;
NSString* dJoinConnectedFB;
NSString* dJoinLow;
NSString* dJoinHigh;
NSString* aJoinLow;
NSString* aJoinHigh;
NSString* sJoinLow;
NSString* sJoinHigh;
NSMutableArray *currentPhonebookEntriesTelepresence;
NSMutableArray *currentPhonebookEntriesVideoChat;
NSMutableArray *currentPhonebookEntriesAudioChat;
}
#property (nonatomic, retain) NSString* ipAddress;
#property (nonatomic, retain) NSString* portNumber;
#property (nonatomic, retain) NSString* phoneAddress;
#property (nonatomic, retain) NSString* cameleonVersion;
#property (nonatomic, retain) NSMutableArray *currentPhonebookEntriesTelepresence;
#property (nonatomic, retain) NSMutableArray *currentPhonebookEntriesVideoChat;
#property (nonatomic, retain) NSMutableArray *currentPhonebookEntriesAudioChat;
#property (nonatomic, retain) NSString* systemName;
#property (nonatomic, retain) NSString* iPID;
#property (nonatomic, retain) NSString* systemFeedBackName;
#property (nonatomic, retain) NSString* dJoinConnectedFB;
#property (nonatomic, retain) NSString* dJoinLow;
#property (nonatomic, retain) NSString* dJoinHigh;
#property (nonatomic, retain) NSString* aJoinLow;
#property (nonatomic, retain) NSString* aJoinHigh;
#property (nonatomic, retain) NSString* sJoinLow;
#property (nonatomic, retain) NSString* sJoinHigh;
+ (id)sharedManager;
#end
i have my singleton.m:
static CrestronControllerValues *sharedMyManager= nil;
#implementation CrestronControllerValues
#synthesize ipAddress, portNumber ,systemName, iPID, systemFeedBackName, dJoinConnectedFB, dJoinLow, dJoinHigh, aJoinLow, aJoinHigh, sJoinLow, sJoinHigh, cameleonVersion, currentPhonebookEntriesAudioChat, currentPhonebookEntriesTelepresence, currentPhonebookEntriesVideoChat, phoneAddress;
+(CrestronControllerValues*)sharedManager
{
#synchronized(self) {
if(!sharedMyManager) {
sharedMyManager = [CrestronControllerValues alloc];
sharedMyManager = [sharedMyManager init];
}
}
}
+(id)alloc
{
#synchronized(self)
{
NSAssert(sharedMyManager == nil, #"Attempted to allocate a second instance of a singleton.");
sharedMyManager = [super alloc];
return sharedMyManager;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
self.ipAddress = #"10.8.40.64";
self.portNumber = 41794;
self.systemName = #"";
self.iPID = 3;
self.cameleonVersion = nil;
self.currentPhonebookEntriesAudioChat = [[NSMutableArray alloc]initWithObjects:nil];
self.currentPhonebookEntriesTelepresence = [[NSMutableArray alloc]initWithObjects:nil];
self.currentPhonebookEntriesVideoChat = [[NSMutableArray alloc]initWithObjects:nil];
self.phoneAddress = nil;
self.systemFeedBackName = #"";
self.dJoinConnectedFB = 5000;
self.dJoinLow = 1;
self.dJoinHigh = 1000;
self.aJoinLow = 1;
self.aJoinHigh = 1000;
self.sJoinLow = 1;
self.sJoinHigh = 1000;
}
return self;
}
return self;
}
-(void)setPhoneAddress:(NSString *)phoneaddress
{
#synchronized(self) {
if (phoneAddress != phoneaddress)
{
[phoneAddress release];
phoneAddress = [phoneaddress retain];
}
}
}
-(NSString*)getPhoneAddress
{
return phoneAddress;
}
-(void)setCurrentPhonebookEntriesAudioChat:(NSMutableArray *)entries
{
#synchronized(self) {
if (currentPhonebookEntriesAudioChat != entries)
{
[currentPhonebookEntriesAudioChat release];
currentPhonebookEntriesAudioChat = [entries retain];
}
}
}
-(NSMutableArray*)getCurrentPhonebookEntriesAudioChat
{
return currentPhonebookEntriesAudioChat;
}
-(void)setCurrentPhonebookEntriesTelepresence:(NSMutableArray *)entries
{
#synchronized(self) {
if (currentPhonebookEntriesTelepresence != entries)
{
[currentPhonebookEntriesTelepresence release];
currentPhonebookEntriesTelepresence = [entries retain];
}
}
}
-(NSMutableArray*)getCurrentPhonebookEntriesTelepresence
{
return currentPhonebookEntriesTelepresence;
}
-(void)setCurrentPhonebookEntriesVideoChat:(NSMutableArray *)entries
{
#synchronized(self) {
if (currentPhonebookEntriesVideoChat != entries)
{
[currentPhonebookEntriesVideoChat release];
currentPhonebookEntriesVideoChat = [entries retain];
}
}
}
-(NSMutableArray*)getCurrentPhonebookEntriesVideoChatLocal
{
return currentPhonebookEntriesVideoChat;
}
-(void)setCameleonVersion:(NSString *)cameleonversion
{
cameleonVersion = cameleonversion;
}
-(NSString*)getCameleonVersion
{
return cameleonVersion;
}
-(void)setIPaddress:(NSString *)ipaddress
{
ipAddress = ipaddress;
}
-(NSString*)getIPaddress
{
return ipAddress;
}
-(void)setPortNumber:(NSString *)portnumber
{
portNumber = portnumber;
}
-(NSString*)getPortNumber
{
return portNumber;
}
-(void)setSystemName:(NSString *)systemname
{
systemName = systemname;
}
-(NSString*)getSystemName
{
return systemName;
}
-(void)setIPID:(NSString *)ipid
{
iPID=ipid;
}
-(NSString*)getIpid
{
return iPID;
}
-(void)setSystemFeedBackName:(NSString *)systemfeedbackname
{
systemFeedBackName=systemfeedbackname;
}
-(NSString*)getSystemFeedBackName
{
return systemFeedBackName;
}
-(void)setDJoinConnectedFB:(NSString *)djoinconnectedfb
{
dJoinConnectedFB = djoinconnectedfb;
}
-(NSString*)getDJoinConnectedFB
{
return dJoinConnectedFB;
}
-(void)setDJoinLow:(NSString *)djoinlow
{
dJoinLow=djoinlow;
}
-(NSString*)getDJoinLow
{
return dJoinLow;
}
-(void)setDJoinHigh:(NSString *)djoinhigh
{
dJoinHigh = djoinhigh;
}
-(NSString*)getDJoinHigh
{
return dJoinHigh;
}
-(void)setAJoinLow:(NSString *)ajoinlow
{
aJoinLow = ajoinlow;
}
-(NSString*)getAJoinLow
{
return aJoinLow;
}
-(void)setAJoinHigh:(NSString *)ajoinhigh
{
aJoinHigh = ajoinhigh;
}
-(NSString*)getAJoinHigh
{
return aJoinHigh;
}
-(void)setSJoinLow:(NSString *)sjoinlow
{
sJoinLow = sjoinlow;
}
-(NSString*)getSJoinLow
{
return sJoinLow;
}
-(void)setSJoinHigh:(NSString *)sjoinhigh
{
sJoinHigh = sjoinhigh;
}
-(NSString*)getSJoinHigh
{
return sJoinHigh;
}
- (void)dealloc
{
[self.ipAddress release];
[self.iPID release];
[self.portNumber release];
[self.currentPhonebookEntriesVideoChat release];
[self.currentPhonebookEntriesTelepresence release];
[self.currentPhonebookEntriesAudioChat release];
[self.aJoinHigh release];
[self.aJoinLow release];
[self.cameleonVersion release];
[self.sJoinHigh release];
[self.sJoinLow release];
[self.dJoinHigh release];
[self.dJoinLow release];
[self.dJoinConnectedFB release];
[super dealloc];
}
#end
and then i use it in 3 classes total
in one i set values:
if i read values from the CCV (sharedobject) i get the correct values. but this is in the same class as they are set from
CCV = [CrestronControllerValues sharedManager];
CCV.currentPhonebookEntriesAudioChat = currentPhonebookEntriesAudioChat;
and another i read the values:
(these show/read as nil)
switch (viewOptions) {
case 1:
[self setTableArray:CCV.currentPhonebookEntriesVideoChat];
break;
case 2:
[self setTableArray:CCV.currentPhonebookEntriesVideoChat];
break;
case 3:
[self setTableArray:CCV.currentPhonebookEntriesTelepresence];
break;
case 4:
[self setTableArray:CCV.currentPhonebookEntriesAudioChat];
break;
default:
[self setTableArray:CCV.currentPhonebookEntriesVideoChat];
break;
}
but besides the class that i actually set the values in i do not get the filled array when i access it from another class
i have done NSLOG(#"%#", CCV) and from what i can see all three classes have the same pointer so the shared instance seems to be working
Here is a simplier singleton pattern, less code is more:
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
_sharedSingleton = [[MySingleton alloc] init];
}
return _sharedMySingleton;
}
sharedMyManager has not been set at the time you are initializing the ivars.
In a init it is best practice to set the ivars directly, that is do not use setters such as created by #synthesize, the class is not completely established so calling methods on it is not a great idea.
A singleton is just a class like any other class with one exception, there is only one. Also all the extra methods to guarantee a singleton are really just noise that is best not present--but that is a matter of taste.
Consider:
sharedMyManager = [[super allocWithZone:NULL] init];
Rewrite it as:
id x = [super allocWithZone:NULL];
id y = [x init];
sharedMyManager = y;
When init is executed, the assignment to sharedMyManager hasn't been evaluated yet. Thus, sharedMyManager is nil and all your assignments are no-ops in your init method.
In your init method, you should always refer to your instance variables through self; either by directly assignment to them (which is a reference to self, really) or using the setter methods directly (i.e. self.foo = 442;).
(This is what #CocoaFu said, but clarified)
Looking at the code a little more closely, there are a ton of problems with it.
NSString properties should be copy, not retain.
you are leaking all of the currentPhonebookEntries* mutable arrays.
Getter methods should not have the prefix get*
there is no need to implement any of those getter/setter methods when using #synthesize (and you are actually creating two getter methods for each; one with and one without the get prefix).
the dealloc method should either directly release the instance variables or it should set the properties to nil; the [self.ivar release] is discouraged.
The code I showed above is merely illustrative. If your init still assigns through sharedMyManager, you didn't fix the problem.
so in the end all i can do is apologize. none of you had the code that you would have needed to see what was going on.
here is the array being saved (aboved was abridged (bad idea))
if ([phonebookEntriesAudioChat count] >=8) {
[CCV setCurrentPhonebookEntriesAudioChat:phonebookEntriesAudioChat];
[phonebookEntriesAudioChat removeAllObjects];
}
basically i was tring to add an item to the array from a socket return. getting one address up to 8 for each return/message. so i populated a temporary array (phonebookEntriesAudioChat) and added one to it for each message and once it got to 8 saved it to my singleton (CCV). but some how (and im still trying to figure this out) it would get to 8, be saved, temporary array cleared, then resaved the array (an empty one) to the singleton.
thanks for all the help and direction, i know i dont get points for my own answer if one of you wants some easy points just re answer with a simliar description as this and ill give u the check. otherwise im just going to vote up ur comments and mark this as the answer in a day or two.

Objective-C for Dummies: How do I loop through an NSDictionary inside of an NSDictionary?

Alright guys, I'm quite confused. So, I have an NSDictionary which is populated by a JSON string which looks like:
{"Success":true,"Devices":[{"UDId":"...","User":"...","Latitude":0.0,"Longitude":0.0}]}
Now, I know how to check if Success is true, but I need to loop through the array of Devices (JSON object) and create an internal array of Devices (internal app object) and I have no idea how to do that. Can someone please explain how to do it?
Here's my Device.m/h:
#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>
#interface Device : NSObject {
NSString *udId;
NSString *name;
NSNumber *latitude;
NSNumber *longitude;
}
#property (nonatomic, retain) NSString *udId;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSNumber *latitude;
#property (nonatomic, retain) NSNumber *longitude;
#pragma mark -
#pragma mark MKAnnotation Properties
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#end
----
#import "Device.h"
#implementation Device
#synthesize udId, name, latitude, longitude;
- (CLLocationCoordinate2D)coordinate {
CLLocationCoordinate2D internalCoordinate;
internalCoordinate.latitude = [self.latitude doubleValue];
internalCoordinate.longitude = [self.longitude doubleValue];
return internalCoordinate;
}
- (void)dealloc {
[udId release];
udId = nil;
[name release];
name = nil;
[latitude release];
latitude = nil;
[longitude release];
longitude = nil;
[super dealloc];
}
#end
And here's the methods where I should be reading the response and converting it to objects I can use:
- (void)requestFinished:(ASIHTTPRequest *)request {
if (![request error]) {
NSError *jsonError = nil;
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithJSONString:[request responseString] error:&jsonError];
if (!jsonError || ([[jsonDictionary objectForKey:#"Success"] intValue] == 1)) {
// READ "DEVICES" AND CONVERT TO OBJECTS
} else {
// AUTHORIZATION FAILED
}
}
}
I'd really appreciate some help on this. I just can't seem to wrap my head around it...
Thanks in advance!
You are almost there. In your code where you say:
// READ "DEVICES" AND CONVERT TO OBJECTS
do this:
NSArray * devices = [jsonDictionary objectForKey:#"Devices"];
for(NSDictionary * deviceInfo in devices) {
Device * d = [[[Device alloc] init] autorelease];
[d setLatitude:[deviceInfo objectForKey:#"Latitude"]];
[d setLongitude:[deviceInfo objectForKey:#"Longitude"]];
[d setName:[deviceInfo objectForKey:#"User"]];
[d setUdId:[deviceInfo objectForKey:#"UDId"]];
// do some stuff with d
}
What's going on here: I didn't see what JSON library you are using to convert, but presuming it works like TouchJSON or SBJSON, the JSON array is automatically turned into an NSArray instance, while the inner hashes of the NSArray are NSDictionary objects. At the point that you have deserialized that JSON string, everything you're dealing with will be instances of NSString, NSNumber, NSArray and NSDictionary (and depending on the library, NSNull to represent null values).
First you need to define your initializer/constructor for your Device class.
Device.h
- (id)initWithUdid:(NSString *)udid name:(NSString *)name latitude:(NSNumber *)lat longitude:(NSNumber *)lon;
Device.m
- (id)initWithUdid:(NSString *)udid name:(NSString *)name latitude:(NSNumber *)lat longitude:(NSNumber *)lon {
if (self = [super init]) {
self.udid = udid;
self.name = name;
self.latitude = lat;
self.longitude = lon;
}
return self;
}
Then you can initialize a new object like:
Device *dev = [[Device alloc] initWithUdid:#"a udid" name:#"the name" latitude:latNum longitude:lonNum];
So, you should be able to iterate the array and build your Device objects like so:
NSArray *devicesArray = [dict objectForKey:#"Devices"];
for (NSDictionary *d in devicesArray) {
Device *dev = [[Device alloc] initWithUdid:[d objectForKey:#"UDId"]
name:[d objectForKey:#"User"]
latitude:[NSNumber numberWithDouble:[d objectForKey:#"Latitude"]]
longitude:[NSNumber numberWithDouble:[d objectForKey:#"Latitude"]]];
}
You want to access the array of device dictionaries from the top-level dictionary just as you did the Success value. Then iterating over the dictionaries you can use each's -keyEnumerator method to iterate over its keys.
- (void)requestFinished:(ASIHTTPRequest *)request {
if (![request error]) {
NSError *jsonError = nil;
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithJSONString:[request responseString] error:&jsonError];
if (!jsonError || ([[jsonDictionary objectForKey:#"Success"] intValue] == 1)) {
NSArray* deviceArray = [jsonDictionary objectForKey:#"Devices"];
for(NSDictionary* dict in deviceArray)
{
for(NSString* key in [dict keyEnumerator])
{
NSLog(#"%# -> %#", key, [dict objectForKey:key]);
}
}
// READ "DEVICES" AND CONVERT TO OBJECTS
} else {
// AUTHORIZATION FAILED
}
}
}
Sounds like you need to reuse your line:
[jsonDictionary objectForKey:#"Success"]
try having a look at
[jsonDictionary objectForKey:#"Devices"]
You really need to figure out what type it returns.
If you're lucky, it returns an NSDictionary, or alternately something that you can easily turn into an NSDictionary.