5

私のアプリは、iPhoneのアルバムリストと特定のアルバムのすべての写真を取得したいと考えています。GCDとKVOの問題

アプリでは、iPhoneの1つのアルバムに写真を列挙します。 あるアルバムの写真がたくさんあるかもしれないので、私はGCD:dispatch_asyncを使ってパフォーマンスを考えています。しかし、KVOによって呼び出されるtableviewセルが更新されると、常にクラッシュします。 KVOかGCDを間違った方法で使用しているかどうかはわかりません。

代わりにperformSelectorInBackground:dispatch_asyncの置き換えを使用します。今ではアプリがクラッシュすることはありませんが、アプリのパフォーマンスは悪いです。セルのタイトルは、写真がたくさんあるときにタッチするかテーブルビューをスクロールするときにのみ表示されます。つまり、メインスレッドをブロックする必要があります。

添付されているコードは、AlbumListViewController.mにあります。

私はそれを確認する手助けができますか?

私には知りたいことがあります: 1なぜdispatch_asyncを使用するとアプリがクラッシュするのですか? 2多くの写真の場合のパフォーマンスを向上させる方法を教えてください。

ありがとうございました。以下は

は私のコードです:

 
// 
// 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

答えて

7

今日は正確に同じ問題に遭遇しました。要するに、UIKit関連のタスク(テーブルの更新、私の場合はTextview)をバックグラウンドディスパッチキューから行うことができないからです。詳細は以下のリンクを確認してください。

comparison GCD vs. performSelectorInBackground: dispatch_async not in background

以下れる可能性のある解決策:代わりにクラッシュを引き起こしKVO変数に直接お使いの更新ブロックであなたの新鮮なデータを割り当てるので、あなたはメインキューにこれを行い、別のブロックを派遣、あなたの更新ブロックの中から。これを行うためにdispatch_async_f関数を使用すると、データへのポインタをコンテキストとして渡すことができます。このよう

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. 
    }); 
}); 

は私にとって、これは保持し、データを解放せずに動作します。これが正しいかどうかわかりません。

+0

ありがとう、私は見てみましょう。 – scorpiozj

+0

それは機能しましたか?あなたは同じ方法で他のアプリでやったように、メイン/バックグラウンドスレッドで問題にならないかもしれないと答えた – tomk

+0

という質問に印を付けることができました。だから私はまだ何が問題なのか分からない。 – scorpiozj