斯坦福iOS7 2013-2014秋Assignment 6的一種答案 #10

這篇文章是針對斯坦福iOS7 2013-1014的公開課Assignment 6 Top Regions所進行的解答的第十部分。

Extra  Credit 2. Loading Flickr information into your database can be ridiculously inefficient if, for each photo you download, you query to see if it is already in the database, add it if it is not (and maybe update it if it is). Enhance your application to make this download much more efficient, preferably only doing two or three queries total in the database for each “batch” of Flickr photos you download (instead of hundreds, which is what Photomania does). The predicate operator IN might be of value here.

本節主要是完成限制數據庫查詢的次數。

首先,我們限制photo查詢,在這裏我們使用提示中的IN謂詞(必須有圖片才能使用IN,否則會崩潰),查詢所有的ID。然後創建一個新數組,只存儲數據庫中不存在的photos。修改Photo+Flickr.m爲

+ (void)loadPhotosFromFlickrArray:(NSArray *)photos // of Flickr NSDictionary
         intoManagedObjectContext:(NSManagedObjectContext *)context
{
    if ([photos count]) {
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
        request.predicate = [NSPredicate predicateWithFormat:@"unique IN %@", [FlickrHelper IDsforPhotos:photos]];
        NSError *error;
        NSArray *matches = [context executeFetchRequest:request error:&error];
        if (!matches || ![matches count]) {
            // nothing to do ...
        } else {
            NSArray *existingPhotoIDs = [matches valueForKeyPath:@"unique"];
            NSMutableArray *newPhotos = [NSMutableArray arrayWithCapacity:[photos count] - [matches count]];
            for (NSDictionary *photo in photos) {
                if (![existingPhotoIDs containsObject:[FlickrHelper IDforPhoto:photo]]) {
                    [newPhotos addObject:photo];
                }
            }
            photos = newPhotos;
        }
    }
    ,,,
}

這裏需要在FlickrHelper中定義一個public方法

+ (NSArray *)IDsforPhotos:(NSArray *)photos
{
    return [photos valueForKeyPath:FLICKR_PHOTO_ID];
}

這裏我們只使用的是不在數據庫的photos,所以不需要再查詢了;並且如過有兩個同時啓動的下載進程,它們都認爲相同的圖片沒有存儲,就有可能導致數據庫衝突。所以我們註釋掉這個查詢:

+ (Photo *)photoWithFlickrInfo:(NSDictionary *)photoDictionary
        inManagedObjectContext:(NSManagedObjectContext *)context
{
    ...
//    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
//    request.predicate = [NSPredicate predicateWithFormat:@"unique = %@", unique];    
//    NSError *error;
//    NSArray *matches = [context executeFetchRequest:request error:&error];    
//    if (!matches || error || ([matches count] > 1)) {
//    } else if ([matches count]) {
//        photo = [matches firstObject];
//    } else {
        ...
//    }    
    ...
}
同樣,爲了限制photographer和region查詢,我們創建另外兩個可變數組(當我們有一個新的photographer或region時我們就把他添加到這兩個數組中),以防出錯,我把最終的代碼貼出來

+ (void)loadPhotosFromFlickrArray:(NSArray *)photos intoManagedObjectContext:(NSManagedObjectContext *)context
{
 
        //Let’s start by limiting the photo queries, but only if there any photos. If there are none, the request containing a predicate with an IN operator and nil will most likely crash … A mystical helper method (described a little bit later) generates an array of photo IDs used to query the database. If there are matches, create a new array of Flickr photo dictionaries containing only photos not yet in the database.
    NSMutableArray *existingPhotographers = [NSMutableArray array];
    NSMutableArray *existingRegions = [NSMutableArray array];
        if ([photos count]) {
            NSError *error;

            NSFetchRequest *req = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
            req.predicate = [NSPredicate predicateWithFormat:@"uique IN %@",[FlickrHelper IDsforPhotos:photos]];
            NSArray *matches = [context executeFetchRequest:req error:&error];
            
            req = [NSFetchRequest fetchRequestWithEntityName:@"Region"];
            req.predicate = [NSPredicate predicateWithFormat:@"placeID IN %@",[FlickrHelper placeIDSforPhotos:photos]];
            existingRegions = [[context executeFetchRequest:req error:&error] mutableCopy];
            
            req = [NSFetchRequest fetchRequestWithEntityName:@"Photographer"];
            req.predicate = [NSPredicate predicateWithFormat:@"name IN %@", [FlickrHelper ownersOfPhotos:photos]];
            existingPhotographers = [[context executeFetchRequest:req error:&error] mutableCopy];
            
            if (![matches count] || !matches) {
               //nothing to do
            } else {
                NSArray *uniqueIDs= [matches valueForKeyPath:@"uique"];
                
                NSMutableArray *newPhotos = [NSMutableArray arrayWithCapacity:([photos count] - [matches count])];
                    for (NSDictionary *photo in photos) {
                    if (![uniqueIDs containsObject:[FlickrHelper IDforPhotoDictionary:photo] ]) {
                        [newPhotos addObject:photo];
                    }
                }
                photos = newPhotos;
            }
            
        
        }
        
    for (NSDictionary *photo in photos) {
        
        [Photo photoWithFlickrInfo:photo inManagedObjectContext:context existingPhotographers:existingPhotographers existingRegions:existingRegions];
    }
    
    
    [Region loadRegionNamesFromFlickrIntoManagedObjectContext:context];

}

同樣,在FlickrHelper中定義兩個public方法

+ (NSArray *)placeIDsforPhotos:(NSArray *)photos
{
    return [photos valueForKeyPath:[NSString stringWithFormat:@"@distinctUnionOfObjects.%@", FLICKR_PHOTO_PLACE_ID]];
}
 
+ (NSArray *)ownersOfPhotos:(NSArray *)photos
{
    return [photos valueForKeyPath:[NSString stringWithFormat:@"@distinctUnionOfObjects.%@", FLICKR_PHOTO_OWNER]];
}

修改上面註釋過的方法,使他們接收這兩個新的屬性,並傳給region和photographer的category方法,同樣貼出最終的代碼

+ (Photo *)photoWithFlickrInfo:(NSDictionary *)photoDictionary inManagedObjectContext:(NSManagedObjectContext *)context
         existingPhotographers:(NSMutableArray *)photographers
               existingRegions:(NSMutableArray *)regions
{
            Photo *photo = nil;
        NSString *uniqueID = [photoDictionary valueForKey:FLICKR_PHOTO_ID];
            photo = [NSEntityDescription insertNewObjectForEntityForName:@"Photo" inManagedObjectContext:context];
            photo.uique = uniqueID;
            photo.title = [FlickrHelper titleOfPhoto:photoDictionary];
            photo.subtitle = [FlickrHelper subtitleOfPhoto:photoDictionary];
            photo.imageURL = [[FlickrHelper URLforPhoto:photoDictionary] absoluteString];
            photo.thumbnaiURL = [[FlickrHelper URLforThumbnail:photoDictionary] absoluteString];
    
    photo.photographer = [Photographer photographerWithName:[FlickrHelper ownerOfPhoto:photoDictionary]
                                     inManagedObjectContext:context
                                      existingPhotographers:photographers];
    photo.region = [Region regionWithPlaceID:[FlickrHelper placeIDforPhoto:photoDictionary]
                             andPhotographer:photo.photographer
                      inManagedObjectContext:context
                             existingRegions:regions];
    
    return photo;

    
    
//    NSFetchRequest *req = [[NSFetchRequest alloc] initWithEntityName:@"Photo"];
//    req.predicate = [NSPredicate predicateWithFormat:@"uique = %@",uniqueID];
//    NSError *error;
//    NSArray *matches = [context executeFetchRequest:req error:&error];
//    if (!matches || error || [matches count] > 1) {
//        //handle error
//
//    } else if ([matches count] == 1) {
//        photo = [matches firstObject];
//    } else {
//        photo = [NSEntityDescription insertNewObjectForEntityForName:@"Photo" inManagedObjectContext:context];
//        photo.uique = uniqueID;
//        photo.title = [FlickrHelper titleOfPhoto:photoDictionary];
//        photo.subtitle = [FlickrHelper subtitleOfPhoto:photoDictionary];
//        photo.imageURL = [[FlickrHelper URLforPhoto:photoDictionary] absoluteString];
//        photo.thumbnaiURL = [[FlickrHelper URLforThumbnail:photoDictionary] absoluteString];
//        
//        photo.photographer = [Photographer photographerWithName:[FlickrHelper ownerOfPhoto:photoDictionary]
//                                         inManagedObjectContext:context];
//        
//        photo.region = [Region regionWithPlaceID:[FlickrHelper placeIDforPhoto:photoDictionary]
//                                 andPhotographer:photo.photographer
//                          inManagedObjectContext:context];
//        NSLog(@"photoTitle: %@", photo.title);
//    }
//
    photo.photographer = [Photographer photographerWithName:[FlickrHelper ownerOfPhoto:photoDictionary]
                                     inManagedObjectContext:context
                                      existingPhotographers:photographers];
    photo.region = [Region regionWithPlaceID:[FlickrHelper placeIDforPhoto:photoDictionary]
                             andPhotographer:photo.photographer
                      inManagedObjectContext:context
                             existingRegions:regions];
    
    return photo;
}

修改Photographer的public方法,這裏我們對數組進行過濾而不是查詢數據庫,並在這裏修改了existingPhotographers這個可變數組(注意這個數組的變化會傳遞到Photo+Flickr文件中)

+ (Photographer *)photographerWithName:(NSString *)name
                inManagedObjectContext:(NSManagedObjectContext *)context
                 existingPhotographers:(NSMutableArray *)existingPhotographers
{
    Photographer *photographer = nil;
    if ([name length]) {
        NSArray *matches = [existingPhotographers filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"name = %@",name]] ;
        if (!matches || ([matches count] > 1)) {
            // handle error
        } else if (![matches count]) {
            photographer = [NSEntityDescription insertNewObjectForEntityForName:@"Photographer"
                                                         inManagedObjectContext:context];
            photographer.name = name;
            [existingPhotographers addObject:photographer];
            NSLog(@"photographer: %@", photographer.name);
        } else {
            photographer = [matches lastObject];
            NSLog(@"photographer: %@ already in database", photographer.name);
        }
    }
    return photographer;

}

同樣,修改Region+Create的.h和.m文件

+ (Region *)regionWithPlaceID:(NSString *)placeID
              andPhotographer:(Photographer *)photographer
       inManagedObjectContext:(NSManagedObjectContext *)context
              existingRegions:(NSMutableArray *)regions
{
    Region *region = nil;
    if ([placeID length]) {
        NSArray *matches = [regions filteredArrayUsingPredicate: [NSPredicate predicateWithFormat:@"placeID = %@", placeID]];
        
        if (!matches || ([matches count] > 1)) {
            // handle error
        } else if (![matches count]) {
            region = [NSEntityDescription insertNewObjectForEntityForName:@"Region"
                                                   inManagedObjectContext:context];
            region.placeID = placeID;
            region.photoCount = @1;
            [region addPhotographersObject:photographer];
            region.photographerCount = @1;
            
            [regions addObject:region];
            NSLog(@"region id %@", region.placeID);
        } else {
            region = [matches lastObject];
            region.photoCount = @([region.photoCount intValue] + 1);
            
            if (![region.photographers member:photographer]) {
                [region addPhotographersObject:photographer];
                region.photographerCount = @([region.photographerCount intValue] + 1);;
            }
            NSLog(@"region id%@ already in database", region.placeID);
        }
    }
    
    return region;
}

這樣,我們不用一個個查詢下載好的圖片,只需要使用這3個查詢就夠了。運行一下吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章