AFNetworking setImageWithURLRequest reverting to placeholder when scrolled past - iphone

I am making a simple reddit app for a school project. I am loading my
data from reddit via json (http://www.reddit.com/.json for example) with AFNetworking library.
I am displaying each reddit thread with a prototype cell, which
contains a UIImageView for the post thumbnails.
I am trying to use AFNetworking to lazy load the images, with the
setImageWithURLRequest method.
the problem: when the app launches all the thumbnails load lazily as they should as I scroll down the tableView. As soon as the cell is out of the view and I scroll back up to it, the thumbnail has been replaced with the placeholder image -- even though it loaded the correct thumbnail before scrolling past.
Relevant code from cellForRowAtIndexPath method. the lazy loading is being called in the setImageWithURLRequest block
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"threadCell";
SubredditThreadCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *tempDictionary = [self.subredditArrayFromAFNetworking objectAtIndex:indexPath.row];
NSDictionary *singleThreadDict = [tempDictionary objectForKey:#"data"];
if ( [[singleThreadDict objectForKey:#"thumbnail"] isEqualToString:#"nsfw"] ){
cell.postImageThumbnail.image = [UIImage imageNamed:#"NSFWIcon"];
}
else if ([[singleThreadDict objectForKey:#"thumbnail"] isEqualToString:#"self"]){
cell.postImageThumbnail.image = [UIImage imageNamed:#"selfIcon"];
}
else if ([[singleThreadDict objectForKey:#"thumbnail"] length] == 0){
cell.postImageThumbnail.image = [UIImage imageNamed:#"genericIcon"];
}
else{
NSURL *thumbURL = [NSURL URLWithString:[singleThreadDict objectForKey:#"thumbnail"] ];
[cell.postImageThumbnail setImageWithURLRequest:[NSURLRequest requestWithURL:thumbURL]
placeholderImage:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
{
if (request) {
[UIView transitionWithView:cell.postImageThumbnail
duration:1.0f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[cell.postImageThumbnail setImage:image];
}
completion:NULL];
}
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error)
{
NSLog(#"failure loading thumbnail");
}
];
}
return cell;
}

Don't directly update the cell's postImageThumbnail when the image is done downloading.
Instead, tell the UITableView to refresh just that cell, using – reloadRowsAtIndexPaths:withRowAnimation:.
To get better performance, UITableView will re-use a cell-object once it's been scrolled offscreen, to show different data that is currently visible. (This is why dequeueReusableCellWithIdentifier: starts with "dequeueReusable", instead of "makeNew".) This lets UITableView only create about as many cells as are visible, instead of having to create and destroy as many cells as there are rows in the table. By the time your networking request succeeds, the cell that the success: block captures is being used to display another row, and you're over-writing that row's image.

Related

add additional cells to the bottom ofUICollectionView

Im using UICollectionView to get a set of images from instagram however i want to load past images when i get to the bottom of the page. I do however recieve the past images because they are logged into the console but dont appear on the screen.
Here is my code to retrieve the images:
- (void)nextInstagramPage:(NSIndexPath *)indexPath{
NSDictionary *page = self.timelineResponse[#"pagination"];
NSString *nextPage = page[#"next_url"];
[[InstagramClient sharedClient] getPath:[NSString stringWithFormat:#"%#",nextPage] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.timelineResponse = responseObject;
[self.collectionView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
}
Here is my code to detect when the user gets to the bottom of the screen:
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath{
[self nextInstagramPage:indexPath];
}
Edit: I have found out that the collectionview is a subclass of uiscrollview so how would i correctly implement a method which has an indexpath to detect the bottom of the scrollview!
With in your code i am making 2 assumptions, that you have an array of data to run parallel with your ui collection view and the delegate/datasource methods. you would notice at least these two methods in your code.
– collectionView:numberOfItemsInSection:
– collectionView:cellForItemAtIndexPath:
the first one tells you how many items/cells/squares you see in your ui collection view, the second makes you create what the item/cell/square will look like.
In the first method you told the uicolelction view how many there were, with in the cellforitem you should test "is this the last image" if it is the last image then download more images, if it is not the last image then continue as if nothing happened
Edit:
[[self entries] count]
you had that, now with in cell for row at index path do
if ( indexpath.item == ([[self entries] count] - 1) ) {
// download more data and add it to the 'entries' array
}

UICollectionView Cell Image changing as it comes into view with GCD

I need to resize a large locally stored image (contained in self.optionArray) and then show it in the collectionView. If I just show it, iOS trying to resize the images as I scroll quickly causing memory-related crashes.
In the code below, the collectionView will scroll smoothly, but sometimes if I scroll extremely fast, there will be an incorrect image that shows and then changes to the correct one as the scrolling decelerates. Why isn't setting the cell.cellImage.image to nil fixing this?
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CustomTabBarCell" forIndexPath:indexPath];
cell.cellImage.image = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
cell.cellImage.image = nil;
UIImage *test = [self.optionArray objectAtIndex:indexPath.row];
UIImage *localImage2 = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)];
dispatch_sync(dispatch_get_main_queue(), ^{
cell.cellImage.image = localImage2
cell.cellTextLabel.text = #"";
[cell setNeedsLayout];
});
});
}
return cell;
}
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
EDIT:
I added another async to cache first and nil and initialized the cell.image. I'm having the same issue on the initial fast scroll down. However, on the scroll back up, it's flawless now.
I added this:
-(void)createDictionary
{
for (UIImage *test in self.optionArray) {
UIImage *shownImage = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)];
[localImageDict setObject:shownImage forKey:[NSNumber numberWithInt:[self.optionArray indexOfObject:test]]];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (!localImageDict) {
localImageDict = [[NSMutableDictionary alloc]initWithCapacity:self.optionArray.count];
}
else {
[localImageDict removeAllObjects];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
[self createDictionary];
});
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CustomTabBarCell" forIndexPath:indexPath];
cell.cellImage.image = nil;
cell.cellImage.image = [[UIImage alloc]init];
if ([localImageDict objectForKey:[NSNumber numberWithInt:indexPath.row]]) {
cell.cellImage.image = [localImageDict objectForKey:[NSNumber numberWithInt:indexPath.row]];
cell.cellTextLabel.text = #"";
}
else {
cell.cellImage.image = nil;
cell.cellImage.image = [[UIImage alloc]init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *test = [self.optionArray objectAtIndex:indexPath.row];
UIImage *shownImage = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)];
[localImageDict setObject:shownImage forKey:[NSNumber numberWithInt:indexPath.row]];
dispatch_sync(dispatch_get_main_queue(), ^{
cell.cellImage.image = shownImage;
cell.cellTextLabel.text = #"";
[cell setNeedsLayout];
});
});
}
}
return cell;
Taking a closer look at your code sample, I can see the source of your memory problem. The most significant issue that jumps out is that you appear to be holding all of your images in an array. That takes an extraordinary amount of memory (and I infer from your need to resize the images that they must be large).
To reduce your app's footprint, you should not maintain an array of UIImage objects. Instead, just maintain an array of URLs or paths to your images and then only create the UIImage objects on the fly as they're needed by the UI (a process that is called lazy-loading). And once the image leaves the screen, you can release it (the UICollectionView, like the UITableView does a lot of this cleanup work for you as long as you don't maintain strong references to the images).
An app should generally only be maintaining UIImage objects for the images currently visible. You might cache these resized images (using NSCache, for example) for performance reasons, but caches will then be purged automatically when you run low in memory.
The good thing is that you're obviously already well versed in asynchronous processing. Anyway, the implementation might look like so:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CustomTabBarCell" forIndexPath:indexPath];
NSString *filename = [self.filenameArray objectAtIndex:indexPath.row]; // I always use indexPath.item, but if row works, that's great
UIImage *image = [self.thumbnailCache objectForKey:filename]; // you can key this on whatever you want, but the filename works
cell.cellImage.image = image; // this will load cached image if found, or `nil` it if not found
if (image == nil) // we only need to retrieve image if not found in our cache
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *test = [UIImage imageWithContentsOfFile:filename]; // load the image here, now that we know we need it
if (!test)
{
NSLog(#"%s: unable to load image", __FUNCTION__);
return;
}
UIImage *localImage2 = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)];
if (!localImage2)
{
NSLog(#"%s: unable to convert image", __FUNCTION__);
return;
}
[self.thumbnailCache setObject:localImage2 forKey:filename]; // save the image to the cache
dispatch_async(dispatch_get_main_queue(), ^{ // async is fine; no need to keep this background operation alive, waiting for the main queue to respond
// see if the cell for this indexPath is still onscreen; probably is, but just in case
CustomTabBarCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
if (updateCell)
{
updateCell.cellImage.image = localImage2
updateCell.cellTextLabel.text = #"";
[updateCell setNeedsLayout];
}
});
});
}
return cell;
}
This assumes that you define a class property of thumbnailCache which is a strong reference to a NSCache that you'll initialize in viewDidLoad, or wherever. Caching is a way to get the best of both worlds, load images in memory for optimal performance, but it will be released when you experience memory pressure.
Clearly, I'm blithely assuming "oh, just replace your array of images with an array of image filenames", and I know you'll probably have to go into a bunch of different portions of your code to make that work, but this is undoubtedly the source of your memory consumption. Clearly, you always could have other memory issues (retain cycles and the like), but there's nothing like that here in the snippet you posted.
I had a similar problem but went about it a different way.
I also had the issue of "pop-in" as images that were loaded async were flashed as they come in until finally the correct one was shown.
One reason this is happening is that the current indexpath for the cell that was initially dequeued did't match the index of the image you are putting into it.
Basically, if you scroll quickly from 0-19 and the cell you want to update is #20 and you want it to show image #20, but it's still loading images 3, 7, 14 asynchronously.
To prevent this, what I did was track two indices; #1) the most recent indexpath that reflects the actual position of the cell and #2) the index corresponding to the image that is actually being loaded async (in this case should actually be the indexpath you are passing into cellforitematindexpath, it gets retained as the async process works through the queue so will actually be "old" data for some of the image loading) .
One way to get the most recent indexpath may be to create a simple method that just returns an NSInteger for the current location of the cell. Store this as currentIndex.
I then put a couple if statements that checked that the two were equal before actually filling in the image.
so if (currentIndex == imageIndex) then load image.
if you put an NSLog(#"CURRENT...%d...IMAGE...%d", currentIndex, imageIndex) before those if statements you can see pretty clearly when the two do not match up and the async calls should exit.
Hope this helps.
I found the wording of what chuey101 said, confusing. I figured out a way and then realized that chuey101 meant the same.
If it is going to help anyone, images are flashed and changed because of the different threads that are running. So, when you spawn the thread for image operations, its going to get spawned for a specific cell no, say c1. But, at last when you actually load your image into the cell, its going to be the current cell that you are looking at, the one that you scrolled to - say c2. So, when you scrolled to c2, there were c2 threads that were spawned, one fore each cell, as you scrolled. From what I understand, all these threads are going to try loading their images into the current cell, c2. So, you have flashes of images.
To avoid this, you need to actually check that you are loading the image that you want into the cell that you mean to load to. So, get the collectionviewcell indexpath.row before loading image into it (loading_image_into_cell). Also, get the cell for which you spawned off your thread to before you spawn off the thread i.e. in the main thread (image_num_to_load). Now, before loading, check that these two numbers are equal.
Problem solved :)

iPhone : friend list, how did Facebook accomplished that?

I wonder if anyone can speculate or better yet provide a piece of code as for the implementation of the lengthy friends list in the Facebook iPhone app.
when you open the app and go strait to the friends list, you get the list almost in an instant, at least for me with ~500 friends.
when I try it in my own app it takes lots of precious seconds to populate the table view with the same data, so how does Facebook accomplished such a quick response time ?
upon looking at the tableview in the facebook app you notice there is no scroll bar usually found in such tableview, could that be one sign of the neat trick facebook is utilizing to achieve this rapid rows insert ? could it be they implemented some sort of a virtual tableview with only holds a few dozen rows but rotates them ?
any thoughts ?
the UITableView will let you do this. There are a number of examples on the internet with UITableView and Custom Cell's
Essentially, you load your images in the background, and you reuse the Cells that are in the tableview
EDIT Added example code to demonstrate how this is accomplished.
IMPORTANT NOTE
This code was not tested and may or may not actually function as is.
It was pasted with some editing for length. I did a lot more then this in my app, but in the interest of keeping with the example requested I omitted a lot.
On with the example:
Here is where I get the cell, load it with the items that are readily available. And send it to the background thread to load the rest.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"OfferCell";
static NSString *CellNib = #"OfferItem";
OfferCell* cell = (OfferCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
cell = (OfferCell*)[nib objectAtIndex:0];
}
NSDictionary* couponPackage = [self.jsonOfferData valueForKey:#"result"];
NSArray *couponList = [couponPackage valueForKey:#"offers"];
if ([couponList count] >= indexPath.row )
{
NSDictionary* couponData = [couponList objectAtIndex:indexPath.row];
Coupon *coupon = [[Coupon alloc] initWithDictionary:couponData];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:cell,#"cell",coupon,#"coupon", nil];
//Right here you would try to load any cached imaged from disk.
//Then send a Thread to the background to load the image.
[self performSelectorInBackground:#selector(loadTableViewCellData:) withObject:params];
//Load up the rest of the custom info into the custom cell.
[cell.captionLabel setText:coupon.name];
[cell.subTextLabel setText:coupon.subText];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
[cell setCommand:coupon.command];
[cell setParameter:coupon.commandArgs];
[cell setImageURL:coupon.imageURL];
[cell setImageAltURL:coupon.imageAltURL];
[cell setRegistrationCode:coupon.registrationCode];
[coupon release];
}
return cell;
}
as you can see, i call a background thread before i even load the custom content in the cell.
- (void) loadTableViewCellData:(NSDictionary*) objectData
{
OfferCell *cell = [objectData objectForKey:#"cell"];
Coupon *coupon = [objectData objectForKey:#"coupon"];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[coupon iconURL]]]];
[objectData setValue:image forKey:#"image"];
self performSelectorOnMainThread:#selector(setImageOnMainThread:) withObject:objectData
}
after downloading the image, i send a Main thread request to update the Image that is in the cell object.
- (void) setImageOnMainThread:(NSDictionary*) objectData
{
OfferCell *cell = [objectData objectForKey:#"cell"];
Coupon *coupon = [objectData objectForKey:#"coupon"];
UIImage *image = [objectData objectForKey:#"image"];
cell.icon.image = image;
}
##AGAIN This May not Actually Function. ##
I did not copy all of my code for this. this is a hammer out so you can get the idea.
play with the code and test it. but the fundamentals are.
Dequeue the cell that will fit your needs (Reuse Identifier)
Use the cell if it can be dequeue'd or create a new one with a reuse identifier (my example uses a xib file named OfferItem.xib)
Send a thread to the background that will load the image data from disk or url (a combination of both is recommended)
Send a thread back to the UI when you are ready to load the image into the View (Updating the UI must be done on the main thread)
if you can do that, then your friends list (or in this case offers) will be loaded up as fast as possible. and the Images will pop on the screen as soon as they download.
Also if you use a Caching technique it will be faster for subsequent loads because in the the first method {tableView:cellForRowAtIndexPath:} you would load up the cached image immediately.
Aside from that, this should load your cell's pretty fast.
They obviously load the data from a local resource (plist, ManagedObject, ...)
Have a look at some sample code to draw a TableView:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *kCellIdentifier = #"MyCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kCellIdentifier] autorelease];
}
return cell;
}
The dequeueReusableCellWithIdentifier: thing is one reason why TableViews in iOS can draw quickly. It works somehow like this:
1)You provide an identifier for a cell you're creating.
2)Cells that are visible at first get alloced (with identifier)
3)When a Cell is moved off the screen it gets put on a pile MyCellIdentifier
4)Whenever the system needs to draw a cell of identifier:MyCellIdentifier it first looks whether there are any cells currently unused on the MyCellIdentifier pile. If that's the case it picks one off the pile and thus doesn't have to alloc a new one. That way expensive allocing can be kept at a minimum.
I hope this answers your question :)

How to improve performance while using UIWebView in cells of table view?

I am creating an application which uses a web service.And retrieves a list of users and there details like images, user id and there names.I displayed all the information related to users in table view, Thus each of the cell in the table view has an image with the for tables in it. I am using a Custom cell class to create the individual cells. selecting any row in table presents a detail view (using navigation) which show a larger image and all the details related to the particular user i selected.
in customcell and detail view class i am using web view to display image.
But when i run the app the images gets a bit of delay to display and same happens with the detail view.
Is there any alternative so that i can improve the performance of the table view so that i can have smooth scrolling of table view with out any delay in image loading and detail view??
here is the code...
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CustomCellIdentifier = #"CustomCellIdentifier ";
CustomCell *cell = (CustomCell *)[tableView
dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil ];
//cell = [[CustomCell alloc] initwi ];
for (id oneObject in nib)
if ([oneObject isKindOfClass:[CustomCell class]])
cell = (CustomCell *)oneObject;
}
NSUInteger row = [indexPath row];
NSString *imageName = [imgArray objectAtIndex:row];
NSString *completeImageUrl = [[NSString alloc] initWithFormat:#"http://122.160.153.166:201/%#", imageName];
NSURL *url = [NSURL URLWithString:completeImageUrl];
//NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
NSData *imageData = [[NSData alloc] initWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData]; /// check to see if we are getting all the arrays such as image array and userId array and name array of same size..
if(image == nil)
{
}
cell.imgView.image = image; /// other wise an execption of out out array range will be shown
[image release];
[imageName release];
//[cell.webView initWithFrame:CGRectZero];
//[cell.webView loadRequest:requestObj];
//cell.webView.frame = CGRectMake(20, 0, 80.0, 64);
cell.userIdLabel.text = (NSString *)[userId objectAtIndex:row];
cell.nameLabel.text = (NSString *)[userName objectAtIndex:row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}`
i think problem can be in imgArray array as i am setting it from other class. Where i request the web service and fetched all the data about users .
Any help is greatly appreciated . Thanks in advance
I got the same problem. For that I used EGOImageCache and some thing.
Find this Url for EGOImage
Download files those names starts with "EGO", and add those to your project.
and write the following code where ever you want to put the image:
EGOImageView *imgView = [[EGOImageView alloc] initWithFrame:CGRectMake(50,50,220,360)];
NSString *imageName = [imgArray objectAtIndex:row];
NSString *completeImageUrl = [[NSString alloc] initWithFormat:#"http://122.160.153.166:201/%#", imageName];
NSURL *url = [NSURL URLWithString:completeImageUrl];
imgView.imageUrl = url;
[self.view addSubView:imgView];
Thats it. Use the similar code, where ever you want the image.
At the first time while loading it will take some time to download the image. But later it will cache the image and use it the next time.
Is there any alternative so that i can improve the performance of the table view so that i can have smooth scrolling of table view with out any delay in image loading
For this the answer is Lazy Loading of images in cells
You can implement lazy loading on webviewDidLoad which shows that webview has loaded completely
Using a UIWebView to display a single image is overkill. You should be using NSURLConnection (or one of many alternative HTTP wrappers/libraries) to load the image data and UIImageView to display it in each of your table cells. In my experience, there is no way (or at least no straightforward way) to eliminate the rendering delay when using UIWebView.
Why are you using the webview for displaying the images. Imageview should be used instead. If you are getting the images from the server then you should get those in separate thread and after you receive the images you should reload that particular row.
Some code snippet is like this:
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
...
...
UIImage *img = (UIImage *)[imagesArray objectAtIndex:indexPath.row];
if(img == nil){
[imgView setImage:temporaryImage];
[NSThread detachNewThreadSelector:#selector(getTheThumnbails:) toTarget:self withObject:indexPath];
}
else
[imgView setImage:img];
for further assistance look at this
Hope this helps.
Thanks,
Madhup
okay finally i removed the uiweb view from my app and used Imaged view and succeed.
i used the link that was given by the developer of the tweetie . here it is link text
i used the ABTableViewCell class and created the whole cell using the code.
The scrolling was very jerky when i executed the app. after applying almost 4 hours and with the help of lazy loading i was able to run my app smoothly. I stored the data which is retrieved from url into an array then applied the concept in tableView:cellForRowAtIndexPath:
method..
Thanks you guys for helping me ...
Three20 has a rich-text table view cell class (TTStyledTextTableCell I think) - should render significantly faster than an embedded UIWebView. You could also roll your own, though that would take a lot longer.

release NSData in NSManagedObject

I use datamodel to store 2 objects : Video, Images.
Video contain just string attributes and Images have 2 "Binary data" attributes.
At the start the 2 binary data attributes was in the video object.
But all videos are loading during initialization of UITableView.
For 400 videos binary data represent 20 Mo, so imagine with 4000 videos...
Now with 2 objects the UITableView loading work well.
I load binary data when it's necessary in the method : tableView:cellForRowAtIndexPath
But now more I scroll into the list, more the memory grow up :(
look at my method :
- (UITableViewCell *)tableView:(UITableView *)myTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"videoCell";
Video *theVideo = (Video *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
VideoCellViewController *cell = (VideoCellViewController *)[myTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"VideoCellView" owner:self options:nil];
cell = editingTableViewCell;
self.editingTableViewCell = nil;
}
cell.video = theVideo;
return cell;
}
And the method setvideo in VideoCellViewController
- (void)setVideo:(Video *)newVideo {
if (newVideo != video) {
[video release];
video = [newVideo retain];
}
NSData *imageData = [video.allImages valueForKey:#"thumbnailImage"];
UIImage *uiImage = [[UIImage alloc] initWithData:imageData];
smallImage.image = uiImage;
nameLabel.text = video.displayName;
[uiImage release];
}
Even without set the smallImage, I have memory trouble.
If I load the image object, it's never release.
I try a lot of solution to release memory without succes...( didTurnIntoFault, release, CFRelease...)
In performance tool, I can see my binary data as CFData.
I use a lot iPhoneCoreDataRecipes and PhotoLocations sample.
I need help to clean my memory ;)
Thanks
Samuel
Clearly there is something going on with your table cell creation logic. Let's take a look at a typical cellForRow delegate handler first..
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
}
// do stuff with cell
return cell;
Here we see we are
trying to get a reusable cell
if that fails (nil) create a new one and pass the reusable id to the ctor
then do stuff with the cell (new or existing) and return it
If you do not key the cell for reuse in the table view, you will always get a 'nil' cell returned from the dequeue, hence the need to create new cells every time. This will cause memory to continue to grow as you scroll around, but stay fairly flat when idle.
EDIT:
Assuming your cell is fine, then you need to narrow down if it's the video data or the image data that is leaking. What is smallImage? And are you sure you do not want to do everything only when the video is new?
- (void)setVideo:(Video *)newVideo {
if (newVideo != video) {
[video release];
video = [newVideo retain];
NSData *imageData = [video.allImages valueForKey:#"thumbnailImage"];
UIImage *uiImage = [[UIImage alloc] initWithData:imageData];
smallImage.image = uiImage;
nameLabel.text = video.displayName;
[uiImage release];
}
}