首页 文章

GCD和KVO问题

提问于
浏览
6

我的应用程序想要获取iphone的专辑列表和某些专辑中的所有照片 .

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

现在,我使用performSelectorInBackground:替换dispatch_async . 现在应用程序没有崩溃,但应用程序的性能很差:只有当你触摸它时才会显示单元格的 Headers ,或者当有很多照片时滚动桌面视图 . 换句话说,必须阻止主线程 .

附件是代码,核心代码在AlbumListViewController.m中 .

任何人都可以帮我检查一下吗?

我只想知道:1如果使用dispatch_async 2应用程序崩溃的原因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

1 回答

  • 8

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

    对我来说,这可以保留和释放数据 . 不确定,如果这是正确的 .

相关问题