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.
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've read all the relevant other questions on this topic and tried the fixes, none of which have worked. My app crashes/hangs to the extent that I have to force quit Xcode in order to restart working, when dequeueReusableCellWithIdentifier: is called.
It makes no difference if I use dequeueReusableCellWithIdentifier:, or dequeueReusableCellWithIdentifier:forIndexPath: , and I HAVE set the class with registerClass:forCellReuseIdentifier: , as you can see in the code below.
Registering the class in my ViewController:
#implementation LWSFlavourMatchesViewController
-(void)viewDidLoad
{
[super viewDidLoad];
_flavourMatchesView = [LWSFlavourMatchesView flavourMatchesViewWithDataSource:self.flavourMatchesDataSource andDelegate:self.flavourMatchesDelegate];
self.tableView = _flavourMatchesView;
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"flavourCell"];
}
And trying to dequeue cell in tableView:cellForRowAtIndexPath in my dataSource:
#implementation LWSFlavourMatchesDataSource
// other methods...
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *flavourCellIdentifier = #"flavourCell";
NSString *currentSelectedFlavour = [self.flavourWheel selectedFlavour];
UITableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:flavourCellIdentifier forIndexPath:indexPath];
if(tableViewCell == nil)
{
tableViewCell = [[UITableViewCell alloc ]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:flavourCellIdentifier];
}
[tableViewCell.textLabel setText: currentSelectedFlavour];
return tableViewCell;
// return [UITableViewCell new];
}
If I remove all other code but un-comment out return [UITableViewCell new]; then the app does not crash. What is it about my dequeuing that is causing this problem?!
I refactored your tableview delegate. You do not need to check if the cell is nil because you registered the class with [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"flavourCell"];.
I made your cellIdentifier static. But to remove the duplication on the registerClass function may you make a #define REUSE_IDENTIFIER #"flavourCell".
If this is still slow, than is the [self.flavourWheel selectedFlavour]; the cause. Check out the instruments tutorial for performance improvements: http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *flavourCellIdentifier = #"flavourCell";
NSString *currentSelectedFlavour = [self.flavourWheel selectedFlavour];
UITableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:flavourCellIdentifier forIndexPath:indexPath];
[tableViewCell.textLabel setText: currentSelectedFlavour];
return tableViewCell;
}
try removing the class registration:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"flavourCell"];
You shouldn't need to register the class if you are instantiating a generic UITableViewCell class cell from a storyboard
Another cause for this error can be-
invalid nib registered for identifier (Cell) - nib must contain exactly one top level object which must be a UICollectionReusableView instance
I had a UIView in my xib instead of a collectionViewCell. Of course, if you have multiple top level objects in the .xib, the same crash will show. Hope this helps.
Take care that if your cell is an object in a XIB, and you are using something like :
[self.tableView registerNib:[UINib nibWithNibName:#"cell_class_name" bundle:nil] forCellReuseIdentifier:#"cell_reuse_name"];
to register the cell, be sure the identifier in the attributes inspector in Interface Builder is correct, in this case #"cell_reuse_name".
If the identifier isn't the same, you may be stuck with an odd situation where creating new cells from the nib each time, i.e.
NSArray *objects = [bundle loadNibNamed:#"cell_nib_name"
owner:nil
options:nil];
cell = (UITableViewCell *)[objects safeObjectAtIndex:0];
seems to work fine, but trying to use
[tableView dequeueReusableCellWithIdentifier:#"cell_reuse_name"];
crashes.
In practice it's often easiest to use the same name for the XIB, custom class, and reuse identifier. Then if there are issues, you can just make sure they are all the same.
I had completely different issue. Mismatch between String/XIB based localization. It did help to enable/disable+remove unneeded localizations.
Try this:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"NIB_NAME" owner:self options:nil];
If it hangs, there's something wrong with your XIB (like in my case).
Try this:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"flavourCellIdentifier"]; }
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 been testing the application on the device (iOS 5) while using Instruments and I found a couple of memory leaks.
This is the part of the code I'm being redirected to from Instruments (see the arrow for exact line):
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CeldaUltimasFotosViewCell *cell =
(CeldaUltimasFotosViewCell *) [self.tableView
dequeueReusableCellWithIdentifier:#"CeldaUltimasFotosViewCell"];
if (cell == nil) {
- - - - > NSArray *topLevelObjects =
[[NSBundle mainBundle]
loadNibNamed:#"CeldaUltimasFotosViewCell"
owner:nil options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
// Configure the cell...
[[cell titulo] setFont:fuente_titulo];
...
return cell;
}
As you can see, I have a custom cell which is loaded from a NIB file. There are three files for the cell (customCell.m, customCell.h, customCell.xib). The thing is that I don't know if I have to release something in the cell controller (which is now empty, no methods), since this is iOS 5 with ARC.
check out my answer here:
How can I recycle UITableViewCell objects created from a XIB?
you don't even need to use loadNibNamed any more on iOS5
Take a look at the Table View Programming and how to load cells from NIB (XIB) files.
https://developer.apple.com/library/ios/#documentation/userexperience/conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7-SW1
The first thing weird is that you are storing the cell in a local variable. You should be wiring the custom cell up to a property in the class and all you call in your code is:
[[NSBundle mainBundle] loadNibNamed:#"CeldaUltimasFotosViewCell" owner:self options:nil];
Follow the code from Loading Custom Table-View Cells From Nib Files and you can't go wrong.
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;
}