【问题标题】:GCD and KVO problemsGCD 和 KVO 问题
【发布时间】:2011-09-27 23:36:03
【问题描述】:

我的应用想要获取 iphone 的相册列表以及某个相册中的所有照片。

在应用程序中,我列举了一个 iPhone 相册中的照片。 由于某个专辑可能有很多照片,考虑到性能我使用 GCD:dispatch_async。但是当 KVO 调用的 tableview 单元格更新时,它总是崩溃。 我不知道我是否以错误的方式使用 KVO 或 GCD。

现在我也可以使用 performSelectorInBackground: 替换 dispatch_async。现在应用程序没有崩溃,但应用程序的性能很差:只有当你触摸它时才会显示单元格的标题,或者当有很多照片时滚动表格视图。也就是说,主线程必须被阻塞。

附上代码,核心代码在 AlbumListViewController.m 中。

谁能帮我查一下?

我只想知道: 1 如果使用 dispatch_async 为什么应用程序崩溃 2 在照片很多的情况下如何提高性能。

谢谢。

以下是我的代码:

// // RootViewController.h // 专辑演示 #进口 @interface RootViewController : UITableViewController { NSMutableArray *_listArray; } @property (nonatomic, 保留) NSMutableArray *listArray; @结尾 // RootViewController.m #import "RootViewController.h" #进口 #import "专辑列表视图控制器.h" NSString *thumnail = @"thumnail"; NSString *albumName = @"albumName"; NSString *albumNum = @"albumNum"; NSString *albumGroup = @"albumGroup"; @implementation RootViewController @synthesize listArray = _listArray; #pragma - #pragma 函数 -(无效)设置 { _listArray = [[NSMutableArray alloc] initWithCapacity:1]; self.title = @"相册"; } - (void)fetchAlbumList { ALAssetsLibrary *assetLib = [[[ALAssetsLibrary alloc] init] autorelease]; ALAssetsFilter *fileter = [ALAssetsFilter allPhotos]; [assetLib enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { 如果(组) { [组 setAssetsFilter:fileter]; NSString *_groupName = [组 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]; } 别的 { NSLog(@"_listArray :%@",_listArray); } } failureBlock:^(NSError *error) { NSLog(@"错误: %@", 错误);; } ]; } #pragma - #pragma ViewController 提升周期 - (void)viewDidLoad { [超级视图DidLoad]; [自我设置]; [self fetchAlbumList]; } - (void)viewWillAppear:(BOOL)动画 { [超级viewWillAppear:动画]; } - (void)viewDidAppear:(BOOL)动画 { [超级 viewDidAppear:动画]; } - (void)viewWillDisappear:(BOOL)动画 { [超级viewWillDisappear:动画]; } - (void)viewDidDisappear:(BOOL)动画 { [超级 viewDidDisappear:动画]; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 返回 50; } // 自定义表格视图中的节数。 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 返回 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 返回 [_listArray 计数]; } // 自定义表格视图单元格的外观。 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 静态 NSString *CellIdentifier = @"Cell"; UILabel *nameLab = nil; UILabel *numLab = nil; UIImageView *thumnailImage = nil; UIFont *font = [UIFont boldSystemFontOfSize:18]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 如果(细胞 == 零){ 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]; [缩略图发布]; nameLab = [[UILabel alloc] initWithFrame:CGRectMake(60, 10, 100, 30)]; nameLab.tag = 200; nameLab.backgroundColor = [UIColor clearColor]; nameLab.font = 字体; [cell.contentView addSubview:nameLab]; [nameLab 发布]; numLab = [[UILabel alloc] initWithFrame:CGRectMake(200, 10, 50, 30)]; numLab.tag = 300; numLab.backgroundColor = [UIColor clearColor]; numLab.textColor = [UIColor grayColor]; numLab.font = 字体; [cell.contentView addSubview:numLab]; [numLab 发布]; } 别的 { 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 = [标题 sizeWithFont:font]; CGRect rect = nameLab.frame; rect.size = 标题大小; nameLab.frame = 矩形; nameLab.text = 标题; 矩形 = numLab.frame; rect.origin.x = 60 + nameLab.frame.size.width + 10; numLab.frame = 矩形; numLab.text = [NSString stringWithFormat:@"(%d)",[[dic valueForKey:albumNum] intValue]]; // 配置单元格。 返回单元格; } - (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 Animation:YES]; [viewController 发布]; [tableView deselectRowAtIndexPath:indexPath 动画:YES]; } - (void)didReceiveMemoryWarning { // 如果没有超级视图,则释放视图。 [super didReceiveMemoryWarning]; // 放弃所有未使用的缓存数据、图像等。 } - (void)viewDidUnload { [超级 viewDidUnload]; // 放弃可以在 viewDidLoad 中或按需重新创建的任何内容的所有权。 // 例如:self.myOutlet = nil; } - (void)dealloc { My_Release (_listArray); [超级释放]; } @结尾 // AlbumListViewController.h // 专辑演示 #进口 #进口 @interface AlbumListViewController : UITableViewController { NSMutableArray *_marr; ALAssetsGroup *_assetsGroup; } @property (nonatomic, 保留) NSMutableArray *list; @property(非原子,保留)ALAssetsGroup *assetsGroup; - (id)initWithAssetGroup:(ALAssetsGroup *)group; @结尾 // 专辑列表视图控制器.m // 专辑演示 #import "专辑列表视图控制器.h" @interface PhotoObj : NSObject { NSString *_name; UIImage *_thumbnail; UIImage *_fullImage; } @property (nonatomic, copy) NSString *name; @property (nonatomic, 保留) UIImage *thumbnail; @property (nonatomic, 保留) UIImage *fullImage; @结尾 @implementation PhotoObj @synthesize name = _name; @synthesize thumbnail = _thumbnail,fullImage = _fullImage; - (void)dealloc { 我的_Release(_thumbnail); 我的_Release(_fullImage); My_Release(_name); [超级释放]; } @结尾 @interface AlbumListViewController() - (NSMutableArray*) 列表; - (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; @结尾 @implementation AlbumListViewController @synthesize assetsGroup = _assetsGroup; - (id)initWithAssetGroup:(ALAssetsGroup *)group { self = [self initWithStyle:UITableViewStylePlain]; 如果(自我) { _marr = [[NSMutableArray alloc] initWithCapacity:1]; self.assetsGroup = 组; self.tableView.delegate = self; self.tableView.dataSource = 自我; } 回归自我; } - (id)initWithStyle:(UITableViewStyle)style { self = [超级 initWithStyle:style]; 如果(自我){ // 自定义初始化 } 回归自我; } - (void)dealloc { My_Release(_marr); My_Release(_assetsGroup); [self removeObserver:self forKeyPath:@"list"]; [超级释放]; } - (void)didReceiveMemoryWarning { // 如果没有超级视图,则释放视图。 [super didReceiveMemoryWarning]; } #pragma mark - 查看生命周期 - (void)parseAssetGroup { [_marr removeAllObjects]; [self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { 如果(结果) { PhotoObj *obj = [[PhotoObj alloc] init]; obj.thumbnail = [UIImage imageWithCGImage:[结果缩略图]]; ALAssetRepresentation *represention = [结果 defaultRepresentation]; obj.fullImage = [UIImage imageWithCGImage:[representation fullScreenImage]]; obj.name = [[representation url] absoluteString]; [self willChangeValueForKey:@"list"]; [self insertObject:obj inListAtIndex:[_marr count]]; [self didChangeValueForKey:@"list"]; My_Release(obj); } }]; } - (void)viewDidLoad { [超级视图DidLoad]; [self addObserver:self forKeyPath:@"list" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:NULL]; /* 如果 performSelectorInBackground,性能很差 因为单元格的标题会显示很长时间,现在看来主线程被阻塞了 */ [self performSelectorInBackground:@selector(parseAssetGroup) withObject:nil]; /* 使用 dispatch_async 它总是崩溃 正如它所说的那样,tableview 更新有问题 */ // dispatch_async(dispatch_get_main_queue(), ^{ // [self parseAssetGroup]; // }); } - (void)viewDidUnload { [超级 viewDidUnload]; } #pragma mark - 表格视图数据源 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 返回 50; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // 返回部分的数量。 返回 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // 返回节中的行数。 返回 [_marr 计数]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 静态 NSString *CellIdentifier = @"Cell"; UIImageView *thumbNail = nil; UILabel *nameLab = nil; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 如果(细胞 == 零){ 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]; [拇指指甲释放]; 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 发布]; } 别的 { thumbNail = (UIImageView *)[cell.contentView viewWithTag:99]; nameLab = (UILabel *)[cell.contentView viewWithTag:199]; } // 配置单元格... PhotoObj *obj = [_marr objectAtIndex:indexPath.row]; nameLab.text = obj.name; thumbNail.image = obj.thumbnail; 返回单元格; } #pragma 标记 - - (NSUInteger)countOfList { 返回 [_marr 计数]; } - (NSMutableArray*) 列表 { 返回_marr; } - (void)setList:(NSMutableArray *)_arr { 如果 (_marr != _arr) { [_marr 发布]; _marr = _arr; } } - (id)objectInListAtIndex:(NSUInteger)idx { 返回 [_marr objectAtIndex:idx]; } - (void)insertObject:(id)anObject inListAtIndex:(NSUInteger)idx { if ([NSThread isMainThread]) { NSLog(@"插入主线程"); } 别的 { NSLog(@"插入不是主线程"); } [_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) 对象 改变:(NSDictionary *)改变 上下文:(无效*)上下文 { NSIndexSet *indices = [更改 objectForKey:NSKeyValueChangeIndexesKey]; 如果(索引 == 零) 返回; // 没事做 // 从索引集构建索引路径 NSUInteger indexCount = [索引计数]; NSUInteger 缓冲区[indexCount]; [索引 getIndexes:buffer maxCount:indexCount inIndexRange:nil]; NSMutableArray *indexPathArray = [NSMutableArray 数组]; for (int i = 0; i

【问题讨论】:

    标签: ios objective-c grand-central-dispatch key-value-observing


    【解决方案1】:

    我今天遇到了完全相同的问题。简而言之,原因是你不能从后台调度队列中执行 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.
      });
    });
    

    对我来说,这无需保留和释放数据即可工作。不确定,如果这正确。

    【讨论】:

    • 它可能与主/后台线程无关,就像我在其他应用程序中以相同的方式所做的那样,它运行良好。所以我还是不知道是什么问题。
    • 你是对的。 UI相关的任务应该在dispatch_get_main_queue中实现,其他的最好不要。我刚刚发现了各种队列之间的区别。谢谢你的提示。
    猜你喜欢
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-29
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多