I am trying to use fast enumeration to print all songs that are in a playlist, but it seems like I am doing it wrong. Could someone help me out?
I have defined a Song class like this :
#interface Song : NSObject
#property (nonatomic,strong) NSString *title;
#property (nonatomic,strong) NSString *artist;
#property (nonatomic,strong) NSString *album;
#property (nonatomic,strong) NSString *playtime;
-(void) displaySong;
#end
displaySong function looks like this :
-(void) displaySong {
NSLog(#"%# - %# [%#] - %#\n", artist, title, album, playtime);
}
and now, to the PlayList class :
#interface PlayList : NSObject{
NSString *name;
NSMutableArray *playlist;
}
-(void) addSongToPlaylist: (Song *) song;
-(void) displayPlaylist;
#end
#implementation PlayList
-(void) addSongToPlaylist: (Song *) nameOfSong{
[playlist addObject:nameOfSong];
}
-(void) displayPlaylist{
NSLog(#"Playlist called %#:\n", name);
for(id obj in playlist)
{
if([obj isKindOfClass:[Song class]])
[obj displaySong];
}
}
// I also tried the displayPlaylist method this way, but it wasn't working either
-(void) displayPlaylist{
NSLog(#"Playlist called %#:\n", name);
for(Song *song in playlist)
{
[song displaySong];
}
#end
Could somebody please explain to me why my code isn't working?
And what displayPlaylist method is better?
You are never creating playlist
in the Playlist class' init add a playlist = [[NSMutableArray alloc] init] ;
And consider using a property for playlist (man — the similar names are quite annoying!)
If you don't want to have a init, you also can do:
-(void) addSongToPlaylist: (Song *) nameOfSong{
if(!playlist)
playlist= [[NSMutableArray alloc] init];
[playlist addObject:nameOfSong];
}
to answer the second question: it depends
if you know, that there are only instances of Song in the array, the second version is better, as a bit faster
if you don't know, it is the first, as otherwise a object would receive a message, it can't handle -> crash
or you can do
for(id obj in playlist)
{
if([obj respondsToSelector:#selector(displaySong)])
[obj displaySong];
}
if you wants to be sure, that the message is understood, no matter what object it is.
You don't appear to be initializing your playlist array. You can do so lazily in your methods by checking for nil or by overriding the init method for your PlayList class.
Related
I have this piece of code below and I'm trying to add Objects(String elements) to an array, problem is that every time I'm out its adding's method, it goes to nil, it doesn't retain the objects.
I know I'm doing wrong, even that I already tried lot of combinations and variations, even with my own constructor _MyArray etc etc, same result... it works, but not further...
Could you help me please?
#interface ArraysModel()
#property (nonatomic, retain) NSMutableArray *MyArray;
#end
#implementation ArraysModel
#synthesize MyArray;
-(void)AddObjectToTheList:(NSString *)object {
if(!MyArray) MyArray = [[NSMutableArray alloc] init];
[MyArray addObject:object];
NSLog(#"%#",self.MyArray);
NSLog(#"Object added %u",[self.MyArray count]);
}
-(NSMutableArray *)ObjectList {
return self.MyArray;
NSLog(#"%#",self.MyArray);
NSLog(#"Object added %u",[self.MyArray count]);
}
#end
The header is like this:
#interface ArraysModel : NSObject
-(void)AddObjectToTheList:(NSString *)object;
And here is my call from my ViewController:
- (IBAction)AddToTheList {
ArraysModel *MyObjectToAdd = [[ArraysModel alloc] init];
[MyObjectToAdd AddObjectToTheList:TextArea.text];
[self.view endEditing:YES];
Well, there's your problem -- you're alloc init'ing a new instance of ArraysModel, and therefore a new array with every call. You need to create a strong reference to your instance, and check for whether it exits, and only init if it doesn't.
In the .h:
#property (strong, nonatomic) ArraysModel *myObjectToAdd;
in the .m:
-(IBAction)AddToTheList {
if (! self.myObjectToAdd) {
self.myObjectToAdd = [[ArraysModel alloc] init];
}
[self.myObjectToAdd AddObjectToTheList:TextArea.text];
[self.view endEditing:YES]
}
I'm reading a book to learn Objective-C. I'm stuck in one of the exercises. You have to make a program that has a class song, playlist, and music collection. If you create a song, it has to automatically add to the music collection, who has a NSMutableArray for collecting songs. And if You remove an object from music collection, the song has to remove from every playlist containing that song.
Song Interface
#import <Foundation/Foundation.h>
#interface Song : NSObject{
NSString *title;
NSString *artist;
NSString *album;
}
#property (copy) NSString *title;
#property (copy) NSString *artist;
#property (copy) NSString *album;
-(Song *) initWithNames:(NSString*) title1 and: (NSString*) artist1 and: (NSString*) album1;
#end
Playlist Interface
#import <Foundation/Foundation.h>
#interface Playlist : NSObject{
NSString * title;
NSMutableArray *collecsongs;
}
#property (strong) NSString *title;
#property (strong) NSMutableArray *collecsongs;
-(Playlist *) initWithName: (NSString *) name;
#end
Music Collection Interface
#import <Foundation/Foundation.h>
#import "Playlist.h"
#interface MusicCollection : NSObject{
NSMutableArray *collecplist;
Playlist *library;
}
#property (strong) NSMutableArray *collecplist;
#property (strong) Playlist *library;
#end
So if i create a song for example song1, is there a way if i add it to a playlist, automatically add it to the mastercoleection variable "library", instead of doing this
Song *song1 = [[Song alloc] initWithNames:#"Somebody That I Used To Know" and: #"Gotye" and: #"First Album"];
Playlist *Misrolas = [[Playlist alloc] initWithName: #"Misrolas"];
MusicCollection *music = [[MusicCollection alloc] init];
[Misrolas.collecsongs addObject: song1];//adds song1 to the playlist named "Misrolas"
[music.library.collecsongs addObject: song1];//adds song1 to the music collection
So i don't know what to do, i was thinking overriding maybe addObject:, but that doesn't seem right and easy, thanks for the help =)
I do it like this, is there more efficient or better ways to add it ???
-(void) addsong: (Song *)song addtocollection: (Playlist *) library{
NSAssert([song isKindOfClass: [Song class]], #"Not the same class");
[self.collecsongs addObject:song];
[library.collecsongs addObject: song];
}
-(Song *) initWithNames:(NSString*) title1 and: (NSString*) artist1 and: (NSString*) album1;
This is a pretty bad naming. Your selector shortens to initWithNames:and:and: (which is not really descriptive). Consider using
- (id)initWithTitle:(NSString *)title artist:(NSString *)artist album:(NSString *)album;
Notice how I use return type of (id) here. It allows easier subclassing, as any descendant class can use the init... "constructor" without any type mismatch warnings.
Speaking of your issue, I'd suggest you to expose only NSArray * property accessors (so that you cannot modify the array contents) and make a method on Playlist class:
- (void)addSong:(Song *)song
{
NSAssert([song isKindOfClass:[Song class]]);
[self.collecplist addObject:song];
}
That's OOP's encapsulation in action. You don't expose private interface (the array), you provide interface for adding exactly songs (you cannot add other kind of object), finally, you do a verification, that what you add is really a song. Here you can also add the song to your music collection.
You will probably find this easier if you call a method on the Playlist object to add the song, rather than accessing its collecsongs property. In that method, it can add the song to the array and then add it to the library. (And then you can make the collecsongs property return an NSArray, rather than an NSMutableArray, which seems much cleaner to me.
So I'm at the point in my app where I need to replace the image of a sprite and I think I know how to do it, but I'm having trouble implementing it. Basically, I'm making a domino game and I need to be able to flip the domino over so you can see the numbers.
Starting with my domino.h file....
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Domino : CCSprite {
int int_leading;
int int_trailing;
int int_suitrank;
int int_playerid;
NSString *str_tilename;
NSString *str_mirrortilename;
}
#property int int_leading,int_trailing, int_playerid,
#property(nonatomic, retain) NSString *str_tilename;
#property(nonatomic, retain) NSString *str_mirrortilename;
-(void) print;
-(void) setTileName: (NSString *) theTileName;
-(void) setMirrorName: (NSString *) theMirrorName;
-(NSString *) str_tilename;
-(NSString *) str_mirrortilename;
#end
and then my .m file...
#import "Domino.h"
#implementation Domino
#synthesize int_leading,int_trailing, str_tilename, str_mirrortilename, int_playerid;
-(void) print {
NSLog (#"%i/%i", int_leading, int_trailing);}
-(void) setTileName: (NSString *) theTileName;
{
str_tilename=[[NSString alloc] initWithString: theTileName];
}
-(void) setMirrorName: (NSString *) theMirrorName;
{
str_mirrortilename=[[NSString alloc] initWithString: theMirrorName];
}
-(NSString *) str_tilename
{
return str_tilename;
}
-(NSString *) str_mirrortilename
{
return str_mirrortilename;
}
#end
Finally, in my game layer...
Domino *d06 =[[Domino alloc] initWithSpriteFrameName:#"blank.png"];
TileName= #"0-6.png";
MirrorName= #"6-0.png";
[d06 setTileName: TileName];
[d06 setMirrorName: MirrorName];
d06.int_leading=0;
d06.int_trailing=6;
At this point, I add all the dominoes together into a large mutable array that tracks which ones are still available for players to pick. The problem I'm having is that I either haven't found how to pull out the "TileName" from the mutable array, or maybe I have found it, but not understood it.
If I'm in a For loop, I would think the code should be something like
NSString *temp1=[[[movableSprites objectAtIndex:i]valueForKey:#"str_tilename"]string];
But this just results in the program crashing. Can you point me in the right direction?
NSString *temp1=[(Domino *)[movableSprites objectAtIndex:i] str_tilename];
I think using this code will solve your stuff... Check for the syntax.. Hope this helps.. :)
I am trying to pass the selected object in my coredata from the rootviewcontroller to the edit view. The selected object is being passed but is then becoming null after the theObject=selectedObject is being called. Anyone know what im doing wrong?
This is in the edit.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "LearningAppDelegate.h"
#interface edit : UIViewController <UITextViewDelegate, UITableViewDelegate, UITableViewDataSource, UIActionSheetDelegate>{
UITableView *tableView;
NSManagedObject *theObject;
UITextView *messageView;
}
#property(nonatomic, retain) IBOutlet UITableView *tableView;
#property(nonatomic, retain) IBOutlet UITextView *messageView;
#property(nonatomic, retain) NSManagedObject *theObject;
-(id)initWithObject:(NSManagedObject *)selectedObject;
#end
This is in the edit.m:
-(id)initWithObject:(NSManagedObject *)selectedObject {
self = [super init];
if (nil == self) {
return nil;
}
NSLog(#"selectedObject: %#", selectedObject);
NSLog(#"selecetedObject.message: %#", [[selectedObject valueForKey:#"message"] description]);
theObject=selectedObject;
NSLog(#"theObject 1: %#", theObject);
NSLog(#"theObject.message 1: %#", [[theObject valueForKey:#"message"] description]);
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(#"theObject 2: %#", theObject);
NSLog(#"theObject.message 2: %#", [[theObject valueForKey:#"message"] description]);
messageView.text=[[theObject valueForKey:#"message"] description];
[super viewDidLoad];
}
I am actually amazed that doesn't crash for you. You're assigning the variable selectedObject into the instance variable theObject without retaining it for your own use. By accessing the instance variable directly in the assignment 'theObject=selectedObject', you're bypassing the behavior granted by the #property declaration. This means that once selectedObject is finally dealloc'd, theObject will point to garbage memory.
The correct way to do this is to put theObject = [selectedObject retain]; in the -initWithObject: method and in -viewDidLoad to access it via self.theObject rather than just theObject.
In my own usage I prefer to give instance variables names different from the actual property name to avoid confusion. For example:
#interface SomeClass : NSObject
{
#private
NSManagedObject *_theObject;
}
#property (nonatomic, retain) NSManagedObject *theObject;
...
#end
#implementation SomeClass
#synthesize theObject = _theObject
...
- (void)dealloc
{
[_theObject release], _theObject = nil;
[super dealloc];
}
#end
I only have experience with Coredata on the desktop, but the problem looks like it would be with your initWithObject method. At no point do you actually insert the new object into the managed object context. You should be using this method to make new objects:
- (id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context
As an example in pseudocode:
NSManagedObject *newObject = [[NSManagedObject alloc] initWithEntity:NSENTITYDESCRIPTION insertIntoManagedObjectContext:MANAGEDOBJECTCONTEXT];
[newObject setValue:#"VALUE_OF_SELECTED_OBJECT" forKey:#"APPROPRIATE_KEY"];
//REPEAT AS NECESSARY
[MANAGEDOBJECTCONTEXT save];
*Code not tested, naming conventions are ignored, etc.
The save is important. If you don't do this the object won't persist.
Im attempting to pass an array that is created in one class into another class. I can access the data but when I run count on it, it just tells me that I have 0 items inside the array.
This is where peopleArray's data is set up, it's in a different class than the code that is provided below.
[self setPeopleArray: mutableFetchResults];
for (NSString *existingItems in peopleArray) {
NSLog(#"Name : %#", [existingItems valueForKey:#"Name"]);
}
[peopleArray retain];
This is how I get the array from another class, but it always prints count = 0
int count = [[dataClass peopleArray] count];
NSLog(#"Number of items : %d", count);
The rest of my code:
data.h
#import <UIKit/UIKit.h>
#import "People.h"
#class rootViewController;
#interface data : UIView <UITextFieldDelegate>{
rootViewController *viewController;
UITextField *firstName;
UITextField *lastName;
UITextField *phone;
UIButton *saveButton;
NSMutableDictionary *savedData;
//Used for Core Data.
NSManagedObjectContext *managedObjectContext;
NSMutableArray *peopleArray;
}
#property (nonatomic, assign) rootViewController *viewController;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSMutableArray *peopleArray;
- (id)initWithFrame:(CGRect)frame viewController:(rootViewController *)aController;
- (void)setUpTextFields;
- (void)saveAndReturn:(id)sender;
- (void)fetchRecords;
#end
data.m(some of it at least)
#implementation data
#synthesize viewController, managedObjectContext, peopleArray;
- (void)fetchRecords {
[self setupContext];
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"People" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setPeopleArray: mutableFetchResults];
for (NSString *existingItems in peopleArray) {
NSLog(#"Name : %#", [existingItems valueForKey:#"Name"]);
}
[peopleArray retain];
[mutableFetchResults release];
[request release];
//NSLog(#"this is an array: %#", eventArray);
}
login.h
#import <UIKit/UIKit.h>
#import "data.h"
#class rootViewController, data;
#interface login : UIView <UITextFieldDelegate>{
rootViewController *viewController;
UIButton *loginButton;
UIButton *newUser;
UITextField *entry;
data *dataClass;
}
#property (nonatomic, assign) rootViewController *viewController;
#property (nonatomic, assign) data *dataClass;
- (id)initWithFrame:(CGRect)frame viewController:(rootViewController *)aController;
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField;
#end
login.m
#import "login.h"
#import "data.h"
#interface login (PrivateMethods)
- (void)setUpFromTheStart;
- (void)loadDataScreen;
-(void)login;
#end
#implementation login
#synthesize viewController, dataClass;
-(void)login{
int count = [[dataClass peopleArray] count];
NSLog(#"Number of items : %d", count);
}
Is it the same object? If so, what you have should work. Check to see how you are getting the dataClass instance -- if you alloc a new one, you don't get the array from the other object.
Edit: From your comments below, it appears that you are having some confusion on the difference between classes and objects. I will try to explain (I'm going to simplify it):
A class is what you write in Xcode. It's the description that lets your application know how to create and access objects at run-time. It is used to figure out how much memory to allocate (based on instance variables) and what messages can be sent, and what code to call when they are. Classes are the blueprints for creating objects at runtime.
An object only exists at run-time. For a single class, many objects of that class can be created. Each is assigned its own memory and they are distinct from each other. If you set a property in one object, other objects don't change. When you send a message to an object, only the one you send it to receives it -- not all objects of the same class.
There are exceptions to this -- for example if you create class properties (with a + instead of a - at the beginning), then they are shared between all objects -- there is only one created in memory, and they all refer to the same one.
Also, since everything declared with a * is a pointer -- you could arrange for all pointer properties to point to the same data. The pointer itself is not shared.
Edit (based on more code): dataClass is nil, [dataClass peopleArray] is therefore nil, and then so is the count message call. You can send messages to nil, and not crash, but you don't get anything useful.
I don't see how the login object is created. When it is, you need to set its dataClass property.
Try running the code in the debugger, setting breakpoints, and looking at variables.
From the code, it looks like you are passing a mutable array.
[self setPeopleArray: mutableFetchResults];
Probably the items of the array are removed somewhere in your calling class / method. Or the array is reset by the class from which you get the mutableFetchResults in the first place.