Passing NSMutableArray to other classes - cocoa

I have created an NSMutableArray in the implementation of my class loginController. The mutable array contains a set of strings. I want to pass the mutable array with its objects to other classes within my cocoa-project. What is the best way to pass the array?

The most basic case is your login controller simply handing a snapshot of the array to the other controller. In this case, your login controller will need to have references to instances of the other classes, and it will set some property of those instances to the array. Remember to declare the properties with the copy attribute, so that the receivers don't hold on to your private mutable array.
If you want the other controllers to be able to modify the array, don't let them have your mutable array—that's an invitation to hard-to-find bugs.
Instead, you'll need to implement one property on the login controller, instead of one property on each of the other controllers. The login controller's property should have at least a getter and setter (which you can #synthesize), but you can implement more specific accessor methods for efficiency.
Once you have this property, the other controllers should access the property in a KVO-compliant way. If you implement the specific accessors, they can just use those. Otherwise, they'll need to send mutableArrayValueForKey: to the login controller. When they access the contents of that proxy array, they really access the login controller's array; when they mutate the proxy array, they mutate the login controller's array in turn.
Next comes the actual KVO part. You'll want the other controllers to know when one of them (or the login controller) changes the property. Have each controller (except the login controller) add itself as an observer of the property of the login controller. Remember to have them remove themselves in their -dealloc (or -finalize) methods.
In order for the right notifications to get posted, everything needs to use either accessors or mutableArrayValueForKey:. That goes for the login controller itself, too—it should use its own accessors when mutating the array, instead of messaging the array directly. The only exceptions are in init and dealloc (because the accessor messages would be messages to a half-inited/deallocked object, which will be a problem if you ever make the accessors fancy*).
BTW, it sounds like you may have way too many controllers. See if you can't move some of your logic into model objects instead. That drastically simplifies your code, as Cocoa is designed to work with a model layer. Being controller-heavy is fighting the framework, which makes more work for you.
*By “fancy”, I mean doing things other than or in addition to the normal behavior of a given accessor method. For example, insertObject:in<Foo>AtIndex: normally just tail-calls [<foo> insertObject:atIndex:]; if you insert or store the object somewhere other than in an array in an instance variable, or if you do something else in the same method (such as tell a view that it needs to display), then your accessor method is fancy.

short answer that may not be the best practice:
[otherObject giveArray:[NSArray arrayWithArray:theMutableArray]];

the question is a good one, but not complete... do you just need to pass an array of strings or does the class you are passing to need to modify the array?
In general, it's not a problem to simply pass around an NSMutableArray*, however you need to be careful, because you are just passing a pointer ( so if you retain it somewhere, you need to be aware that the owner or some other class may modify the array ).
generally spoken you would want to use NSMutableArray to dynamically build up an array of objects and when you need to share them, then make a non-mutable copy and pass that along.
NSMutableArray* myArr = [NSMutableArray arrayWithObjects:#"1",#"2",#"3",#"four",nil];
// maybe modify the array here...
NSArray* nonMut = [[myArr copy] autorelease];
[someObject doWork:nonMut];
|K<

I think the pattern that's best for your situation is delegation. Your LoginController shouldn't have to know what class it's sending this data to. Instead, you would implement a LoginControllerDelegate protocol
#protocol LoginControllerDelegate <NSObject>
#optional
- (void)loginController:(LoginController *)loginController didReceiveLoginIDs:(NSArray *)ids;
#end
Then, in your LoginController class, you would implement a delegate property like this:
#property (nonatomic, assign) id <LoginControllerDelegate> delegate;
Then, when you've actually got something to communicate to the delegate, you would write this:
if ([self.delegate respondsToSelector:#selector(loginController:didReceiveLoginIDs:])
[self.delegate loginController:self didReceiveLoginIDs:[NSArray arrayWithArray:loginIDs]];
The object that should receive the login IDs would incorporate the LoginControllerDelegate protocol like this:
#interface SomeOtherClass : NSObject <LoginControllerDelegate>
And you would implement the loginController:didReceiveIDs: method in SomeOtherClass.
This way, instead of your LoginController needing to have intimate knowledge of the other classes in your project, you simply establish a mechanism for sending that data to whatever object is interested in it when it becomes available. If you later change which object should receive the login IDs, you only need to choose a different delegate.

Related

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.

Programmatically retrieving the selected object of an NSArrayController

When I use an NSArrayController with instances of NSManagedObject, I was under the impression that the following would give me its selected object:
[myArrayController selection]
However, this gives me an instance of some private NS Proxy class.
When I use:
[[myArrayController selectedObjects] objectAt: 0]
...all is fine and I have my instance of NSManagedObject.
I understand the necessity for these methods as you can probably have lists which allow for multiple selection. I do not understand why selection does not give me the instance of NSManagedObject.
It states in the documentation for NSObjectController (which NSArrayController inherits from) that this method returns a proxy object.
You will be able to use any KVC methods on the proxy object, but if you want any class - specific functionality you will have to use the second method in your sample above. As you've stated, this is to return a valid object in cases of no or multiple selection.

How is Key-Value Observing implemented internally?

I got answer about Foundation magic for this question: What's the most *simple* way to implement a plain data object which conforms key-value-observing?
What's the magic? How it work internally? Because it's dangerous using framework which I can't understand its internal behavior, I want to know its behavior. Currently, I cannot understand how it work without any method definitions.
Apple's documentation describes how KVO is implemented internally.
The gist of it is that when you register an observer on an object, the framework dynamically creates a subclass of the object's original class, and adjusts the object to appear as an instance of this new dynamic class. You can see this if you inspect an object in the debugger after it has had an observer registered.
This new class intercepts messages to the object and inspects them for those matching certain patterns (such as the getters, setters, and collection access).
In a nutshell: Objective-C 2.0's #property declaration creates accessor methods for the named property, so there are method definitions. #property is just a shorthand way to define them which avoids a lot of repetitious boilerplate code.
When you observe a property, a private subclass is created which implements accessors that call the appropriate notification methods before and after changing the property value. A technique known as "isa swizzling" is then used to change the class of the observed object.

What is the KVC Search Pattern for mutableArrayValueForKey?

I'm attempting to understand Cocoa's Key-Value Coding (KVC) mechanism a little better. I've read Apple's Key-Value Programming Guide but am still a little confused about how certain KVC methods search for keys. Particularly, mutableArrayValueForKey:.
Below I'm going to explain how I understand valueForKey: KVC "getters" to work. Then I'll get to my question regarding mutableArrayValueForKey.
There are seven different "getter" KVC methods:
- (id)valueForKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys;
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;
When searching for a Value inside a Property (named myKey), Apple's docs state that valueForKey: searches like this:
Tries -getMyKey, -myKey, and -isMyKey (in that order) inside the receiver
If not found, it attempts these ordered, to-many getters (NSArray):
// Required:
- (NSUInteger)countOfMyKey;
// Requires At Least One:
- (id)objectInMyKeyAtIndex:(NSUInteger)index;
- (NSArray *)myKeyAtIndexes:(NSIndexSet *)indexes;
// Optional (improves performance):
- (void)getMyKey:(KeyClass **)buffer range:(NSRange)inRange;
Next, it attempts these unordered, to-many getters (NSSet):
- (NSUInteger)countOfMyKey;
- (NSEnumerator *)enumeratorOfMyKey;
- (KeyClass *)memberOfMyKey:(KeyClass *)anObject;
Next, it attempts to access Instance Variables directly, assuming YES is returned by accessInstanceVariablesDirectly, in this order: _myKey, _isMyKey, myKey, isMyKey.
Lastly, it gives up and calls the receiving class's - (id)valueForUndefinedKey:(NSString *)key method. Usually an error is raised here.
My question is, what is the search order pattern for mutableArrayValueForKey:?
Apple's docs state this:
Accessor Search Pattern for Ordered
Collections
The default search pattern for
mutableArrayValueForKey: is as
follows:
The receiver's class is searched for a
pair of methods whose names match the
patterns -insertObject:inAtIndex:
and -removeObjectFromAtIndex:
(corresponding to the NSMutableArray
primitive methods
insertObject:atIndex: and
removeObjectAtIndex: respectively), or
methods matching the pattern
-insert:atIndexes: and -removeAtIndexes: (corresponding to the
NSMutableArrayinsertObjects:atIndexes:
and removeObjectsAtIndexes: methods).
If at least one insertion method and
at least one removal method are found
each NSMutableArray message sent to
the collection proxy object will
result in some combination of
-insertObject:inAtIndex:, -removeObjectFromAtIndex:, -insert:atIndexes:, and -removeAtIndexes: messages being sent to the original receiver of
mutableArrayValueForKey:.
...etc...
This makes no sense to me as it's discussing "setter" like methods. mutableArrayValueForKey: returns an NSMutableArray. All of the methods listed above return void, and are used to edit an NSMutableArray, not get it. Example:
- (void)insertMyKey:(KeyClass *)keyObject inMyKeyAtIndex:(NSUInteger)index;
- (void)removeObjectFromMyKeyAtIndex:(NSUInteger)index;
Any idea what Apple is trying to say in their docs, or if this is perhaps an error?
My theory is that mutableArrayValueForKey: is likely taking a similar path as valueForKey: when searching to retrieve a KVC value. I'm just not sure what path that really is.
Thanks for any help you can offer! :)
The NSMutableArray you get back from calling mutableArrayValueForKey: is actually a private subclass of NSMutableArray which overrides normal array methods such as -count, -objectAtIndex:, -insertObject:atIndex:, etc. and calls the corresponding KVC methods on the object the array was retrieved from. It basically acts as a proxy for manipulating the to-many relationship of the object, and it's not something you have to worry about creating or returning yourself. A quick example of usage:
Playlist* aPlaylist;
Track* aTrack;
NSMutableArray* mutableTracks = [aPlaylist mutableArrayValueForKey:#"tracks"];
[mutableTracks insertObject:aTrack atIndex:0];
This piece of code adds a track to the beginning of the playlist. If the Playlist class implements KVC methods for its "tracks" relationship, then calling a method on the mutable array will result in the appropriate method being called on the underlying object. So in this example, when you call insertObject:atIndex: on the array, the array will in turn call insertObjectInTracks:atIndex: on the playlist object, and the track gets added to the playlist's array of tracks.
Now, in this example, of course you could just call insertObjectInTracks:atIndex: directly, but there are several advantages you can get out of using mutableArrayValueForKey: instead.
The array wrapper hides the implementation details of the underlying KVC methods. Implementing the entire list of methods isn't strictly required to be KVC compliant. The Playlist class could just implement -tracks and -setTracks:, and the code above will still work. In this case, instead of calling insertObjectInTracks:atIndex:, the mutable array proxy will create a new array with the object inserted at the beginning, and then just call setTracks: on the Playlist object. This is obviously less efficient, so implementing the full list of KVC methods is usually recommended.
In the case where, instead of a constant string for the key, you instead have a variable, using mutableArrayValueForKey: allows you to manipulate the relationship without having to know the exact names of the methods you have to call. As long as the object is KVC compliant for the key you're using, everything will "just work".
It also lets you use any method that NSMutableArray itself implements, so for example you could use methods that search the array for objects, sort the array, etc. without having to rewrite special versions to deal with the KVC stuff.

Should "to-many" relationships be modelled as properties?

After reading the Key-Value Coding Programming Guide, the Key-Value Observing Programming Guide and the Model Object Implementation Guide, as well as reading many StackOverflow entries on the topic and experimenting with various modelling scenarios, I feel like I have a good grasp on how to model my data.
I end up using declared properties for all my attributes and to-one relationships, backed by private ivars. For read-only attributes which need to be privately writeable, I use the readonly attribute in the .h interface declaration, then re-declare the property with the readwrite attribute in a class extension declared in the .m file. Inside the class methods, I always use the property accessors with the dot syntax and never access the private ivars directly.
There is however one aspect which still leaves me puzzled: how to properly model to-many relationships, especially when the collection is to be publicly immutable, but privately mutable (i.e. consumers of the model object cannot add or remove objects to the collection, but the collection's content is managed privately by the class).
I do understand how to implement the KVC accessor methods for to-many relationships (countOf<Key>, objectsIn<Key>AtIndex, etc.) and this is the route I've been following so far.
However, I've seen some sample code that uses declared properties to expose the relationships, do not implement the KVC accessor methods, yet are still Key-Value observable. For example:
#interface MyModel : NSObject
{
// Note that the ivar is a mutable array,
// while the property is declared as an immutable array.
#private NSMutableArray *transactions_;
}
#property (nonatomic, retain, readonly) NSArray transactions;
#end
--------------------
#implementation MyModel
#synthesize transactions = transactions_;
- (void)privateMethodThatManagesTransactions
{
[[self mutableArrayValueForKey:#"transactions"] addObject:t];
}
#end
If a consumer object adds itself as an observer of a MyModel instance for the "transactions" key path, it will be notified whenever transactions are added or removed from the transactions collection (as long as the mutations are done via the mutableArrayValueForKey: method).
To me, this seems like the cleanest way to expose to-many relationships as I don't need to hand-code the collection KVC accessors and it keeps the code clean.
However, it doesn't seem to be the way that is promoted by the Apple documentation, and I can't help but wonder if the fact that it works is only an unreliable side-effect.
So before commiting to one technique or the other in my real-life model classes for a project I'm beginning to work on, I'd like to get the opinion and advice of experienced Cocoa developers.
So the question is: if I use properties to model to-many relationships, do I still need to implement the KVC accessor/mutator methods?
Update
Even when I declare a to-many property as readonly, like in the example above, external code can still call mutableArrayValueForKey:#"transactions" on the model object and mutate the collection. This seems to indicate that using declared properties for to-many relationships isn't the way to go, but I still feel like I don't quite get it...
Yes.
There is however one aspect which still leaves me puzzled: how to properly model to-many relationships, especially when the collection is to be publicly immutable, but privately mutable ….
Easy: Declare the property as readonly in the header, then redeclare it as readwrite, copy in a class extension in the implementation file.
I do understand how to implement the KVC accessor methods for to-many relationships (countOf<Key>, objectsIn<Key>AtIndex, etc.) and this is the route I've been following so far.
There are mutative ones, too. With these, you don't need to use mutableArrayValueForKey:; instead, you can use the mutative accessors directly. You'll still get KVO notifications, because KVO wraps those methods the first time something adds itself as an observer for the property.
I have a list of the accessor selector formats, including the mutative accessors, on my blog.
Edit:
Even when I declare a to-many property as readonly, like in the example above, external code can still call mutableArrayValueForKey:#"transactions" on the model object and mutate the collection.
This is a good reason to make it a habit to use the mutative accessors and avoid mutableArrayValueForKey:. You won't send mutation messages from outside the class if you get a compiler warning (no such [public] method) any time you try it.
Despite the availability of mutableArrayValueForKey: and the risk that someone will use it, KVO-compliant properties are the way to go here.

Resources