我的应用程序想要获取
iphone的专辑列表和某些专辑中的所有照片.
在应用程序中,我列举了iphone的一张专辑中的照片.
由于某些专辑可能有很多照片,考虑到性能,我使用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,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,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,50)]; thumbNail.tag = 99; [cell.contentView addSubview:thumbNail]; [thumbNail release]; nameLab = [[UILabel alloc] initWithFrame:CGRectMake(60,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
解决方法
我今天遇到了完全相同的问题.简而言之,原因是你无法从后台调度队列中执行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. }); });
对我来说,这可以保留和释放数据.不确定,如果这是正确的.