If I testing my codes with performance tool - leaks, and it doesn't detect any leaks. Does that mean the codes is not leaking any memory?
I have a Jail-broken iPhone, which I can monitor the available memory. If anyone knows, it's SBSettings. I tested my app which has a UITableView and I can see the available memory dropping when I am scrolling through the tableView. From 300MB to 30MB, where it seems like it can't drop further. It usually doesn't drop that much with other apps other than games. I am using a custom UITableViewCell with 2 buttons, 1 textView and 3 UILabels.
So, yeah. If performance tool does not detect any leak, am I safe?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"StatusTableCell";
StatusTableCell *cell = (StatusTableCell *)
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle]
loadNibNamed:#"StatusTableCell"
owner:nil options:nil];
for (id currentObjects in topLevelObjects){
if ([currentObjects isKindOfClass:[StatusTableCell class]]){
cell = (StatusTableCell *) currentObjects;
break;
}
}
[cell.cancelButton addTarget:self action:#selector(cancelButton:) forControlEvents:UIControlEventTouchUpInside];
}
/// some other stuff
return cell;
}
No, you're not necessarily safe.
A memory leak occurs when the program no longer has a reference to an object. So if an object is released, but an object it was retaining is not (not released properly in the dealloc method, for example), you get a leak.
However, if the owning object is never released itself, no leak is detected.
To look for these kinds of memory problems, run the allocations instruments tool. Click on the Mark Heap button, and perform some kind of repeatable action in the app (for example, select a row in a table view to push a detail view on to the nav stack, then tap the back button). Click on the Mark Heap button again. Then repeat the action a few times. Ideally you should see no heap growth, and no persistent objects between heap shots.
You should consider value of LiveBytes in performance tool if it is increasing with app running, it is an issue. This might happen with tableviews if you are not using reusable cells. Check for it if you have reusable cells or not.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"reusablecell"];
if(!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"reusablecell"];
[cell autorelease];
}
//update cell here
return cell;
}
Related
I have a class that defines a custom contact cell called ContactItemCell and a xib file that lays out that cell. When I create the table view it opens up and six of these ContactItemCell classes are created. When I navigate back they aren't deallocated, and when I open the tableview again another 6 are created. Here's the tableview code:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Contact* contact = [self contactAtIndexPath:indexPath];
static NSString *cellID = #"ContactItemWithTagsForBothCell";
ContactItemCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil)
{
NSArray *arr = [[NSBundle mainBundle] loadNibNamed:#"ContactItemWithTagsForBothCell" owner:nil options:nil];
if (arr.count <= 0)
{
NSLog(#"couldnt find cell with ID: %#", cellID);
return nil;
}
cell = [arr firstObject];
}
[cell configureCellForContact:contact];
return cell;
}
The fileowner in the xib file is just set to be blank which I'm assuming means NSObject. I've tried looking for a strong reference cycle to see if the class is kept alive by pointers but I haven't seen anything after days of investigation. I'm really at my limit and I'm not sure what else I can do, I've been using instruments too and that's how I've figured out that they're being created 6 at a time but I can't find out what's pointing to them. Why is this happening? Am I doing something wrong with the table view? If I'm not and you think it's a strong reference cycle then how can I find every object that points to this ContactItemCell? Thanks in advance!
If anyone was curious I used the memory debugger and found that there was a strong reference cycle with a pod I was using.
I am working with tableviews in iOS. Reusable cells are reloaded when scrolling.
So, when updating -for example- textfields inside a cell, it disappears once scrolling over. I solved it by using an Array that saves all texts in all cells, but I wonder if there's a better way to solve this issue.
Thanks.
Using String array you have to store data of all textfields in tableview.
Use delegate methods of UITableView to implement more efficiently.
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell * mc = (MyCell *) cell;
names[indexPath.row] = mc.myTf.text;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell * mc = (MyCell *) cell;
mc.myTf.text = names[indexPath.row];
}
Here, MyCell is the custom cell which has UITextField. name[] is the NSString array declared at class scope like NSString * names[20].
What you are describing is how tableviews are meant to work! The tableView is a display, not a store, and the reusable cells are only those currently displayed
You are right to use an array (or collection) to hold the data and just use the tableView to display it
for memory management (to releasing memory) tableview remove cell memory which are not displaying in current screen its only keep those cell in memory which are currently displaying on screen so you have to store that text separate from tableview.
You issue can be because of multithreading. If you are reloading data from a GCD operation or any NSOperation(different thread), then you have to use the below code to get the handle to main thread
dispatch_sync(dispatch_get_main_queue(), ^{
//perform the reload activity
});
Yes you are doing right, as cells are reused you need to save your data, something like this -
1 . Have an array to hold your data.
2 . Update your array whenever you make any change in your textFields, so that your UI and data are in sync.
Use this array to populate your tableView.
You need to define the cellIdentifier as unique. Then each cell created with unique identifier.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell%dR%d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
//your logic here
}
return cell;
}
I am using the same code in two of my view controllers (they are implementing the same class what changes is the url they download) and in one occassion the image is displayed correclty while in the other I do see an empty cell.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier=#"MyCell";
//this is the identifier of the custom cell
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
tableView.backgroundColor=[UIColor clearColor];
tableView.opaque=NO;
tableView.backgroundView=nil;
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSLog(#"Image url is:%#",[images_url objectAtIndex:indexPath.row]);
NSURL *url_image=[NSURL URLWithString:[images_url objectAtIndex:indexPath.row]];
cell.myimage.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:url_image]];
return cell;
}
As i told you I have 2 view controllers implementing the same class. In the view did load the url is set depending on the value of a flag. If I open controller A, I see no image, if I open view B i can see the image. Both of the urls are correct as I can check it with the NSLog I have inserted.
What might be the problem?
Unfortunately calling "NSData dataWithContentsOfURL" is a blocking call. Execution of your program will stop until iOS is able to fetch all the data from the server or fails trying. This may often be "fast" if you're on LTE or WiFi; but can potentially take a LONG time.
Meanwhile, you're on the "main thread" in your app - so your app will appear to freeze-up, and the system's watchdog timer may kill your app. If anyone besides you will use this ap, you absolutely need to populate your tableview cell's image with local data that's retrieved immediately or use asynchronous methods.
Just google for "lazy load UIImage". This SO question has some good tips on the subject:
lazy-load-images-in-uitableview
Additionally, you should move these lines to some setup code. You don't need to perform them every time to update a cell:
tableView.backgroundColor=[UIColor clearColor];
tableView.opaque=NO;
tableView.backgroundView=nil;
Best of luck!
I have use xib set the interface,when using it dosen't smooth,why?
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomerCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
NSArray * nib = [[NSBundle mainBundle] loadNibNamed:#"MSNeedCheckCell" owner:self options:nil] ;
cell = [nib objectAtIndex:0];
}
//填充cell的内容
}
Try calling the following method from your init method for the class:
[self.tableView registerNib:[UINib nibWithNibName:#"MSNeedCheckCell" bundle:nil]
forCellReuseIdentifier:#"CustomerCell"];
And then, you can remove your entire if (!cell) { section of code since it will always return a cell. This only loads the nib once instead of every time that you need a cell so will be much faster.
Is this, quite literally, all there is to it? Or are you doing anything else (e.g. in subclassed cell, or other code that you may have omitted for the sake of brevity).
The most common source of lack of smoothness (and by smoothness, I assume you're talking about a stuttering in the UI as you scroll in your tableview) would be if you're doing anything with images in the foreground queue.
The WWDC 2012 - 211 - Building Concurrent User Interfaces offers a wonderful, practical illustration of how to use Instruments to identify the sources of performance bottlenecks. Clearly they're focusing on a very specific design consideration there, but the Instruments tutorial is very useful. It would be good to make sure that this particular line of code is really the problem before you lose too much sleep over it.
If we give same Identifier to all cells, Disappearing cell uses the memory of Appearing cell. Means content will repeat when I scroll Table-view. But If we give diff Identifier then every cell will have its own memory location and shows data perfectly.
Now suppose I have 1000 or more records to load in Table-view. If I will give different Identifiers, there will be lots of allocations in memory. So Is there any solution to show data perfectly with minimum memory allocation ?
Here is how I define cell identifier:
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [NSString stringWithFormat:#"%d%d",indexPath.section,indexPath.row];
UITableViewCell *Cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (Cell == nil)
{
Cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
}
The problems that you are experiencing are caused by you improperly using cell identifiers. Cell identifier should be the same for all cells that you want to reuse. Take a look at this template, it should explain the correct approach:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = #"MY_CELL";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
// everything that is similar in all cells should be defined here
// like background colors, label colors, indentation etc.
}
// everything that is row specific should go here
// like label text, progress view progress etc.
return cell;
}
Btw. use camel case to name your variables, capitalized names are meant for class names.
you should clear the contents of dequeued cell like emptying labels and others. if you allocate separate memory for each cell you will easily go low memory. perfect memory management is still reusing cells.