Core Data: Find the property of NSEntity Objects in NSMutableSet - iphone

How can I efficiently find the attributes of an NSEntity object in a set ?
I have implemented it this way, but it seems to be inefficient. Is there a faster, easier, more efficient method of finding the attributes of an NSEntity object in a set, than this approach?
soccerTeamViewController.players = [[NSMutableArray alloc] init];
for (GGPlayer *playa in chelsea.players) {
[soccerTeamViewController.players addObject:playa.name];
}
^here chelsea is a GGTeam, it has a set of GGPlayers as a property.
Models:
GGTeam:
#property (nonatomic, retain) NSMutableSet *players;
GGPlayer:
#property (nonatomic, retain) NSString *name;
From a first time iOS-er.

Do not optimize prematurely. You have no evidence as to whether what you are doing is inefficient or not. If there appears to be a slowdown, use Instruments and figure it out. But don't guess, and especially don't guess in advance.
What you're doing is perfectly standard.
There is a more elegant way, namely to use KVC, as described here:
iPhone - getting unique values from NSArray object
But I have no reason to think that is more efficient.

Related

Core Data : Updating array.

I have a Core Data model entity NoteObject that has a transformable type arrayOfTags. In the NoteObject.h file, which is a subclass of NSManagedObject, the arrayOfTags is declared as:
NSMutableArray *arrayOfTags;
#property (nonatomic, retain) NSMutableArray *arrayOfTags;
//.m
#dynamic arrayOfTags;
The issue is that changes that are made to this array are not saved. Someone suggested the following as the solution:
If there are mutable and immutable versions of a class you use to
represent a property—such as NSArray and NSMutableArray—you should
typically declare the return value of the get accessor as an immutable
object even if internally the model uses a mutable object.
However I'm not exactly sure what that means. How would I follow those instructions for my case?
Even of you've found a workaround in the meantime try this:
[noteObject willChangeValueForKey:#"arrayOfTags"];
// make changes to noteObject.arrayOfTags
[noteObject didChangeValueForKey:#"arrayOfTags"];
Implementing accessor functions for Core Data varies with your relationship model. This document should help you get started. Most likely you will be using this setup for your getter:
- (NSArray*)data
{
[self willAccessValueForKey:#"data"];
NSArray* array = [[NSArray alloc] initWithArray:arrayOfTags copyItems:YES];
[self didAccessValueForKey:#"data"];
return array;
}
Please note that the above snippet is just an example and will have to be modified for your use.

Pulling an NSArray out of a NSMutableArray

I have an NSMutableArray which is storing a list of other arrays. And when i run the code.
NSLog(#"%#",[[appDelegate teamRoster]objectAtIndex:[indexPath.row]class])
It returns and tells me that i am looking at an Array,
however when i try to do the following
[selectedRowerView tempArray] = [[appDelegate teamRoster]objectAtIndex:[indexPath.row]];
The program errors out. Anyone have any ideas why this might be happening?
You have to understand that [selectedRowerView tempArray] is actually a command / message that is being sent. In C++ equivalent, you are calling selectedRowerView->tempArray() = .... Which doesn't make logical sense because you cannot make an assignment to a function.
What you're trying to do is set the tempArray. If you have the proper setters/getters set-up, you can just run: selectedRowerView.tempArray = ...;
Just make sure that tempArray has a #property and is #synthesize'd.
How about this?
selectedRowerView.tempArray = [[appDelegate teamRoster]objectAtIndex:[indexPath.row]];
…assuming that tempArray is a synthesized property à la
#property (nonatomic, readwrite, retain) NSArray *tempArray;
#synthesize tempArray;
Clarification:
selectedRowerView.tempArray = …;
gets internally processed to
[selectedRowerView setTempArray:…];
which is a setter method.
While
selectedRowerView.tempArray;
gets internally processed to
[selectedRowerView tempArray];
which is a getter method.
Subtle but important difference.
The meaning of foo.bar depends on the very context (enclosing expression) it is used in.

Design issue in Iphone Dev - Generic implementation for Game Bonuses

So, I thought consulting you guys about my design, cause I sense there might be a better way of doing it.
I need to implement game bonuses mechanism in my app.
Currently there are 9 bonuses available, each one is based of different param of the MainGame Object.
What I had in mind was at app startup to initialize 9 objects of GameBonus while each one will have different SEL (shouldBonus) which will be responsible for checking if the bonus is valid.
So, every end of game I will just run over the bonuses array and call the isBonusValid() function with the MainGame object(which is different after every game).
How's that sound ?
The only issue I have currently, is that I need to make sure that if some bonuses are accepted some other won't (inner stuff)... any advice how to do that and still maintain generic implementation ?
#interface GameBonus : NSObject {
int bonusId;
NSString* name;
NSString* description;
UIImage* img;
SEL shouldBonus;
}
#implementation GameBonus
-(BOOL) isBonusValid(MainGame*)mainGame
{
[self shouldBonus:mainGame];
}
#end
Sounds ok, the only change I would consider is perhaps removing a bonus from the array should it be acepted. That way it is not checked in the future. This would also work for bonus that for other reason should no longer be available.
Whether or not the player can obtain a particular bonus according to the rules of your game isn't something the individual bonuses would know about. This is something the game itself would know. For example, you may have one game that allows bonuses A and B together, but another game that wouldn't.
So the logic to grant or deny a bonus should be in the MainGame object. I would organize it so that GameBonus is a plain bit bucket class and the logic is all in the MainGame. MainGame would be a superclass of any other custom games that might want to override the bonus logic.
A starting point:
typedef enum {
BonusTypeA, BonusTypeB, BonusTypeC
} BonusId;
#interface GameBonus : NSObject {
BonusId bonusId;
NSString *name;
NSString *description;
UIImage *img;
}
#property (nonatomic,assign) BonusId bonusId;
#property (nonatomic,retain) NSString *name;
#property (nonatomic,retain) NSString *description;
#property (nonatomic,retain) NSString *img;
#end
#interface MainGame : NSObject {
NSMutableSet *activeBonuses;
}
-(BOOL) tryToSetBonus:(BonusId)bonus; // tries to set, returns YES if successful.
-(BOOL) isBonusValid:(BonusId)bonus; // has no side effect, just check validity.
#end

Do I have a leak with this statement?

The statement is:
//Pass the copy onto the child controller
self.childController.theFoodFacilityCopy = [self.theFoodFacility copy];
My property is set to:
#property (nonatomic, retain) FoodFacility *theFoodFacilityCopy;
The reason I think I have a leak is because copy retains the value and then my dot syntax property also retains the value. Doubly retained.
What is the correct way of writing the above statement?
yes, you do have a leak there.
SomeClass *someObj = [self.theFoodFacility copy];
self.childController.theFoodFacilityCopy = someObj;
[someObj release];
This mirrors the recommended approach for initializing an object too:
SomeClass *someObj = [[SomeClass alloc] init];
self.someProperty = someObj;
[someObj release];
In both cases the first line returns an objects with a retain count of 1, and you treat it identically after that.
As mentioned by others, that is indeed a leak. If you expect to be using copies in this way, it’s likely your property should be declared copy instead and the synthesized accessor will do the work for you.
You are right. The cleanest way is something like
id temp = [self.theFoodFacitlity copy];
self.childController.theFoodFacilityCopy = temp;
[temp release]
You want to read the apple site on memory management a lot until these rules become second nature.
What is the advantage of doing this vs just setting the property to copy?
#property (nonatomic, copy) FoodFacility *theFoodFacilityCopy;

release/autorelease confusion in cocoa for iphone

I'm slowly teaching myself cocoa for the iPhone(through the Stanford Class on iTunes U) and I've just gone through the part on memory management, and I wanted to hopefully get some confirmation that the assumptions I'm making on how memory is handled and how [release] and [autorelease] work. Since memory management is a really basic and fundamental, but very essential part of the programming experience, I'd like to make sure I'm doing it right.
I understand that anything with an alloc, new, or copy needs to be released.
If I do this:
NSString *temp = [[NSString alloc] initWithString:#"Hello World"];
Then I need to add [temp release/autorelease] eventually, since I have an alloc.
However, if I do this:
NSString *temp = #"Hello World";
Then it doesn't seem to need a release statement. Does the NSString class call autorelease automatically as part of the assignment?
Also, is there any difference between the two *temp objects here after these statements? They both contain the same string, but are there memory/usage ways where they differ?
Secondly, with properties, I'm assuming that the autorelease is handled automatically. If I have this:
#interface Person : NSObject
{
//ivars
NSString *firstName;
NSString *lastName;
}
//properties
#property NSString *firstName;
#property NSString *lastName;
///next file
#implementation Person
#synthesize firstName;
#synthesize lastName;
- (void) dealloc
{
//HERE!!!!
[super dealloc];
}
I'm assuming I don't need to add [firstName release] and [lastName release] (at //HERE!!!!), since that's automatically handled by the properties. Is that correct?
I do understand that if I do this in code(assuming I've defined initWithFirstName):
Person *Me = [[Person alloc] initWithFirstName: #"Drew", lastName:"McGhie"];
that later I'm going to have to use [Me release/autorelease];
Any help confirming or correcting my understanding so far is greatly appreciated.
POST ANSWER WRITE-UP
I thought I'd write this all up after going over all the answers and testing out the suggestions and talk about what worked.
I do need to add the [firstName release], [lastName release], but I also need to add (retain) to the property descriptions. Not adding the (retain) caused warnings because it assumes (assign). Here's how I finally set up the class
#interface Person : NSObject
{
//ivars
NSString *firstName;
NSString *lastName;
}
//properties
#property (retain) NSString *firstName;
#property (retain) NSString *lastName;
///next file
#implementation Person
#synthesize firstName;
#synthesize lastName;
- (void) dealloc
{
[firstName release];
[lastName release];
[super dealloc];
}
The rule is simple: if you alloc, copy or retain, it's your responsibility to release. If you didn't, it's not. However, if you need to rely on an object staying around, you have to retain (and subsequently release).
We can treat the string literal according to the rules - you don't need to release it because you don't own it. That's simple; there's no need to worry about whether they're special cases or not, just follow the rules and you'll be fine.
I wrote up a blog post with a collection of articles about the Cocoa memory management rules; I'd recommend following up some of the references.
I've never released string constants like NSString *foo = #"x";. Logically, if you had to release the result of that, it you would have to release the parameter to initWithString, and both the parameters to initWithFirstName:lastName:, too.
You must do release or autorelease firstName and lastName. I've seen warnings about not using property syntax in destructors, which I think is the same reason you don't use virtual functions in C++ constructors and destructors.
Your assumption was wrong. You must do either this:
Person *Me = [[Person alloc] initWithFirstName: #"Drew"
lastName: #"McGhie"];
...
[Me release];
or this:
Person *Me = [Person personWithFirstName: #"Drew"
lastName: #"McGhie"];
...and make sure your Person object handles +personWithFirstName:lastName: correctly, i.e. [[[self alloc] initWithFirstName: firstName lastName: lastName] autorelease].
You should probably do the one with less code. Clarity is important, NSAutoreleasePool will probably never be your bottleneck, and if it ever is it's easily fixed.
I think people put a lot of effort into avoiding the class messages that return an autorelease'd object that just isn't merited. It's premature optimization, in that it probably isn't necessary and may not even be the correct thing to do. And it's harder to maintain, you'll very likely be chasing leaks forever.
Also, you're going to autorelease an object you had to init (i.e. alloc + initPersonWithFirstName:lastName: instead of using a class message like personWithFirstName:lastName:), I'd suggest you do it immediately. Otherwise, you're potentially chasing that same kind of leak. So if you're not going to add a personWithFirstName:lastName: method to Person, do this instead:
Person *Me = [[[Person alloc] initWithFirstName: #"Drew"
lastName: #"McGhie"] autorelease];
Summary: Cocoa does a lot to help you with memory management. Make sure you don't fight it.
Updated based on Jon's feedback in comment.
NSStrings created with the #"String here" syntax are constant strings. These are different from normal strings. Much like normal C constant strings, they are created when your program loads and exist for its entire lifetime. The NXConstantString class, to which they belong, ignores all memory management messages. You can retain and release them all you like and it won't make any difference.
For a string created with an [[NSString alloc] initWith...]-type method, normal memory management rules apply. I'd strongly recommend reading the linked docs — they're not complicated, and after reading them, you will know pretty much everything you will ever need to know about Cocoa memory management.
Last part first: You will indeed have to auto/release Me. However, you will also have to add [firstName release]; and [lastName release]; in -dealloc; or better yet; self.firstName = nil;
As for string literals; this part gets a bit hairy, but [#"String literal" release] is essentially a noop. As such, there is a difference between the two temp objects, but as you won't generally know which one you'll be dealing with, adding [temp release]; is generally the safe choice, unless you know it'll contain an autoreleased object.
About firstName/lastName.
You should always, for clarity, remember to specify the properties' attributes. The setter default attribute is assign, in this case you want to use retain.
#interface Person : NSObject
{
NSString *firstName;
}
#property (retain) NSString *firstName;
#end
With retain each and only time you use the dot notation to assign a value the compiler inserts a retain: just remember to always use it. For consistency I advice you to write your initializer this way:
- (id) initWithFirstName:(NSString*) aString
{
self.firstName = aString;
}
and the dealloc method this way:
- (void) dealloc
{
self.firstName = nil;
}
About #""-type objects. They are constant NSStrings objects. Just use them as the were (they are) NSString objects and never release them. The compiler takes care of them.
Does the NSString class call autorelease automatically as part of the assignment?
The NSString class didn't do anything because you didn't send it a message. All you did was assign to a variable. NSString doesn't find out about this, and it's none of its business.
Also, is there any difference between the two *temp objects here after these statements? They both contain the same string, but are there memory/usage ways where they differ?
They're both NSStrings, they both contain the same value, and they're both presumed immutable. That's all you should ever care about.
Secondly, with properties, I'm assuming that the autorelease is handled automatically. If I have this:
#property NSString *firstName;
#property NSString *lastName;
- (void) dealloc
{
//HERE!!!!
[super dealloc];
}
I'm assuming I don't need to add [firstName release] and [lastName release] (at //HERE!!!!), since that's automatically handled by the properties. Is that correct?
No. NSObject will not release all your property values for you; you still need to release them yourself there.
Also, don't do self.firstName = nil and self.lastName = nil in dealloc. Those translate into messages (to your accessor methods), and when you do that in dealloc, you're sending messages to a half-deallocked object. That's asking for trouble. The same applies the other way to initializing property values in init: Using your properties/accessors there would be sending messages to a half-inited object.