2

Newbie Questions,

I have 3 Class, 3 of them are subclass from NSOBject.

  1. Collection Class, have 2 properties, masterSong as NSMutableSet (strong, nonatomic) and listOfPlaylists as NSMutableArray (strong, nonatomic)

  2. Playlist Class

Have 2 properties, playListName as NSString (copy, nonatomic) and songList as NSMutableArray (strong, nonatomic)

3 . Song Class

Have 4 properties: title,artist,album, playtime as NSString (copy, nonatomic).


masterSong will contain Song object, and listOfPlaylist will contain Playlist Object.

while songList only store reference to Song Object.

I want to create removeSong method for Collection Class by looking up Song for title,artist,or album inside masterSong.

if looking up find 1, it will return NSSet, returned NSSet will take place as parameter in minusSet: method to remove the song from masterSong and all Playlist.songList.

but, I can't figure out how to write down NSPredicate syntax to filter out .title or .album or .artist from masterSong which contain Song Object.

here is what I got so far, for Collection.m

- (void) removeSong: (NSString *)zSong{

    //remove from reference playlist


    NSSet *targets = [self lookUpTitle:zSong];

    if ([targets count]>0) {
        [self.masterSongs minusSet:targets];

        for (Playlist *playlist in listOfPlaylists) {
            // -all objects converts the set into array.

            [playlist.songList removeObjectsInArray:[targets allObjects]];
        }
    }
    else
        ;
}

Xcode throw exception when executing lookUpTitle method by saying that Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't look for value (title:Whole point of no return ,artist: Cafe Bleu ,album: CBSB ,playtime: 3:56) in string (What Do You Know); value is not a string

- (NSSet *) lookUpTitle: (NSString *)aName {

    NSString *filters = @"%K CONTAINS [cd] %@";
    NSPredicate *filter = [NSPredicate predicateWithFormat:filters, @"title", aName];

    NSSet *result = [masterSongs filteredSetUsingPredicate:filter];

    if ([result count] == 0) {
        NSLog(@"not found");

        return nil;
    }
    else{
        return result;
    }
}

I know that the main problem is at lookUpTitle method

 NSPredicate *filter = [NSPredicate predicateWithFormat:filters, @"title", aName];

 NSSet *result = [masterSongs filteredSetUsingPredicate:filter];

the filter is filtering in a wrong way, while it should filtering NSString object (title, artist, or album) but masterSong is containing Song Object, how do I access title,artist,album in Song Object placed inside masterSong with NSPredicate?

any other effective way for this condition?

Predicate Programming Guide only show example set/array containing string

I found other thread which has similar topic, but block is quite confusing to read, and still won't work in my case.


Edit : additions to the partial code (Class.m method) as requested by responder #1 . here are some method in .m files related to my questions

Main.m

    #import <Foundation/Foundation.h>
#import "MusicCollection.h"
#import "Playlist.h"
#import "Song.h"
int main(int argc, const char * argv[])
{

    @autoreleasepool {

        // insert code here...
       // NSLog(@"Hello, World!");



        //create Songs

        Song *aSong = [[Song alloc]initWithTitle:@"Whole point of no return" withArtist:@"Cafe Bleu" withAlbum:@"CBSB" withPlaytime:@"3:56"];
        Song *bSong  = [[Song alloc]initWithTitle:@"Council Meetin'" withArtist:@"Cafe Bleu" withAlbum:@"CBSB" withPlaytime:@"4:00"];

        Song *cSong = [[Song alloc]initWithTitle:@"Hayate" withArtist:@"Spitz" withAlbum:@"Indigo Chiheisen" withPlaytime:@"4:21"];

        Song *dSong = [[Song alloc]initWithTitle:@"I have a Dreams" withArtist:@"WestLife" withAlbum:@"Season" withPlaytime:@"4:11"];

        Song *eSong = [[Song alloc]initWithTitle:@"What Do You Know" withArtist:@"David Choi" withAlbum:@"Tomorrow" withPlaytime:@"3:46"];


        //create playList

        Playlist *playlistA = [[Playlist alloc]initWithName:@"Playlist A"];

        Playlist *playListB = [[Playlist alloc]initWithName:@"Playlist B"];

    //store Song A & B to Playlist A and Song A,B,C to playlist B

        [playlistA addSong:aSong];

        [playlistA addSong:bSong];

        [playListB addSong:aSong];

        [playListB addSong:bSong];

        [playListB addSong:cSong];

//        [playListB removeSong:eSong];


//Creating Master Collection

        MusicCollection *myCollection = [[MusicCollection alloc]initWithName:@"Library"];

        [myCollection addPlaylist:playlistA];
        [myCollection addPlaylist:playListB];
        [myCollection addSong:eSong];
        [myCollection addSong:dSong];

        [myCollection removePlaylist:playListB];
        [myCollection removeSong:aSong];

        NSSet *container2 = [myCollection lookUpTitle:@"What"];

    NSLog(@"%@",container2);


    //    NSLog(@"%@",myCollection);
    }
    return 0;
}

Collection.m

-(instancetype) initWithName: (NSString *)aName{
    self = [super init];
    if (self) {

        self.name           = [NSMutableString stringWithString:aName];
        self.listOfPlaylists = [NSMutableArray array];
        self.masterSongs = [NSMutableSet set];


    }
    return self;
}

-(instancetype) init{

    return  [self initWithName:@""];

}

-(void) addPlaylist: (Playlist *)aPlayList{

    if ([listOfPlaylists containsObject:aPlayList]==YES) {

    }
    else
        [listOfPlaylists addObject:aPlayList];


}

-(void) removePlaylist: (Playlist *)aPlayList{
    if ([listOfPlaylists containsObject:aPlayList]) {

        [listOfPlaylists removeObjectIdenticalTo:aPlayList];
    }

    else{
        ;
    }

}

- (void) displaySong{

    NSLog(@"displaying all song in Collection");

    NSLog(@" %@",self.masterSongs);

}

- (void) addSong :(Song *)aSong{
    if (![masterSongs containsObject:aSong]) {

        [masterSongs addObject:aSong];

    }
}

- (void) removeSong: (NSString *)zSong{

    //remove from reference playlist


    NSSet *targets = [self lookUpTitle:zSong];

    if ([targets count]>0) {
        [self.masterSongs minusSet:targets];

        for (Playlist *playlist in listOfPlaylists) {
            // -all objects converts the set into array.

            [playlist.songList removeObjectsInArray:[targets allObjects]];
        }
    }
    else
        ;
}



 - (NSSet *) lookUpTitle: (NSString *)aName {

    NSString *filters = @"%K CONTAINS [cd] %@";
    NSPredicate *filter = [NSPredicate predicateWithFormat:filters, @"title", aName];

    NSSet *result = [masterSongs filteredSetUsingPredicate:filter];

    if ([result count] == 0) {
        NSLog(@"not found");

        return nil;
    }
    else{
        return result;
    }
}

Playlist.m

- (void) addSong :(Song *)aSong{

    if (![songList containsObject:aSong]) {
        [songList addObject:aSong];
    }

}

- (void) removeSong: (Song *)aSong{
    if ([songList containsObject:aSong]){
    [self.songList removeObjectIdenticalTo:aSong];
    }
}
- (instancetype) initWithName: (NSString *)aPLName{
    self = [super init];

    if (self) {
        self.songList = [NSMutableArray array];
        self.playlistName = aPLName;
    }
    return self;
}

- (instancetype)init{
    return  [self initWithName:@""];
}

Song.m

 - (instancetype) initWithTitle: (NSString *)aTitle withArtist: (NSString *)anArtist withAlbum: (NSString *)aAlbum withPlaytime: (NSString *)playingTime{

    self = [super init];

    if (self) {
        self.artist = [NSMutableString stringWithString:anArtist];
        self.album  = [NSMutableString stringWithString:aAlbum];
        self.title =   [NSMutableString stringWithString:aTitle];
        self.playtime = [NSMutableString stringWithString:playingTime];

    }
       return self;
}

-(instancetype) init{
    return  [self initWithTitle:@"null" withArtist:@"null" withAlbum:@"null" withPlaytime:@"null"];
}
4

2 回答 2

2

您不需要使用-predicateWithBlock,在您的情况下使用以下谓词就足够了

[NSPredicate predicateWithFormat:@"SELF.title CONTAINS %@", @"Layla"];

编辑

Jeffery Thomas 是对的,问题出在这一行——实际上编译器给出了警告

 [myCollection removeSong:aSong];

您正在传递给-removeSong:Song 对象,而不是歌曲的标题。如果您想传递给-removeSong:一个Song对象,请将方法签名从- (void)removeSong:(NSString *)songTitleto -(void)removeSong:(Song*)song更改为并相应地更改方法实现以传递给谓词,歌曲的标题是NSString

- (void) removeSong: (Song *)zSong {

    //remove from reference playlist
    NSSet *targets = [self lookUpTitle:zSong.title];

    if ([targets count]>0) {
        [self.masterSongs minusSet:targets];

        for (Playlist *playlist in self.listOfPlaylists) {
            // -all objects converts the set into array.

            [playlist.songList removeObjectsInArray:[targets allObjects]];
        }
    }
}

从现在开始,请不要忽略编译器警告,以免浪费时间调试这种细微的错误。

于 2014-02-23T02:12:46.520 回答
2

您已经超载了的含义,-removeSong:这使您感到困惑。In Playlist,-removeSong:取一个Song实例,但 in Collection,-removeSong:取一个NSString歌曲标题的实例。

问题是您将 a 的实例传递Song给需要NSString.

- (void)removeSong:(NSString *)zSong
{
    NSSet *targets = [self lookUpTitle:zSong];
    // …
}

应该是

- (void)removeSong:(Song *)aSong
{
    NSSet *targets = [self lookUpTitle:aSong.title];
    // …
}

或者

- (void)removeSongWithTitle:(NSString *)aTitle
{
    NSSet *targets = [self lookUpTitle:aTitle];
    // …
}

我更改了方法的第二个版本的名称,以清楚地显示预期的参数是什么。当您想使用第二个版本时,您将传递消息[myCollection removeSongWithTitle:aSong.title]

于 2014-02-23T10:14:33.350 回答