如何使用NSPredicate过滤包含来自其他类的复合对象的NSMutableSet?

时间:2014-02-23 00:24:25

标签: objective-c cocoa-touch nspredicate nsmutableset

新手问题,

我有3个Class,其中3个是来自NSOBject的子类。

  1. 集合类,有2个属性, masterSong为NSMutableSet (强,非原子)和 listOfPlaylists为NSMutableArray (强,非原子) )

  2. 播放列表

  3. 有2个属性, playListName为NSString (copy,nonatomic), songList为NSMutableArray (强,非原子)

    3。 歌曲课程

    拥有4个属性:标题,艺术家,专辑,游戏时间为NSString(复制,非原子)。


    masterSong将包含Song对象,listOfPlaylist将包含播放列表对象。

    而songList仅存储对Song对象的引用。

    我想通过在masterSong中查找标题,艺术家或专辑的歌曲来为Collection Class创建removeSong方法。

    如果查找找到1,它将返回NSSet,返回的NSSet将作为参数在minusSet:方法中从masterSong和所有Playlist.songList中删除。

    但是,我无法弄清楚如何写下NSPredicate语法来过滤掉包含Song Object的masterSong中的.title或.album或.artist。

    这是我到目前为止所得到的 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
            ;
    }
    

    执行lookUpTitle方法时Xcode抛出异常,因为由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'无法查找值(标题:不返回的整点,艺术家:Cafe Bleu,专辑:CBSB,游戏时间:3:56)字符串(你知道什么);值不是字符串

    - (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;
        }
    }
    

    我知道主要问题是在lookUpTitle方法

     NSPredicate *filter = [NSPredicate predicateWithFormat:filters, @"title", aName];
    
     NSSet *result = [masterSongs filteredSetUsingPredicate:filter];
    

    过滤器以错误的方式过滤,而它应该过滤NSString对象(标题,艺术家或专辑),但是masterSong包含Song对象,如何使用NSPredicate访问放置在masterSong内的Song对象中的标题,艺术家,专辑?

    这种情况的其他任何有效方法吗?

    Predicate Programming Guide仅显示包含字符串

    的示例集/数组

    我找到了其他帖子which has similar topic,但是块读取起来很困惑,但在我的情况下仍然无效。


    编辑按响应者#1的要求添加部分代码(Class.m方法)。 这里是与我的问题相关的.m文件中的一些方法

    的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"];
    }
    

2 个答案:

答案 0 :(得分:2)

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

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

修改

Jeffery Thomas是对的,问题出在这一行 - 实际上编译器会发出警告

 [myCollection removeSong:aSong];

你要传递给-removeSong:一个歌曲对象,而不是歌曲的标题。如果您要传递给-removeSong:Song对象会将方法签名从- (void)removeSong:(NSString *)songTitle更改为-(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]];
        }
    }
}

从现在开始,请不要忽略编译器警告,以免过去几天调试这种微妙的错误。

答案 1 :(得分:2)

你重载了-removeSong:的含义,这让你感到困惑。在Playlist中,-removeSong:获取了Song个实例,但在Collection中,-removeSong:为歌曲标题提供了NSString个实例。

问题是您将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]