Lazytableimages not load on custom cell storyboard - ios

I'm trying to load Lazy Tableview in a custom cell in storyboard, but the table view just looks blank:
This is the source code:
LazyLoadTable.m
#import "CellData.h"
#import "LazyLoadTableView.h"
#import "CustomCell.h"
#import "ParseOperation.h"
#import "ImageDownloader.h"
#define kCustomRowHeight 157
#define kCustomRowCount 1
static NSString *const xmlDataUrl =
#"http://itunes.apple.com/us/rss/topfreeapplications/limit=300/xml";
#interface LazyLoadTableView ()
#end
#implementation LazyLoadTableView
#synthesize tableElements;
#synthesize queue;
#synthesize connection;
#synthesize xmlData;
#synthesize tView;
#synthesize imageDownloadsInProgress;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.tableElements=[[NSMutableArray alloc] init];
self.imageDownloadsInProgress = [NSMutableDictionary dictionary];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title=#"Menu";
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:xmlDataUrl]];
self.connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
// Test the validity of the connection object.
NSAssert(self.connection != nil, #"Failure to create URL connection.");
// show in the status bar that network activity is starting
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int count = [tableElements count];
// ff there's no data yet, return enough rows to fill the screen
if (count == 0)
{
return kCustomRowCount;
}
return count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// customize the appearance of table view cells
static NSString *placeholderCellIdentifier = #"PlaceholderCell";
// add a placeholder cell while waiting on table data
int nodeCount = [self.tableElements count];
if (nodeCount == 0 && indexPath.row == 0)
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:placeholderCellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:placeholderCellIdentifier];
cell.detailTextLabel.textAlignment = NSTextAlignmentCenter ;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
cell.detailTextLabel.text = #"Loading…";
return cell;
}
static NSString *CustomCellIdentifier = #"CustomCellIdentifier";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CustomCellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[CustomCell class]])
cell = (CustomCell *) [nib objectAtIndex:0];
}
// Leave cells empty if there's no data yet
if (nodeCount > 0)
{
// Set up the cell...
CellData *cellData = (self.tableElements)[indexPath.row];
cell.name.text = cellData.name;
// Only load cached images; defer new downloads until scrolling ends
if (!cellData.icon)
{
if (self.tView.dragging == NO && self.tView.decelerating == NO)
{
[self startIconDownload:cellData forIndexPath:indexPath];
}
// if a download is deferred or in progress, return a placeholder image
cell.itemImage.image = [UIImage imageNamed:#"Placeholder.png"];
}
else
{
cell.itemImage.image = cellData.icon;
}
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return kCustomRowHeight;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.xmlData = [NSMutableData data]; // start off with new data
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.xmlData appendData:data]; // append incoming data
}
#pragma mark -
#pragma mark NSURLConnection delegate methods
- (void)handleError:(NSError *)error
{
NSString *errorMessage = [error localizedDescription];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Cannot Show Data"
message:errorMessage
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if ([error code] == kCFURLErrorNotConnectedToInternet)
{
// if we can identify the error, we can present a more precise message to the user.
NSDictionary *userInfo = #{NSLocalizedDescriptionKey: #"No Connection Error"};
NSError *noConnectionError = [NSError errorWithDomain:NSCocoaErrorDomain
code:kCFURLErrorNotConnectedToInternet
userInfo:userInfo];
[self handleError:noConnectionError];
}
else
{
// otherwise handle the error generically
[self handleError:error];
}
self.connection = nil; // release our connection
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.connection = nil; // release our connection
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// create the queue to run our ParseOperation
self.queue = [[NSOperationQueue alloc] init];
ParseOperation *operation = [[ParseOperation alloc] initWithData:self.xmlData delegate:self];
[queue addOperation:operation]; // this will start the "ParseOperation"
self.xmlData = nil;
}
- (void)didFinishParsing:(NSArray *)cellDataList
{
[self performSelectorOnMainThread:#selector(handleLoadedApps:) withObject:cellDataList waitUntilDone:NO];
self.queue = nil; // we are finished with the queue and our ParseOperation
}
- (void)parseErrorOccurred:(NSError *)error
{
[self performSelectorOnMainThread:#selector(handleError:) withObject:error waitUntilDone:NO];
}
- (void)handleLoadedApps:(NSArray *)loadedCellData
{
[self.tableElements addObjectsFromArray:loadedCellData];
// tell our table view to reload its data, now that parsing has completed
[self.tView reloadData];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return YES;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)startIconDownload:(CellData *)cellData forIndexPath:(NSIndexPath *)indexPath
{
ImageDownloader *imageDownloader = imageDownloadsInProgress[indexPath];
if (imageDownloader == nil)
{
imageDownloader = [[ImageDownloader alloc] init];
imageDownloader.cellData = cellData;
imageDownloader.indexPathInTableView = indexPath;
imageDownloader.delegate = self;
imageDownloadsInProgress[indexPath] = imageDownloader;
[imageDownloader startDownload];
}
}
// this method is used in case the user scrolled into a set of cells that don't have their app icons yet
- (void)loadImagesForOnscreenRows
{
if ([self.tableElements count] > 0)
{
NSArray *visiblePaths = [self.tView indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths)
{
CellData *cellData = (self.tableElements)[indexPath.row];
if (!cellData.icon) // avoid the app icon download if the app already has an icon
{
[self startIconDownload:cellData forIndexPath:indexPath];
}
}
}
}
// called by our ImageDownloader when an icon is ready to be displayed
- (void)imageDidLoad:(NSIndexPath *)indexPath
{
ImageDownloader *imageDownloader = imageDownloadsInProgress[indexPath];
if (imageDownloader != nil)
{
CustomCell *cell = (CustomCell *)[self.tView cellForRowAtIndexPath:imageDownloader.indexPathInTableView];
// Display the newly loaded image
cell.itemImage.image = imageDownloader.cellData.icon;
}
}
#pragma mark -
#pragma mark Deferred image loading (UIScrollViewDelegate)
// Load images for all onscreen rows when scrolling is finished
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate)
{
[self loadImagesForOnscreenRows];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self loadImagesForOnscreenRows];
}
#end
CustomCell.m
#import "CustomCell.h"
#implementation CustomCell
#synthesize itemImage;
#synthesize name;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end

Didn't read through all that code but it looks like you are trying to lazy load an image with some text. I highly recommend using SDWebImage for the lazy loading. It's simple and very straightforward to set up.

Related

Duplicate data when scrolling a UITableView

I had a problem with tableview which is when the table first shows everything looks great but if I scroll up and and down, NSLog(#"page name : %#", cell.pageName.text); output value will be duplicate and it will cause the scrolling to be lagged. This is my code :
#import "HomeTVC.h"
#import "facebook.h"
#import "HomeTVCell.h"
#import <SDWebImage/UIImageView+WebCache.h>
#interface HomeTVC ()<UITableViewDataSource, UITableViewDelegate>
{
NSDictionary *userPageLikesParams;
NSArray *pagesInfo;
}
#end
#implementation HomeTVC
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
facebook *fb = [[facebook alloc] init];
userPageLikesParams = #{#"fields": #"about,name,created_time,picture",#"limit": #"10"} ;
[fb getUserPagelikes:userPageLikesParams completionHandler:^(NSDictionary *pagesResult) {
if (pagesResult != nil) {
pagesInfo = pagesResult[#"data"];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"table count : %d", (int)pagesInfo.count);
if (pagesInfo == nil) {
return 0;
} else {
return pagesInfo.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
HomeTVCell *cell = (HomeTVCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[HomeTVCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSURL *imageURL = [NSURL URLWithString:[pagesInfo[indexPath.row] valueForKeyPath:#"picture.data.url"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
// UIImage *pageProfileImage = [UIImage imageWithData:imageData];
// NSLog(#"pages info : %#", pagesInfo[indexPath.row]);
// NSLog(#"pages info image URL : %#", imageURL);
// cache the image using sdwebimage
cell.pageProfilePic.layer.backgroundColor=[[UIColor clearColor] CGColor];
cell.pageProfilePic.layer.borderWidth= 2.0;
cell.pageProfilePic.layer.masksToBounds = YES;
cell.pageProfilePic.layer.borderColor=[[UIColor whiteColor] CGColor];
cell.pageProfilePic.layer.cornerRadius= 30.0;
[cell.pageProfilePic sd_setImageWithURL:imageURL placeholderImage:[UIImage imageNamed:#"placeholder.jpg"]];
//cell.pageProfilePic.image = pageProfileImage;
cell.pageName.text = pagesInfo[indexPath.row][#"name"];
NSLog(#"page name : %#", cell.pageName.text);
return cell;
}
Thanks in advance.

xCode - UITableView compacted

i have a table view with section and i would like to start the table view with all section grouped like this:
Section1
Section2
Section3
When i tap on a section title, it expand the rows
Section1
Row1
Row2
Row3
Section2
Section3
i requested to all please see the answer if you dont like,just comment
this is for beginners....
this code also refered from net only and i changed few things
this link
creat class file...
ViewController.h file should be
#import <UIKit/UIKit.h>
#import "oneViewController.h"
#import "secondViewController.h"
#import "thirdViewController.h"
#interface ViewController : UIViewController
{
oneViewController *one;
secondViewController *second;
thirdViewController *third;
}
#end
ViewController.m file should be
#import "ViewController.h"
#import "Cell1.h"
#import "Cell2.h"
#interface ViewController()<UITableViewDataSource,UITableViewDelegate>
{
NSMutableArray *dataList;
}
#property (assign)BOOL isOpen;
#property (nonatomic,retain)NSIndexPath *selectIndex;
#property (nonatomic,retain)IBOutlet UITableView *expansionTableView;
#end
#implementation ViewController
#synthesize isOpen,selectIndex;
- (void)dealloc
{
[dataList release];
dataList= nil;
self.expansionTableView = nil;
self.isOpen = NO;
self.selectIndex = nil;
[super dealloc];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nil];
if (self) {
one=[[oneViewController alloc]init ];
second=[[secondViewController alloc]init];
third=[[thirdViewController alloc]init];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"array" ofType:#"plist"];
dataList = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSLog(#"%lu",(unsigned long)dataList.count);
self.expansionTableView.sectionFooterHeight = 0;
self.expansionTableView.sectionHeaderHeight = 0;
self.isOpen = NO;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [dataList count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.isOpen) {
if (self.selectIndex.section == section) {
return [[[dataList objectAtIndex:section] objectForKey:#"list"] count]+1;
}
}
return 1;
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 40;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%#",[[dataList objectAtIndex:indexPath.section]objectForKey:#"list"] );
if (self.isOpen&&indexPath.section == indexPath.section&&indexPath.row!=0) {
static NSString *CellIdentifier = #"Cell2";
Cell2 *cell = (Cell2*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil] objectAtIndex:0];
}
if ([[dataList objectAtIndex:indexPath.section]objectForKey:#"list"])
{
NSArray *list = [[dataList objectAtIndex:indexPath.section] objectForKey:#"list"];
cell.titleLabel.text = [list objectAtIndex:indexPath.row-1];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
return cell;
}else
{
static NSString *CellIdentifier = #"Cell1";
Cell1 *cell = (Cell1*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil] objectAtIndex:0];
}
NSString *name = [[dataList objectAtIndex:indexPath.section] objectForKey:#"name"];
cell.titleLabel.text = name;
//[cell changeArrowWithUp:([indexPath isEqual:indexPath]?YES:NO)];
if ([indexPath isEqual:indexPath])
{
[cell changeArrowWithUp:YES];
}
else
{
[cell changeArrowWithUp:NO];
}
return cell;
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
if ([indexPath isEqual:self.selectIndex]) {
Cell1 *cell = (Cell1 *)[self.expansionTableView cellForRowAtIndexPath:self.selectIndex];
[cell changeArrowWithUp:YES];
self.isOpen = NO;
[self didSelectCellRowFirstDo:NO nextDo:NO];
self.selectIndex = nil;
}else
{
if (!self.selectIndex) {
self.selectIndex = indexPath;
[self didSelectCellRowFirstDo:YES nextDo:NO];
Cell1 *cell = (Cell1 *)[self.expansionTableView cellForRowAtIndexPath:self.selectIndex];
[cell changeArrowWithUp:NO];
}else
{
NSIndexPath *tempIndex=indexPath;
if (tempIndex)
{
Cell1 *cell = (Cell1 *)[self.expansionTableView cellForRowAtIndexPath:tempIndex];
[cell changeArrowWithUp:NO];
}
Cell1 *cell = (Cell1 *)[self.expansionTableView cellForRowAtIndexPath:self.selectIndex];
[cell changeArrowWithUp:YES];
[self didSelectCellRowFirstDo:NO nextDo:YES];
}
}
}else
{
NSDictionary *dic = [dataList objectAtIndex:indexPath.section];
NSArray *list = [dic objectForKey:#"list"];
NSString *item = [list objectAtIndex:indexPath.row-1];
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"sample tree" message:item delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alert show];
// NSArray* temp=[NSArray arrayWithObjects:one,second,third,nil];
// [self viewPresent:[temp objectAtIndex:indexPath.row-1] sectionAt:item];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
//method for pushing view controller
-(void)viewPresent:(UIViewController *)viewName sectionAt:(NSString *)sectionValue
{
[[self navigationController]pushViewController:viewName animated:YES];
}
- (void)didSelectCellRowFirstDo:(BOOL)firstDoInsert nextDo:(BOOL)nextDoInsert
{
self.isOpen = firstDoInsert;
[self.expansionTableView beginUpdates];
int section = self.selectIndex.section;
int contentCount = [[[dataList objectAtIndex:section] objectForKey:#"list"] count];
NSMutableArray* rowToInsert = [[NSMutableArray alloc] init];
for (NSUInteger i = 1; i < contentCount + 1; i++) {
NSIndexPath* indexPathToInsert = [NSIndexPath indexPathForRow:i inSection:section];
[rowToInsert addObject:indexPathToInsert];
}
if (firstDoInsert)
{ [self.expansionTableView insertRowsAtIndexPaths:rowToInsert withRowAnimation:UITableViewRowAnimationTop];
}
else
{
[self.expansionTableView deleteRowsAtIndexPaths:rowToInsert withRowAnimation:UITableViewRowAnimationTop];
}
[rowToInsert release];
[self.expansionTableView endUpdates];
if (nextDoInsert) {
self.isOpen = YES;
self.selectIndex = [self.expansionTableView indexPathForSelectedRow];
[self didSelectCellRowFirstDo:YES nextDo:NO];
}
// if (self.isOpen) [self.expansionTableView scrollToNearestSelectedRowAtScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
#end
then create two cell with subclass of UITableViewCell
the first cell should be
.h file
#import <UIKit/UIKit.h>
#interface Cell1 : UITableViewCell
#property (nonatomic,retain)IBOutlet UILabel *titleLabel;
#property (nonatomic,retain)IBOutlet UIImageView *arrowImageView;
- (void)indication:(BOOL)up;
#end
.m file
#import "Cell1.h"
#implementation Cell1
#synthesize titleLabel,arrowImageView;
- (void)dealloc
{
self.titleLabel = nil;
self.arrowImageView = nil;
[super dealloc];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)indication:(BOOL)up
{
if (up) {
self.arrowImageView.image = [UIImage imageNamed:#"UpAccessory.png"];
}else
{
self.arrowImageView.image = [UIImage imageNamed:#"DownAccessory.png"];
}
}
the second cell should be
.h file
#import <UIKit/UIKit.h>
#interface Cell2 : UITableViewCell
#property (nonatomic,retain)IBOutlet UILabel *titleLabel;
#end
.m file
#import "Cell2.h"
#implementation Cell2
#synthesize titleLabel;
- (void)dealloc
{
self.titleLabel = nil;
[super dealloc];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
for learning purpose my values are like below
(
{
list = (
1,
2,
3
);
name = Numbers;
},
{
list = (
a,
b,
c
);
name = Alaphabets;
},
{
list = (
India,
China,
Australia
);
name = Countries;
}
)

async loading for UITableView

Is the below code, I am loading data using printerArray = [SMPort searchPrinter];. This an expensive operating and locks the ui. Is there a way to do this asynchronously so I can show a loading indicator and when it is done show the data?
//
// SearchPrinterViewController.m
// PHP POS
//
// Created by Chris Muench on 3/12/14.
// Copyright (c) 2014 PHP Point Of Sale. All rights reserved.
//
#import "PrintingViewController.h"
#import "StarIO/SMPort.h"
#import "PrinterFunctions.h"
#interface PrintingViewController ()
#end
#implementation PrintingViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
uitableview_printerList.dataSource = self;
uitableview_printerList.delegate = self;
//Expensive operation. Could take up to 3-5 seconds
printerArray = [SMPort searchPrinter];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return printerArray.count + 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (indexPath.row < printerArray.count)
{
PortInfo *port = [printerArray objectAtIndex:indexPath.row];
cell.textLabel.text = port.modelName;
NSString *detailText = [NSString stringWithFormat:#"%#(%#)", port.portName, port.macAddress];
cell.detailTextLabel.text = detailText;
}
else if (indexPath.row == printerArray.count)
{
cell.textLabel.text = #"Back";
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row < printerArray.count)
{
PortInfo *portInfo = [printerArray objectAtIndex:indexPath.row];
[PrinterFunctions PrintPHPPOSDocumentWithPortName:portInfo.portName textToPrint:self.textToPrint portSettings:#""];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
// This sends the fetching operation on the background
// You can put a loading indicator HERE BEFORE the dispatch.
// This sends the fetching operation on the background
[SVProgressHUD showWithStatus:#"Finding Printers" maskType:SVProgressHUDMaskTypeBlack];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
printerArray = [SMPort searchPrinter];
// Here you send tell the main thread to reload the table
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
[uitableview_printerList reloadData];
});
});
There are of course tons of other solutions, but this seems to me the minimal one for your case.
EDIT:
One thing I forgot: in order to make the printerArray writeable in the block, you have to add the keyword __block in front when you declare it.
EDIT: I put the final working code. I didn't need to add __block for some reaosn.
You can use a second NSOperationQueue or GCD (Grand Central Dispatch). The GCD approach is described above. Here is the NSOperation queue approach:
NSOperationQueue *printQueue = [[NSOperationQueue alloc] init];
[printQueue addOperationWithBlock:^{
// Background work
NSArray * array = [SMPort searchPrinter];
// Update the UI (in the main queue)
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
printerArray = [array copy];
[uitableview_printerList reloadData];
}];
}];
Take a look at this video:
https://developer.apple.com/videos/wwdc/2012/index.php?id=211
and this tutorial:
http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues

Memory management in viewDidUnload - should I nil both array and objects inside?

I have a tableview to load news from internet. I and trying to nil all properties in viewDidUnload.
- (void)viewDidUnload
{
self.newsArray = nil;
self.newsTableView = nil;
self.indicatorView = nil;
// self.iconDownLoader = nil;
self.downloadArray = nil;
[super viewDidUnload];
}
Every time the app crash in viewDidUnload. If I comment self.iconDownLoader = nil;, it will be fine. So can any one tell me why does this happen? Thanks you.
---------------------NewsViewController.m--------------------------
//
// NewsViewController.m
//
// Created by on 18/01/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "NewsViewController.h"
#import "ASIHTTPRequest.h"
#import "SBJson.h"
#import "NewsModel.h"
#import "NewsDetailViewController.h"
#define kCustomRowCount 6
#define IconPlaceHolder #"Spinner"
#implementation NewsViewController
#synthesize appDelegate, newsTableViewCell, newsTableView, indicatorView;
#synthesize iconDownLoader, newsArray, downloadArray;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// setup appDelegate
self.appDelegate = (SydneyAppDelegate *)[[UIApplication sharedApplication] delegate];
// initial arrays
self.newsArray = [[NSMutableArray alloc] init];
self.downloadArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
if(self.appDelegate.reachable) {
[self getNews];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No Connection" message:#"No Internet connection. Please try again later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
- (void)viewDidUnload
{
self.newsArray = nil;
self.newsTableView = nil;
self.indicatorView = nil;
// self.iconDownLoader = nil;
self.downloadArray = nil;
[super viewDidUnload];
}
#pragma mark - ASIHTTPRequest
- (void) getNews
{
NSURL *url = [NSURL URLWithString:#"http://ferrarimaseratisydney.com/api/getPublicNews.html"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
- (void) requestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString];
NSArray *json = [responseString JSONValue];
for (id aNewsInJson in json)
{
NewsModel *aNews = [[NewsModel alloc] initWithJson:aNewsInJson];
[self.newsArray addObject:aNews];
}
[self.indicatorView removeFromSuperview];
[self.newsTableView reloadData];
}
- (void) requestFailed:(ASIHTTPRequest *)request
{
NSError *error;
error = [request error];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Navigation logic may go here. Create and push another view controller.
NewsDetailViewController *newsDetailViewController = [[NewsDetailViewController alloc] init];
// transform news array
newsDetailViewController.news = [self.newsArray objectAtIndex:indexPath.row];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:newsDetailViewController animated:YES];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.newsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NewsCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NewsTableViewCell" owner:self options:nil];
cell = self.newsTableViewCell;
self.newsTableViewCell = nil;
}
// read from newsModel
NewsModel *news = [self.newsArray objectAtIndex:indexPath.row];
UILabel *label;
label = (UILabel *)[cell viewWithTag:10];
label.text = [NSString stringWithString:news.title];
label = nil;
label = (UILabel *)[cell viewWithTag:11];
label.text = [NSString stringWithString:news.description];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
imageView.image = news.image;
if (news.image == nil)
{
imageView.image = [UIImage imageNamed:IconPlaceHolder];
self.iconDownLoader = [[IconDownLoader alloc] init];
self.iconDownLoader.url = news.imageUrl;
self.iconDownLoader.delegate = self;
self.iconDownLoader.indexPath = indexPath;
if (self.appDelegate.ip4 == YES)
{
self.iconDownLoader.width = 300;
self.iconDownLoader.height = 150;
}
else
{
self.iconDownLoader.width = 150;
self.iconDownLoader.height = 75;
}
[self.downloadArray addObject:self.iconDownLoader];
[self.iconDownLoader start];
}
return cell;
}
#pragma mark - IconDownLoaderDelegate
- (void)iconDownLoadFinsh:(NSData *)imageData row:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.newsTableView cellForRowAtIndexPath:indexPath];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
if (imageData != 0)
{
imageView.image = [UIImage imageWithData:imageData];
}
else
{
imageView.image = [UIImage imageNamed:#"icon57"];
}
NewsModel *newsModel = [self.newsArray objectAtIndex:indexPath.row];
newsModel.image = [UIImage imageWithData:imageData];
}
#end
-----------------------IconDownLoader.m-------------------
//
// IconDownLoader.m
//
// Created by on 24/11/11.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import "IconDownLoader.h"
#import "ASIHTTPRequest.h"
#implementation IconDownLoader
#synthesize delegate = _delegate;
#synthesize url = _url;
#synthesize indexPath = _indexPath;
#synthesize width = _width;
#synthesize height = _height;
#synthesize request = _request;
- (void)start {
NSString *originalString = #"width=%s&height=%s";
NSString *newString = [NSString stringWithFormat:#"width=%d&height=%d&type=jpg", self.width, self.height];
NSString *resizedURL = [self.url stringByReplacingOccurrencesOfString:originalString withString:newString];
NSURL *url = [NSURL URLWithString:[resizedURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
_request = [ASIHTTPRequest requestWithURL:url];
if (_indexPath) {
_request.userInfo = [NSDictionary dictionaryWithObject:_indexPath forKey:#"indexPath"];
}
[_request setDelegate:self];
[_request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request {
NSInteger statusCode = request.responseStatusCode;
switch (statusCode) {
case 401: // Not Authorized: either you need to provide authentication credentials, or the credentials provided aren't valid.
break;
case 200: {
NSData *responseData = [request responseData];
if (!responseData) {
UIAlertView *alertView;
alertView = [[UIAlertView alloc] initWithTitle:#"Oops" message:[NSString stringWithFormat:#"Download failed in row %d", _indexPath.row] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
return;
}
[_delegate iconDownLoadFinsh:responseData row:[request.userInfo objectForKey:#"indexPath"]];
}
break;
default:{
}
}
}
- (void)dealloc {
if (_request != nil) {
[_request clearDelegatesAndCancel];
}
}
#end
Generally in viewDidUnload you should only release and zero out all references to the nib objects you own.
That said, you can destroy model objects in viewDidUnload too if they consume a lot of memory. You should remember that viewDidUnload is a counterpart to viewDidLoad, so a good rule of thumb is to destroy only those objects in viewDidUnload which you create in viewDidLoad. You should also remember that viewDidUnload is not called when the view controller is released – only when its view is.
In your case I would not release newsArray and downloadArray just because you create them in init.... I would just send them removeAllObjects instead.
As for the crash, you create a new shared icon downloader every time a cell needs an image, which is a bit awkward. If you do need a shared downloader instance, you should not recreate it for each and every cell. It looks like you want to keep all the downloaders you created in an ivar array instead because each downloader is one-shot and is responsible for loading a single image.
Not enough information here to tell, but probably you have released iconDownloader directly in some other part of the code we cannot see without setting the reference to nil at that time.
Then in viewDidUnload you are trying to release an invalid reference.
For #synthesize, use:
#synthesize iconDownLoader = _iconDownloader;
then fix all of the compiler warnings to use self.icondownloder instead of iconDownloader, and eliminate all uses of "release" (assuming your property is marked as retain which it should be).
In fact, perhaps your whole problem is that the property is not a retain property so you make the iconDOwnloader and it's promptly released.

GCD and KVO problems

My app is want to get the albums list of the iphone and all the photos in certain album.
In the app I enumerate the photos in one album of the iphone.
As there may be lots of photos of certain album, considering of performance I use GCD:dispatch_async. But it always crashes when the tableview cell updates which is invoked by KVO.
I've no idea about whether I use KVO or GCD in a wrong way.
Now alternatively I use performSelectorInBackground: replacing of dispatch_async. Now the app is not crashed but the app's performance is poor: the title of the cell will only be shown when you touch on it or scroll the tableview when there are many photos. In other words, the main thread must be blocked.
Attached is the code and the core code is in AlbumListViewController.m.
Can any one help me to check it ?
I just want to know:
1 why the app is crashed if using dispatch_async
2 how can I improve the performance in case of many photos.
thanks.
Below is my Code:
//
// 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
I ran into exactly the same problem today. In short, the reason is you cannot do UIKit related tasks, like updating a table, or in my case a Textview, from the background dispatch queue. Check the link below for more details.
comparison GCD vs. performSelectorInBackground: dispatch_async not in background
A possible solution is the following: instead of assigning your fresh data in your update block directly to the KVO variable which causes the crash, you dispatch another block which does this to the main queue, from inside your update block. If you use the dispatch_async_f function to do this, you can pass a pointer to your data as context.
Like this:
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.
});
});
For me this works without retaining and releasing the data. Not sure, if this correct.

Resources