5

Moja aplikacja chce pobrać listę albumów iPhone'a i wszystkich zdjęć z określonego albumu.Problemy z GCD i KVO

W aplikacji wyliczam zdjęcia w jednym albumie iPhone'a. Ponieważ może być dużo zdjęć danego albumu, biorąc pod uwagę wydajność, używam GCD: dispatch_async. Ale zawsze zawiesza się, gdy aktualizacje komórki widoku tabeli są wywoływane przez KVO. Nie mam pojęcia, czy używam KVO lub GCD w niewłaściwy sposób.

Teraz alternatywnie używam performSelectorInBackground: zastępując dispatch_async. Teraz aplikacja nie jest uszkodzona, ale wydajność aplikacji jest słaba: tytuł komórki zostanie wyświetlony tylko po dotknięciu lub przewinięciu widoku tabeli, gdy jest dużo zdjęć. Innymi słowy, główny wątek musi być zablokowany.

Załączony jest kod, a kod podstawowy znajduje się w AlbumListViewController.m.

Czy ktoś może mi pomóc to sprawdzić?

Chcę tylko wiedzieć: 1 dlaczego aplikacja jest uszkodzona, jeśli używasz dispatch_async 2 Jak mogę poprawić wydajność w przypadku wielu zdjęć.

dzięki.

Poniżej jest mój kod:

 
// 
// RootViewController.h 
// AlbumDemo 


#import 

@interface RootViewController : UITableViewController { 
    NSMutableArray *_listArray; 
} 

@property (nonatomic, retain) NSMutableArray *listArray; 

@end 


// RootViewController.m 


#import "RootViewController.h" 
#import 
#import "AlbumListViewController.h" 
NSString *thumnail = @"thumnail"; 
NSString *albumName = @"albumName"; 
NSString *albumNum = @"albumNum"; 
NSString *albumGroup = @"albumGroup"; 
@implementation RootViewController 
@synthesize listArray = _listArray; 

#pragma - 
#pragma Function 
- (void)setUp 
{ 
    _listArray = [[NSMutableArray alloc] initWithCapacity:1]; 
    self.title = @"Albums"; 
} 
- (void)fetchAlbumList 
{ 
    ALAssetsLibrary *assetLib = [[[ALAssetsLibrary alloc] init] autorelease]; 
    ALAssetsFilter *fileter = [ALAssetsFilter allPhotos]; 
    [assetLib enumerateGroupsWithTypes:ALAssetsGroupAll 
          usingBlock:^(ALAssetsGroup *group, BOOL *stop) 
    { 
     if (group) 
     { 
      [group setAssetsFilter:fileter]; 
      NSString *_groupName = [group valueForProperty:ALAssetsGroupPropertyName]; 
      NSNumber *_groupNum = [NSNumber numberWithInteger:[group numberOfAssets]]; 
      UIImage *_groupImage = [UIImage imageWithCGImage:[group posterImage]]; 

      NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:_groupName,albumName,_groupNum,albumNum,_groupImage,thumnail,group,albumGroup, nil]; 

      [_listArray addObject:dic]; 
      [self.tableView reloadData]; 

     } 
     else 
     { 
      NSLog(@"_listArray :%@",_listArray); 
     } 

    } 
          failureBlock:^(NSError *error) 
    { 
     NSLog(@"Error: %@", error);; 
    } 
    ]; 

} 
#pragma - 
#pragma ViewController lift cycle 
- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self setUp]; 
    [self fetchAlbumList]; 

} 

- (void)viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 
} 

- (void)viewDidAppear:(BOOL)animated 
{ 
    [super viewDidAppear:animated]; 
} 

- (void)viewWillDisappear:(BOOL)animated 
{ 
    [super viewWillDisappear:animated]; 
} 

- (void)viewDidDisappear:(BOOL)animated 
{ 
    [super viewDidDisappear:animated]; 
} 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return 50; 
} 
// Customize the number of sections in the table view. 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return 1; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    return [_listArray count]; 
} 

// Customize the appearance of table view cells. 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Cell"; 
    UILabel *nameLab = nil; 
    UILabel *numLab = nil; 
    UIImageView *thumnailImage = nil; 

    UIFont *font = [UIFont boldSystemFontOfSize:18]; 

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
     cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 

     thumnailImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0,50, 50)]; 
     thumnailImage.tag = 100; 
     [cell.contentView addSubview:thumnailImage]; 
     [thumnailImage release]; 


     nameLab = [[UILabel alloc] initWithFrame:CGRectMake(60, 10, 100, 30)]; 
     nameLab.tag = 200; 
     nameLab.backgroundColor = [UIColor clearColor]; 
     nameLab.font = font; 
     [cell.contentView addSubview:nameLab]; 
     [nameLab release]; 

     numLab = [[UILabel alloc] initWithFrame:CGRectMake(200, 10, 50, 30)]; 
     numLab.tag = 300; 
     numLab.backgroundColor = [UIColor clearColor]; 
     numLab.textColor = [UIColor grayColor]; 
     numLab.font = font; 
     [cell.contentView addSubview:numLab]; 
     [numLab release]; 
    } 
    else 
    { 
     thumnailImage = (UIImageView *)[cell.contentView viewWithTag:100]; 
     nameLab = (UILabel *)[cell.contentView viewWithTag:200]; 
     numLab = (UILabel *)[cell.contentView viewWithTag:300]; 
    } 

    NSDictionary *dic = [self.listArray objectAtIndex:indexPath.row]; 

    thumnailImage.image = (UIImage *)[dic valueForKey:thumnail]; 

    NSString *title = [dic valueForKey:albumName]; 
    CGSize titleSize = [title sizeWithFont:font]; 
    CGRect rect = nameLab.frame; 
    rect.size = titleSize; 
    nameLab.frame = rect; 
    nameLab.text = title; 

    rect = numLab.frame; 
    rect.origin.x = 60 + nameLab.frame.size.width + 10; 
    numLab.frame = rect; 

    numLab.text = [NSString stringWithFormat:@"(%d)",[[dic valueForKey:albumNum] intValue]]; 


    // Configure the cell. 
    return cell; 
} 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSDictionary *dic = [self.listArray objectAtIndex:indexPath.row]; 

    AlbumListViewController *viewController = [[AlbumListViewController alloc] initWithAssetGroup:[dic valueForKey:albumGroup]]; 
    [self.navigationController pushViewController:viewController animated:YES]; 
    [viewController release]; 
    [tableView deselectRowAtIndexPath:indexPath animated:YES]; 
} 

- (void)didReceiveMemoryWarning 
{ 
    // Releases the view if it doesn't have a superview. 
    [super didReceiveMemoryWarning]; 

    // Relinquish ownership any cached data, images, etc that aren't in use. 
} 

- (void)viewDidUnload 
{ 
    [super viewDidUnload]; 

    // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand. 
    // For example: self.myOutlet = nil; 
} 

- (void)dealloc 
{ 
    My_Release (_listArray); 
    [super dealloc]; 
} 

@end 


// AlbumListViewController.h 
// AlbumDemo 

#import 
#import 

@interface AlbumListViewController : UITableViewController { 
    NSMutableArray *_marr; 
    ALAssetsGroup *_assetsGroup; 
} 

@property (nonatomic, retain) NSMutableArray *list; 
@property (nonatomic, retain) ALAssetsGroup *assetsGroup; 

- (id)initWithAssetGroup:(ALAssetsGroup *)group; 
@end 

// AlbumListViewController.m 
// AlbumDemo 

#import "AlbumListViewController.h" 

@interface PhotoObj : NSObject { 
    NSString *_name; 
    UIImage *_thumbnail; 
    UIImage *_fullImage; 
} 

@property (nonatomic, copy ) NSString *name; 
@property (nonatomic, retain) UIImage *thumbnail; 
@property (nonatomic, retain) UIImage *fullImage; 
@end 

@implementation PhotoObj 
@synthesize name = _name; 
@synthesize thumbnail = _thumbnail,fullImage = _fullImage; 
- (void)dealloc 
{ 
    My_Release(_thumbnail); 
    My_Release(_fullImage); 
    My_Release(_name); 
    [super dealloc]; 
} 
@end 

@interface AlbumListViewController() 

- (NSMutableArray*)list; 
- (NSUInteger)countOfList; 
- (id)objectInListAtIndex:(NSUInteger)idx; 
- (void)insertObject:(id)anObject inListAtIndex:(NSUInteger)idx; 
- (id)objectInListAtIndex:(NSUInteger)idx; 
- (void)removeObjectFromListAtIndex:(NSUInteger)idx; 
- (void)replaceObjectInListAtIndex:(NSUInteger)idx withObject:(id)anObject; 
- (void)setList:(NSMutableArray *)_arr; 

@end 

@implementation AlbumListViewController 
@synthesize assetsGroup = _assetsGroup; 

- (id)initWithAssetGroup:(ALAssetsGroup *)group 
{ 
    self = [self initWithStyle:UITableViewStylePlain]; 
    if (self) 
    { 
     _marr = [[NSMutableArray alloc] initWithCapacity:1]; 
     self.assetsGroup = group; 
     self.tableView.delegate = self; 
     self.tableView.dataSource = self; 

    } 
    return self; 
} 

- (id)initWithStyle:(UITableViewStyle)style 
{ 
    self = [super initWithStyle:style]; 
    if (self) { 
     // Custom initialization 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    My_Release(_marr); 
    My_Release(_assetsGroup); 
    [self removeObserver:self forKeyPath:@"list"]; 
    [super dealloc]; 
} 

- (void)didReceiveMemoryWarning 
{ 
    // Releases the view if it doesn't have a superview. 
    [super didReceiveMemoryWarning]; 

} 

#pragma mark - View lifecycle 
- (void)parseAssetGroup 
{ 
    [_marr removeAllObjects]; 
    [self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { 
     if (result) 
     { 
      PhotoObj *obj = [[PhotoObj alloc] init]; 
      obj.thumbnail = [UIImage imageWithCGImage:[result thumbnail]]; 
      ALAssetRepresentation *represention = [result defaultRepresentation]; 
      obj.fullImage = [UIImage imageWithCGImage:[represention fullScreenImage]]; 
      obj.name = [[represention url] absoluteString]; 


      [self willChangeValueForKey:@"list"]; 
      [self insertObject:obj inListAtIndex:[_marr count]]; 
      [self didChangeValueForKey:@"list"]; 
      My_Release(obj); 
     } 

    }]; 

} 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self addObserver:self forKeyPath:@"list" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:NULL]; 
    /* 
    if performSelectorInBackground, the perofrmance is poor 
    as the title of the cell will be shown in a long time and it now seems the main thread is blocked 
    */ 
    [self performSelectorInBackground:@selector(parseAssetGroup) withObject:nil]; 
    /* 
    using dispatch_async it always crashes 
    as it says the sth is wrong with the tableview update 

    */ 

// dispatch_async(dispatch_get_main_queue(), ^{ 
//  [self parseAssetGroup]; 
// }); 
} 

- (void)viewDidUnload 
{ 
    [super viewDidUnload]; 

} 

#pragma mark - Table view data source 
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return 50; 
} 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    // Return the number of sections. 
    return 1; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    // Return the number of rows in the section. 
    return [_marr count]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Cell"; 

    UIImageView *thumbNail = nil; 
    UILabel *nameLab = nil; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
     cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 

     thumbNail = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)]; 
     thumbNail.tag = 99; 
     [cell.contentView addSubview:thumbNail]; 
     [thumbNail release]; 

     nameLab = [[UILabel alloc] initWithFrame:CGRectMake(60, 10, 240, 40)]; 
     nameLab.numberOfLines = 2; 
     nameLab.font = [UIFont systemFontOfSize:16]; 
     nameLab.tag = 199; 
     [cell.contentView addSubview:nameLab]; 
     [nameLab release]; 
    } 
    else 
    { 
     thumbNail = (UIImageView *)[cell.contentView viewWithTag:99]; 
     nameLab = (UILabel *)[cell.contentView viewWithTag:199]; 
    } 
    // Configure the cell... 
    PhotoObj *obj = [_marr objectAtIndex:indexPath.row]; 
    nameLab.text = obj.name; 
    thumbNail.image = obj.thumbnail; 

    return cell; 
} 
#pragma mark - 
- (NSUInteger)countOfList 
{ 
    return [_marr count]; 
} 
- (NSMutableArray*)list 
{ 
    return _marr; 
} 
- (void)setList:(NSMutableArray *)_arr 
{ 
    if (_marr != _arr) 
    { 
     [_marr release]; 
     _marr = _arr; 
    } 
} 

- (id)objectInListAtIndex:(NSUInteger)idx 
{ 
    return [_marr objectAtIndex:idx]; 
} 

- (void)insertObject:(id)anObject inListAtIndex:(NSUInteger)idx 
{ 
    if ([NSThread isMainThread]) 
    { 
     NSLog(@"insert main thread"); 
    } 
    else 
    { 
     NSLog(@"insert not main thread"); 
    } 
    [_marr insertObject:anObject atIndex:idx]; 
} 


- (void)removeObjectFromListAtIndex:(NSUInteger)idx 
{ 
    [_marr removeObjectAtIndex:idx]; 
} 
- (void)replaceObjectInListAtIndex:(NSUInteger)idx withObject:(id)anObject 
{ 
    [_marr replaceObjectAtIndex:idx withObject:anObject]; 
} 
- (void)observeValueForKeyPath:(NSString *)keyPath 
         ofObject:(id)object 
         change:(NSDictionary *)change 
         context:(void *)context 
{ 
    NSIndexSet *indices = [change objectForKey:NSKeyValueChangeIndexesKey]; 
    if (indices == nil) 
     return; // Nothing to do 

    // Build index paths from index sets 
    NSUInteger indexCount = [indices count]; 
    NSUInteger buffer[indexCount]; 
    [indices getIndexes:buffer maxCount:indexCount inIndexRange:nil]; 

    NSMutableArray *indexPathArray = [NSMutableArray array]; 
    for (int i = 0; i

Odpowiedz

7

wpadłem na dokładnie tym samym problemem dzisiaj. W skrócie, powodem jest to, że nie można wykonywać zadań związanych z UIKit, takich jak aktualizowanie tabeli, lub w moim przypadku widok tekstu z kolejki wywołania w tle. Sprawdź poniższy link, aby uzyskać więcej informacji.

comparison GCD vs. performSelectorInBackground: dispatch_async not in background

Możliwym rozwiązaniem jest następujący: zamiast przypisywania swoich nowych danych w swoim bloku aktualizacji bezpośrednio do zmiennej KVO co powoduje katastrofę, to wysyła inny blok, który robi to do głównej kolejki, od wewnątrz bloku aktualizacji. Jeśli użyjesz do tego funkcji dispatch_async_f, możesz przekazać wskaźnik do danych jako kontekst.

Jak to:

dispatch_async(yourQueue, ^() { 
    NSArray *data; 
    // do stuff to alloc and fill the array 
    // ... 
    dispatch_async(dispatch_get_main_queue(), ^() { 
    myObj.data = data; // the assignment, which triggers the KVO. 
    }); 
}); 

Dla mnie to działa bez zatrzymywania i zwalniania danych. Nie jestem pewien, czy to prawda.

+0

dzięki, przyjrzę się. – scorpiozj

+0

Czy to działa? Wtedy możesz oznaczyć pytanie jako odpowiedź – tomk

+0

może to nie mieć znaczenia w wątku głównym/tle, tak jak zrobiłem to w innej aplikacji w ten sam sposób i działa dobrze. WIĘC nadal nie wiem, na czym polega problem. – scorpiozj