Accessing an NSMutableArray inside my custom object - xcode

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.

Related

implementing custom accessor methods

I am reading "Core Data Programming Guide". It contains this text:
You must, however, change attribute values in a KVC-compliant fashion.
For example, the following typically represents a programming error:
NSMutableString *mutableString = [NSMutableString stringWithString:#"Stig"];
[newEmployee setFirstName:mutableString];
[mutableString setString:#"Laura"];
For mutable values, you should either transfer ownership of the value
to Core Data, or implement custom accessor methods to always perform a
copy. The previous example may not represent an error if the class
representing the Employee entity declared the firstName property
(copy) (or implemented a custom setFirstName: method that copied the
new value). In this case, after the invocation of setString: (in the
third code line) the value of firstName would then still be “Stig” and
not “Laura”.
Question regarding text: "In this case" is which case--the one where property is declared as "copy" or when its not?
Question regarding copy and programming practice:
From what I have read here:
NSString property: copy or retain?
I understand
that using copy will ensure that firstName is "Stig", not Laura
it is wise to do so because "in almost all cases you want to prevent mutating an object's attributes behind its back"
I would really like to know what is the above quoted text trying to tell us in the context of Core Data. We have to use "copy" anyway whether using Core Data or not. Also, I would be glad if someone could throw more light on point "2" (it is wise to...) above as in what will be the consequences of mutating an object's attributes behind its back?
your "Question regarding text: "In this case" is which case--the one where property is declared as "copy" or when its not?"
mis-matched the point that Apple document wants to explain, I believe.
As Apple document points out, if custom-accessor-method is implemented normally, the default implementation does NOT copy attribute values. If the attribute value may be mutable and implements the NSCopying protocol (as is the case with NSString, for example), you can copy the value in a custom accessor to help preserve encapsulation (for example, in the case where an instance of NSMutableString is passed as a value).
Here is a copying setter snippet
#interface Department : NSManagedObject
{
}
#property(nonatomic, copy) NSString *name;
#end
#implementation Department
#dynamic name;
- (void)setName:(NSString *)newName
{
[self willChangeValueForKey:#"name"];
// NSString implements NSCopying, so copy the attribute value
NSString *newNameCopy = [newName copy];
[self setPrimitiveName:newNameCopy];
[self didChangeValueForKey:#"name"];
} #end
The issue is when to use (and how) immutable values.
Since core data use KVO heavily when detecting changes done to objects, if you use a mutable property that is changed directly through it object and not through the property, CoreData will not detect the change to the object and your changes might not persist to the store.
If you use mutable NSManagedObject attributes, override the setter/getter method and use only them to mutate the underlying object (this mean that you are responsible to let CoreData know that a change did happen to the object, and it must be persisted to the store.
Also, if you use transformable properties for complex objects, you must trigger the change notifications yourself in order for CoreData to realise that a change has occurred, and the object should be re-transformed and saved when the context saves.
I would highly recommend that when it comes to simple objects like strings, you use immutable property values which will force you to go through the object properties and trigger the default KVO notification (copy attributes will also force the KVO notifications).

Is passing a controller in a construtor always a bad practice?

I occasionally instantiate a class from my view controller by passing in the view controller instance itself so that the objects that I create can invoke methods of the controller to update the view.
Is that always, often, or never a bad practice?
Concretely:
ViewController.h has
-(void)updateButtonValue:(NSString*)value;
MyObject.h has
-(id)initWithViewController:(ViewController*)aViewController;
I instantiate that class from my view controller with:
[[MyObject alloc] initWithViewController:self];
thus allowing that MyObject instance to update a button value in my view by a simple call like:
MyObject.m
[self.viewController updateButtonValue:#"example"];
It does not seem ideal since I am passing to MyObject much more (the view controller itself) than it should need, but it is certainly quick and functional. If there is a cleaner approach, such as relying on protocols, that is also succinct, a brief code sample would be much appreciated.
It is always bad practice to pass a class-typed pointer in, as you are tightly coupling your objects together (each object needs to know the class of the other, they might as well be a single object). This is what the delegate pattern is for. It minimises the info MyObject needs (minimally, nothing more than a pointer type id - preferably, a protocol specified by MyObject to offer it some behavioural guarantees)
So to translate your example
MyObject.h
replace...
-(id)initWithViewController:(ViewController*)aViewController;
with...
-(id) init;
(which you can dispense with if you have no further reason to override)
and...
#property (nonatomic, weak) id delegate;
Instantiation in myViewController (which does need to #include MyObject) ...
MyObject* object = [[MyObject alloc] init];
Followed by
object.delegate = self;
(Note that object gets a pointer to myViewController without needing to know anything else about it)
Now you can do this from inside object:
[self.delegate updateButtonValue:#"example"];
However ... you will want to ensure that your delegate can receive the message updateButtonValue:
To do this, you declare a protocol in MyObject.h with the signature of this method
#protocol MyObjectDelegate
- (void) updateButtonValue:(NSString*)string;
#end
And in your viewController, declare that you conform to this protocol using <> in the interface line
#interface ViewController <MyObjectDelegate>
(this is no big deal, ViewController already has to #include MyObject to alloc/init it, so needs no more info to do this)
And expand your property declaration thus:
#property (nonatomic, weak) id <MyObjectDelegate> delegate
Now you have given the compiler enough information for it to ensure that you can only pass conformant messages around. The brilliant thing here is that MyObject can confidently pass messages to MyViewController without needing to know anything about MyViewController other than that it is reached via the delegate pointer.

Sub-classing an NSManagedObject

I have a core data entity called Hospitals. There are three fields: name(string), latitude(double), and longitude(double).
I have an NSManagedObject class called Hospitals.h/.m that lets me fetch data just fine.
I also have determined my current CLLocation.
Now, when I load all of my hospital records, I would like to step through them and calculate my current distance from the hospital. No problem.
Ah, but now I need to store the CLLocationDistance somewhere. At first, I just created a new class with iVars to hold 1) the HospitalManagedObject and 2) the CLLocationDistance. But that became very awkward.
So, I thought I would just extend the Hospitals managed object class to include a variable "myDistance." But everything I am trying causes the sub-classed managed object to stop wanting to communicate with core data.
How is the best way to do this?
Any insights are very-much appreciated.
You could add a new field to your object model for Hospital object called something like myDistance and make it of type Undefined in the datamodel. This will tell Core Data the you have a property that you are going to use at runtime but it isn't something that is going to be stored in the database.
In your Hospital.h you define the property using whatever type you want (I am assuming the distance is an NSNumber) like this
#property (nonatomic, retain) NSNumber *myDistance;
and in your implementation you can put it in a #dynamic myDistance; statement just like all of the other data fields.

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.

Passing NSMutableArray to other classes

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.

Resources