Objective-C/cocoa losing array values - cocoa

I have multiple arrays, however, they are not retaining their data for use in another method.
Here's how I have it set up (simplified)
.h
NSArray *array;
#property (nonatomic, copy) NSArray *array;
-(void)someMethod:(NSArray*)someArray;
-(void)heresNewMethod;
.m
-(void)someMethod:(NSArray*)someArray
{
array = [someArray copy];
}
-(void)heresNewMethod //gets called by method not shown
{
NSLog(#"%#", array);
}

One of two things happened:
You sent the object a someMethod: message, passing nil (probably without meaning to). A message to nil returns nil, so you assigned nil—as the result of the copy message—to the array instance variable. Even if you had stashed a pointer to an array there previously, you replaced it with nil in your response to this someMethod: message.
You never sent the object a someMethod: message. Since instance variables are initialized to nil, and you never put anything different in the array instance variable, it still contains nil.
Sprinkle more NSLog statements in your code to test the first theory. The truth is either one or the other, so confirming the first theory disproves the second, and vice versa.

Except for the fact that you'll leak whatever was in array every time you call someMethod:, that code should work. What's the problem you see?

The only answer is that the code you provided is not the code your using, and the difference is crucial. I mean, you declare a property which you then don't use, and it's not clear whether you are defining your accessors properly, or if array is also a local which is shadowing your property, or what.
Please post your real code.

Related

Detect attempt to insert nil on a dictionary and log a backtrace

Is there a way I can detect an attempt to insert a nil value on a dictionary and log a backtrace on my application ?. I know how to do it with Xcode, but the error occurs only with some users. Hence I need to send them a new build that hopefully would log a backtrace of the attempted nil insertion.
This is probably because an image or a font is not being loaded correctly, if there is another way to find out I would also like to know.
You can't do this with regular NSMutableDictionary objects, as adding a nil value is legal.A workaround would be to use a custom dictionary implementation that wraps a NSDictionary instance and forwards all methods to the wrapped objects; and in the case of setObject:forKey: (or setValue:forKey:) makes a check and logs the backtrace if the value is nil. The downside is that you'll have a lot of boiler plate code to write. You can reduce the boiler plate code size if you implement only the methods needed by your code.
Another approach would be to use method swizzling and replace the setObject:forKey: and setValue:forKey: with your method that firstly checks the value and if OK forwards the call to the original method. However NSDictionary being a class cluster you might experience problems with this approach.
Update. Just thought of a 3rd solution: add a category over NSMutableDictionary with getters/setters for the keys you're interested in, and update your code to call those setters instead of the setObject:forKey: method.
As I understand your problem you have failed to check the result when loading an image, font or something similar and this is causing an error when the bad result is later inserted into a dictionary. What you are after is a quick way, as you have a large codebase, to track down that insertion so you can backtrack and find the source of the problem and add appropriate checking code to the load/whatever.
What you can do is:
Replace NSMutableDictionary with a simple class, say DebuggingDictionary, which appears to be (explained below) a derived class and just checks for nil on insertion and produces the diagnostics you are after; and
Do a find/replace over your code base for [NSMutableDictionary alloc] and replace with [DebuggingDictionary alloc]. You can easily change this back once the problem has been fixed.
So how to write DebuggingDictionary?
Well as NSMutableDictionary is a class cluster you cannot just derive from it and override setObject:forKey:, you have provide your own storage for the keys & objects and override six key methods and all (or at least all you use) of the init methods.
Sounds bad but it isn't. First read this answer to a different but related question. In that answer a version of NSMutableArray is created which checks the type of elements added, you need to check whether the items are nil. The implementation provides the storage by wrapping a real NSMutableArray. You can do the equivalent with NSMutableDictionary, the documentation (NSMutableDictionary and NSDictionary) lists the six primitive methods you need to override.
That answer also adds its own initWithClass: initialisers and blocks the standard ones, you just need to implement the standard dictionary ones - by calling them on the wrapped dictionary.
[Minimal checking in the following code sketches, all typed directly into answer so beware of typos]
So for example initWithCapacity: becomes something like:
- (instancetype) initWithCapacity:(NSUInteger)numItems
{
realDictionary = [NSMutableDictionary dictionaryWithCapacity:numItems];
return self;
}
and the core insertion method becomes:
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey
{
if (anObject == nil)
{
// produce your diagnostics
}
else
realDictionary[aKey] = anObject;
}
Once you've tracked your problem to its source and fixed it there just remove your DebuggingDictionary and find/replace all occurrences in your code with NSMutableDicitionary.
HTH
You could create subclass of NSAplication and override method reportException
Use
+[NSThread callStackSymbols];
or
-[NSException callStackSymbols];
to get a backtrace. You can print a backtrace using NSLog.
You may find also Apple's example useful for you:
ExceptionReporting:
Demonstrates how to show a customized exception reporting user interface.
This lets the user know when the exception happens in order to possibly prevent
subsequent random crashes that are difficult to debug.
Instead of setting key/value directly to the dictionary, how about using a method that accepts parameters that should be inserted into the dictionary and tests each for nil before adding it to the dict?
-(void)addKeyAndValueToDict:(NSString*)aKey andValue:(NSString *)aValue {
if ( aValue == nil ) {
NSLog(#"value was nil for key: %#", aKey);
return;
}
[self.someDict setValue:aValue forKey:aKey];
}

Why is my core data object loosing it's relationship?

I have the following code in viewDidLoad:
NSManagedObjectContext *moc=[[NSManagedObjectContext alloc]
initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc setParentContext:[AppCoreDataHandler sharedManagedObjectContext]];
self.restroom=[NSEntityDescription insertNewObjectForEntityForName:#"Restroom"
inManagedObjectContext:moc];
self.restroom.coordinate=[((AppDelegate *)[[UIApplication sharedApplication] delegate]) locationManager].location.coordinate;
self.review=[NSEntityDescription insertNewObjectForEntityForName:#"Review"
inManagedObjectContext:moc];
[self.restroom addReviewsObject:self.review];
Testing using the debug console shows that self.restroom.reviews is equal to an NSSet containing a review. By the time I reach 'viewWillAppear', self.restroom.reviews is nil. I'm not setting reviews to nil ANYWHERE in my code. Self.review remains completely valid the entire time. It simply doesn't make sense!
I'm at my wits end trying to figure this out. It can't be any multithreaded code, I'm not assigning anything else to the property/relationship, the object isn't weakly retained and releasing itself, it just... vanishes. Why?
Edit: Even more fun, I used key value observing to check on WHEN the value is changed.
From what I can read of the stack trace, it's being changed INSIDE the viewDidLoad function. But when I step-through the code, the key value notifications are clearly sent AFTER the function ends.
By pure dumb luck (read: hrm, I wonder if... nope. Ok then. I wonder if... and repeat for about two bazillions iterations), I figured it out.
The child MOC I created for these objects gets deallocated not after the last reference to it (which is what I'd expect ARC to do), but rather at the end of the function. And when it dies, it didn't deallocate the objects it was assigned to, but it did strip them of their relationships. (I'm going to guess that while it LOOKS like an NSSet is providing those, it was actually something in the guts of the MOC).

Accessing an NSMutableArray inside my custom object

I'm sure this is an complete Noob question... but I've actually never had to deal with this scenario before so I'm a bit befuddled...
Let's say I have a custom object I'll call person, and each person object can have an array of "possessions", a kind of inventory if you will. I would set it up like this:
interface person : NSObject {
NSString *name;
NSMutableArray *posessions;
#property (copy) NSString *name;
#property (copy) NSMutableArray *posessions; // no idea if this is even necessary...
}
Of course, I would also synthesize my properties in the implementation file... Now, in my actual controller object, I would make an instance of my object (or usually an array of instances, but for this example, one will work fine...) as so:
person *aPerson;
I know that to access the persons name, I could make a call like this:
[aPerson setName:#"Bob"];
and to retrieve that name, I might use this:
aVar = [aPerson name];
What I'm stuck on is how exactly would I go about adding or retrieving objects to the NSMutableArray located inside my person class? Let's say I want to use the "count" method for the NSMutable Array.
I've done some trial and error with attempts such as:
[aPerson.posessions count];
[[aPerson posessions] count];
Likewise, to add an object to an array, I have often used:
[someArray addObject:anObject];
but attempts like this haven't worked:
[aPerson.posessions addObject:anObject];
After reading up a bunch and searching the web, I can't seem to find exactly how to interact with this NSMutableArray in my custom class. I'm sure it's something obvious that I'm just not quite getting, and it's become a sort of mental block...
Also, am I correct in synthesizing accessor properties for the NSMutableArray? If so, setX and X don't seem to be quite so obvious with NSMutableArray... unless they simply copy the entire array into a local variable...
Perhaps is this what needs to be done? use the accessor methods to get the entire array, place it in a local variable, make my changes, then use the set accessor method to put the entire array back into my person object?
Can someone enlighten me a bit on the syntax I should be using here?
* EDIT *
I thought I'd add a bit of clarification to this question. My custom objects (in the above example, my person object) are basically database records. I have several databases I am working with in my project, so for example:
Person - a custom sub-class of NSObject containing multiple NSString Objects, as well as Ints and BOOLs.
personDatabase - An Array of Person objects (set up and controlled within my main CONTROLLER object)
All of the set and get methods are called from "Controller".
What I have been attempting to do is to directly access the individual objects contained within the personDatabase from within my Controller object. I have done this by declaring another object this way:
Person *activePerson;
Then, all of my calls are made to the currently active Person record (the one currently selected from the personDatabase), such as:
someOutput = [activePerson name];
etc.
Is there a way to directly access the objects inside the NSMutableArray object inside the activePerson object from my Controller object?
You've specified the 'possessions' property as 'copy'. Therefore, when you write aPerson.possessions you are getting a copy of the possessions array. The call to addObject adds anObject to a new array that is a copy of aPerson's array of possessions. The simplest 'fix' would be to change 'copy' to 'retain' (and probably 'readonly'). [Edit: Wrong; it is 'copy on assign' - not 'copy on read']
However, there is a bigger issues. A person has possessions but how you store them is an implementation detail. When you put NSMutableArray in the public interface you overly restrict your implementation. You might be better served to change the Person interface along the lines of:
#interface Person : NSObject {
#private
NSString *name;
// ...
}
- (Boolean) addPossession: (NSObject *) obj;
- (Boolean) remPossession: (NSObject *) obj;
- (Boolean) hasPossession: (NSObject *) obj;
- (NSArray *) allPossessions;
#end
Then, how you implement these possession methods depends on if you use an array, a set, a linked-list, a tree, a whatever.

Archived NSData, isEqualToData: and Empty Strings

I have a custom object containing several NSString objects, some ints and a few bools. I am using NSKeyedArchiver to archive a copy of the object into an NSData object.
The user than makes changes to the object variables, which are connected to an IB interface.
After the changes are made, the new version of the object is archived into a second NSData object.
These two objects are compared using
[myNSData1 isEqualToData: myNSData2];
In most cases it works perfectly well, but there is one very troubling situation:
Let's say the object had a variable initialized as follows:
NSString *myString = #"";
After the object was archived into myNSData1, we called the following:
myString = [myNSTextField stringValue];
Logging myString to the console reveals that the value of myString is still
#""
and thus has not changed value.
We now archive the object into myNSData2.
Upon executing the comparison statement above, however, it now returns FALSE. It ONLY returns FALSE if the original assignment of #"" is replaced with the #"" contained in the textfield using stringValue.
Why is this?
Have a look at the types of those strings (NSLog([myString className]) should work), because NSString is a class cluster. I'm guessing that you'll find that and one of those strings is an NSCFString, and the other is an NSCFConstantString. The archiver encodes type information, so if the types are different, the NSData will also be different.
I wouldn't rely on the NSData objects being identical. If you want to compare the two, you'd be better off unarchiving them and using isEqual:. That way, you'd have full control.

NSArrayController and KVO

What do I need to do to update a tableView bound to an NSArrayController when a method is called that updates the underlying array? An example might clarify this.
When my application launches, it creates a SubwayTrain. When SubwayTrain is initialised, it creates a single SubwayCar. SubwayCar has a mutable array 'passengers'. When a Subway car is initialised, the passengers array is created, and a couple of People objects are put in (let's say a person with name "ticket collector" and another, named "homeless guy"). These guys are always on the SubwayCar so I create them at initialisation and add them to the passengers array.
During the life of the application people board the car. 'addPassenger' is called on the SubwayCar, with the person passed in as an argument.
I have an NSArrayController bound to subwayTrain.subwayCar.passengers, and at launch my ticket collector and homeless guy show up fine. But when I use [subwayCar addPassenger:], the tableView doesn't update. I have confirmed that the passenger is definitely added to the array, but nothing gets updated in the gui.
What am I likely to be doing wrong? My instinct is that it's KVO related - the array controller doesn't know to update when addPassenger is called (even though addPassenger calls [passengers addObject:]. What could I be getting wrong here - I can post code if it helps.
Thanks to anyone willing to help out.
UPDATE
So, it turns out I can get this to work by changing by addPassenger method from
[seatedPlayers addObject:person];
to
NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];
[newSeatedPlayers addObject:sp];
[seatedPlayers release];
[self setSeatedPlayers:newSeatedPlayers];
I guess this is because I am using [self setSeatedPlayers]. Is this the right way to do it? It seems awfully cumbersome to copy the array, release the old one, and update the copy (as opposed to just adding to the existing array).
I don't know if its considered a bug, but addObject: (and removeObject:atIndex:) don't generate KVO notifications, which is why the array controller/table view isn't getting updated. To be KVO-compliant, use mutableArrayValueForKey:
Example:
[[self mutableArrayValueForKey:#"seatedPlayers"] addObject:person];
You'll also want to implement insertObject:inSeatedPlayersAtIndex: since the default KVO methods are really slow (they create a whole new array, add the object to that array, and set the original array to the new array -- very inefficient)
- (void)insertObject:(id)object inSeatedPlayerAtIndex:(int)index
{
[seatedPlayers insertObject:object atIndex:index];
}
Note that this method will also be called when the array controller adds objects, so its also a nice hook for thinks like registering an undo operation, etc.
I haven't tried this, so I cannot say it works, but wouldn't you get KVO notifications by calling
insertObject:atArrangedObjectIndex:
on the ArrayController?
So, it turns out I can get this to work by changing by addPassenger method from
[seatedPlayers addObject:person];
to
NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];
[newSeatedPlayers addObject:sp];
[seatedPlayers release];
[self setSeatedPlayers:newSeatedPlayers];
I guess this is because I am using [self setSeatedPlayers]. Is this the right way to do it?
First off, it's setSeatedPlayers:, with the colon. That's vitally important in Objective-C.
Using your own setters is the correct way to do it, but you're using the incorrect correct way. It works, but you're still writing more code than you need to.
What you should do is implement set accessors, such as addSeatedPlayersObject:. Then, send yourself that message. This makes adding people a short one-liner:
[self addSeatedPlayersObject:person];
And as long as you follow the KVC-compliant accessor formats, you will get KVO notifications for free, just as you do with setSeatedPlayers:.
The advantages of this over setSeatedPlayers: are:
Your code to mutate the set will be shorter.
Because it's shorter, it will be cleaner.
Using specific set-mutation accessors provides the possibility of specific set-mutation KVO notifications, instead of general the-whole-dang-set-changed notifications.
I also prefer this solution over mutableSetValueForKey:, both for brevity and because it's so easy to misspell the key in that string literal. (Uli Kusterer has a macro to cause a warning when that happens, which is useful when you really do need to talk to KVC or KVO itself.)
The key to the magic of Key Value Observing is in Key Value Compliance. You initially were using a method name addObject: which is only associated with the "unordered accessor pattern" and your property was an indexed property (NSMutableArray). When you changed your property to an unordered property (NSMutableSet) it worked. Consider NSArray or NSMutableArray to be indexed properties and NSSet or NSMutableSet to be unordered properties. You really have to read this section carefully to know what is required to make the magic happen... Key-Value-Compliance. There are some 'Required' methods for the different categories even if you don't plan to use them.
Use willChangeValueForKey: and didChangeValueForKey: wrapped around a change of a member when the change does not appear to cause a KVO notification. This comes in handy when you are directly changing an instance variable.
Use willChangeValueForKey:withSetMutation:usingObjects: and didChangeValueForKey:withSetMutation:usingObjects: wrapped around a change of contents of a collection when the change does not appear to cause a KVO notification.
Use [seatedPlayers setByAddingObject:sp] to make things shorter and to avoid needlessly allocating mutable set.
Overall, I'd do either this:
[self willChangeValueForKey:#"seatedPlayers"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:sp];
[seatedPlayers addObject:sp];
[self didChangeValueForKey:#"seatedPlayers"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:sp];
or this:
[self setSeatedPlayers:[seatedPlayers setByAddingObject:sp]];
with the latter alternative causing an automatic invocation of the functions listed under 1. First alternative should be better performing.

Resources