I have a complex tableView, which's cell have many contents.
And when I reloadData my tableView, the cpu usage raise up quickly, even to 180%!
So, in real device, 5c or 5s, the app will crash, I think is because the cpu usage the app crash.
So, is there a method to limit the cpu usage or let app do not crash?
Addition-1
I don't think the tableView delegate methods is help for the question ,but I will also put part of them:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:exp_TableIdentifier];
if (cell == nil) {
cell = [[LMLAgricultureTechCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:exp_TableIdentifier];
}
((LMLAgricultureTechCell *)cell).model = self.help_dataSource[indexPath.row];
((LMLAgricultureTechCell *)cell).indexPath = indexPath;
((LMLAgricultureTechCell *)cell).delegate = self;
((LMLAgricultureTechCell *)cell).photo_view.delegate = self;
// solve SDWebImage memory increase
SDImageCache *imageCache = [SDImageCache sharedImageCache];
[imageCache clearMemory];
return cell;
}
In the LMLAgricultureTechCell.m:
- (void)setModel:(LMLAgricultureTechModel *)model{
_model = model;
_shouldOpenContentLabel = NO;
// 1.头像
_iconView.image = [UIImage imageNamed:model.lml_iconName];
[_iconView sd_setImageWithURL:[NSURL URLWithString:model.lml_iconName] placeholderImage:img_placeholder_circle_header];
[Util roundBorderView:_iconView.bounds.size.width / 2.0 border:0 color:nil view:_iconView];
// 2.名字
_nameLabel.text = model.lml_userName == nil || [model.lml_userName isEqual: #""] || [model.lml_userName isEqual:[NSNull null]] ? #"" : model.lml_userName; // 未获得用户名
// 防止单行文本Label在重新用的时候宽度计算不准的问题
[_nameLabel sizeToFit];
// 3.地址
_loacation.text = model.lml_location == nil || [model.lml_location isEqual: #""] || [model.lml_location isEqual:[NSNull null]] ? #"" : [model.lml_location componentsSeparatedByString:#"-"].lastObject; //未获得地址
// 4.时间
_time.text = model.lml_time == nil || [model.lml_time isEqual: #""] || [model.lml_time isEqual:[NSNull null]] ? #"" : model.lml_time;
// 未获取时间
[_time sizeToFit];
// 5.标题
_title.text = model.lml_title == nil || [model.lml_title isEqual: #""] || [model.lml_title isEqual:[NSNull null]] ? #"" : model.lml_title; //未获得标题
//[_title sizeToFit];
// 6.内容
_content.text = model.lml_content;
_photo_view.picPathStringsArray = model.lml_imagesArr;
// 7.如果文字高度超过60
if (model.shouldShowMoreButton) {
_allContent.sd_layout.heightIs(20);
_allContent.hidden = NO;
// 如果需要展开
if (model.isOpening) {
_content.sd_layout.maxHeightIs(MAXFLOAT);
[_allContent setTitle:NSLocalizedString(#"收起", nil) forState:UIControlStateNormal];
}else {
_content.sd_layout.maxHeightIs(maxContentLabelHeight);
[_allContent setTitle:NSLocalizedString(#"全文", nil) forState:UIControlStateNormal];
}
}else {
_allContent.sd_layout.heightIs(0);
_allContent.hidden = YES;
}
if (model.isMyPost) {
_delete.sd_layout.heightIs(20).widthIs(40);
_delete.hidden = NO;
}else {
_delete.sd_layout.heightIs(0);
_delete.hidden = YES;
}
// 8.现在判断allOrDelete back
if (_allContent.hidden == YES && _delete.hidden == YES) {
_allAndDeleteView.sd_layout.heightIs(0);
}else {
_allAndDeleteView.sd_layout.heightIs(20);
}
// 9.图片数组
CGFloat picContainerTopMargin = 0;
if (model.lml_imagesArr.count) {
picContainerTopMargin = 10;
}
_photo_view.sd_layout.topSpaceToView(_allAndDeleteView, picContainerTopMargin);
_photo_view.picPathStringsArray = model.lml_imagesArr;
_showCountlabel.text = model.lml_scanTimes;
_goodImageView.image = model.lml_isLiked ? [UIImage imageNamed:#"pre_list_thumb_selected.png"]:[UIImage imageNamed:#"pre_list_thumb"];
_goodCountlabel.text = model.lml_likeTimes;
_speakCountlabel.text = model.lml_commentTimes;
// 底部的
_bottomView = part3;
[self setupAutoHeightWithBottomView:_bottomView bottomMargin:15];
}
Related
Has anyone noticed with the changes added to UISwitch in Beta 4, that if you set a switches tag, for some reason it doesn't respect it? I add a switch programmatically to the accessory view of a TableView cell and once the cell is selected I access the switch to get it's tag to know which switch it was and the state that was changed.
Here are the code examples:
if (_showSwitch) {
if (cell.accessoryView == nil) {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
switchview = [[UISwitch alloc]initWithFrame:CGRectZero];
switchview.tag = indexPath.row;
cell.accessoryView = switchview;
[switchview addTarget:self action:#selector(switchChanged:)
forControlEvents:UIControlEventValueChanged];
cell.backgroundColor = UIColor.systemGray2Color;
}
NSArray *strArray = [_tableDataSource[indexPath.row] componentsSeparatedByString:#"/"];
_row = indexPath.row;
_cellLabel = strArray[0];
cell.textLabel.text = _cellLabel;
_cellArray = [_cellArray arrayByAddingObject:(NSString *)_cellLabel];
if (_level >= 1) {
_deviceID = strArray[1];
_deviceArray = [_deviceArray arrayByAddingObject:_deviceID];
_lowcaseDeviceID = _deviceID = _deviceID.lowercaseString;
dictLookup = _statusDict[_lowcaseDeviceID];
if (_row >= 1) {
mycmdString = [mycmdString stringByAppendingString:#","];
}
mycmdString = [mycmdString stringByAppendingString:_deviceID];
UISwitch *switchView = (UISwitch *)cell.accessoryView;
[switchView setOn:NO animated:NO];
if ([dictLookup isEqualToString:#"ON"]) {
cell.textLabel.textColor = UIColor.systemGreenColor;
[switchView setOn:YES animated:YES];
}
_callForStatus = TRUE;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryView = nil;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
- (IBAction)ControlView:(UISwitch *)sender {
ControlViewController *flController = [[ControlViewController alloc]
initWithNibName:#"ControlViewController" bundle:nil];
(flController.navigationItem).title = [NSString stringWithFormat:#"%# - %#", _deviceName, _device];
_level = _level += 1;
flController.deviceID = _device;
flController.deviceName = _deviceName;
flController.deviceState = #"OFF";
if ((sender.isOn) == YES) {
flController.deviceState = #"ON";
}
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
[self presentViewController:flController animated:YES completion:nil];
});
}
- (void)switchChanged:(UISwitch *)sender {
bool mode;
UISwitch *switchControl = sender;
if (switchControl.isOn == YES) {
[switchControl setOn:YES animated:YES];
mode = TRUE;
[General playSound:#"Button Up.mp3"];
} else {
[switchControl setOn:NO animated:NO];
mode = FALSE;
[General playSound:#"Button Down.mp3"];
}
sender.highlighted = YES;
_callForStatus = TRUE;
[self executeSwitchAtRow:switchControl.tag forMode:mode];
// if (_level >= 1) [self startStepperTimer];
}
- (void)executeSwitchAtRow:(NSInteger)row forMode:(BOOL)onoff {
NSString *mode = #"OFF";
if (onoff) {
mode = #"ON";
}
NSString *device = _deviceArray[row];
Debug_1(#"Send Switch Change for device %# %#",device,mode);
NSString *execCommand = [NSString stringWithFormat:#"device %# %#", mode, device];
[[NSNotificationCenter defaultCenter] postNotificationName:#"sendMessageFromExternal" object:self
userInfo:#{ #"cmd": execCommand }];
if (_level >= 1) [self startStepperTimer];
}
...
I have tableviews in my storyboard and it is working till Xcode 7.3, After Updating Xcode to 8, imageviews that are added in tableviewcell are not render first time until you scroll OR explicitly call reloadData. Imageviews are added from storyboard.
After scrolling
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ALContactCell *contactCell;
switch (indexPath.section)
{
case 0:
{
//Cell for group button....
contactCell = (ALContactCell *)[tableView dequeueReusableCellWithIdentifier:#"groupCell"];
//Add group button.....
UIButton *newBtn = (UIButton*)[contactCell viewWithTag:101];
[newBtn addTarget:self action:#selector(createGroup:) forControlEvents:UIControlEventTouchUpInside];
newBtn.userInteractionEnabled = YES;
}break;
case 1:
{
//Add rest of messageList
contactCell = (ALContactCell *)[tableView dequeueReusableCellWithIdentifier:#"ContactCell"];
[contactCell.mUserNameLabel setFont:[UIFont fontWithName:[ALApplozicSettings getFontFace] size:USER_NAME_LABEL_SIZE]];
[contactCell.mMessageLabel setFont:[UIFont fontWithName:[ALApplozicSettings getFontFace] size:MESSAGE_LABEL_SIZE]];
[contactCell.mTimeLabel setFont:[UIFont fontWithName:[ALApplozicSettings getFontFace] size:TIME_LABEL_SIZE]];
[contactCell.imageNameLabel setFont:[UIFont fontWithName:[ALApplozicSettings getFontFace] size:IMAGE_NAME_LABEL_SIZE]];
contactCell.unreadCountLabel.backgroundColor = [ALApplozicSettings getUnreadCountLabelBGColor];
contactCell.unreadCountLabel.layer.cornerRadius = contactCell.unreadCountLabel.frame.size.width/2;
contactCell.unreadCountLabel.layer.masksToBounds = YES;
//contactCell.mUserImageView.hidden = NO;
contactCell.mUserImageView.layer.cornerRadius = contactCell.mUserImageView.frame.size.width/2;
contactCell.mUserImageView.layer.masksToBounds = YES;
[contactCell.onlineImageMarker setBackgroundColor:[UIColor clearColor]];
UILabel* nameIcon = (UILabel*)[contactCell viewWithTag:102];
nameIcon.textColor = [UIColor whiteColor];
ALMessage *message = (ALMessage *)self.mContactsMessageListArray[indexPath.row];
ALContactDBService *contactDBService = [[ALContactDBService alloc] init];
ALContact *alContact = [contactDBService loadContactByKey:#"userId" value: message.to];
ALChannelDBService * channelDBService =[[ALChannelDBService alloc] init];
ALChannel * alChannel = [channelDBService loadChannelByKey:message.groupId];
if([message.groupId intValue])
{
ALChannelService *channelService = [[ALChannelService alloc] init];
[channelService getChannelInformation:message.groupId orClientChannelKey:nil withCompletion:^(ALChannel *alChannel)
{
contactCell.mUserNameLabel.text = [alChannel name];
contactCell.onlineImageMarker.hidden=YES;
}];
}
else
{
contactCell.mUserNameLabel.text = [alContact getDisplayName];
}
contactCell.mMessageLabel.text = message.message;
contactCell.mMessageLabel.hidden = NO;
if ([message.type integerValue] == [FORWARD_STATUS integerValue])
contactCell.mLastMessageStatusImageView.image = [ALUtilityClass getImageFromFramworkBundle:#"mobicom_social_forward.png"];
else if ([message.type integerValue] == [REPLIED_STATUS integerValue])
contactCell.mLastMessageStatusImageView.image = [ALUtilityClass getImageFromFramworkBundle:#"mobicom_social_reply.png"];
BOOL isToday = [ALUtilityClass isToday:[NSDate dateWithTimeIntervalSince1970:[message.createdAtTime doubleValue]/1000]];
contactCell.mTimeLabel.text = [message getCreatedAtTime:isToday];
[self displayAttachmentMediaType:message andContactCell:contactCell];
// here for msg dashboard profile pic
[nameIcon setText:[ALColorUtility getAlphabetForProfileImage:[alContact getDisplayName]]];
if([message getGroupId])
{
[contactCell.onlineImageMarker setHidden:YES];
}
else if(alContact.connected && [ALApplozicSettings getVisibilityForOnlineIndicator])
{
[contactCell.onlineImageMarker setHidden:NO];
}
else
{
[contactCell.onlineImageMarker setHidden:YES];
}
if(alContact.block || alContact.blockBy)
{
[contactCell.onlineImageMarker setHidden:YES];
}
BOOL zeroContactCount = (alContact.unreadCount.intValue == 0 ? true:false);
BOOL zeroChannelCount = (alChannel.unreadCount.intValue == 0 ? true:false);
if(zeroChannelCount || zeroContactCount)
{
contactCell.unreadCountLabel.text = #"";
[contactCell.unreadCountLabel setHidden:YES];
}
if(!zeroContactCount && [alContact userId] && (message.groupId.intValue == 0 || message.groupId == NULL)){
[contactCell.unreadCountLabel setHidden:NO];
contactCell.unreadCountLabel.text=[NSString stringWithFormat:#"%i",alContact.unreadCount.intValue];
}
else if(!zeroChannelCount && [message.groupId intValue]){
[contactCell.unreadCountLabel setHidden:NO];
contactCell.unreadCountLabel.text = [NSString stringWithFormat:#"%i",alChannel.unreadCount.intValue];
}
contactCell.mUserImageView.backgroundColor = [UIColor whiteColor];
if([message.groupId intValue])
{
[contactCell.mUserImageView setImage:[ALUtilityClass getImageFromFramworkBundle:#"applozic_group_icon.png"]];
NSURL * imageUrl = [NSURL URLWithString:alChannel.channelImageURL];
if(imageUrl)
{
[contactCell.mUserImageView sd_setImageWithURL:imageUrl];
}
nameIcon.hidden = YES;
}
else if(alContact.contactImageUrl)
{
NSURL * theUrl1 = [NSURL URLWithString:alContact.contactImageUrl];
[contactCell.mUserImageView sd_setImageWithURL:theUrl1];
nameIcon.hidden = YES;
}
else
{
nameIcon.hidden = NO;
[contactCell.mUserImageView sd_setImageWithURL:[NSURL URLWithString:#""]];
contactCell.mUserImageView.backgroundColor = [ALColorUtility getColorForAlphabet:[alContact getDisplayName]];
}
}break;
default:
break;
}
return contactCell;
}
After long hit and trials it worked after updating
dispatch_async(dispatch_get_main_queue(), ^{
contactCell.mUserImageView.layer.cornerRadius = contactCell.mUserImageView.frame.size.width/2;
contactCell.mUserImageView.layer.masksToBounds = YES;
});
OR
you can use context graphics to get circular image
But still need a prior solution
I have an app with collection view and a cell within this collection view that redirects to external link.
Whenever that link opens the app crashs in the background and gives on the debugger:
"Terminated due to memory issue".
If I just press home button the app continues working just fine.
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) Portrait = NO;
else if (UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation)) Portrait = YES;
else Portrait = [self getStatusBarOrientations];
if(indexPath.row == 4 && indexPath.section == 0)
{
NewsListCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell4" forIndexPath:indexPath];
if ([languageID isEqualToString:#"1025"])
{
cell.title.text =[NSString stringWithFormat:#"جريدة اليوم %#", [self.pdfCoverDict valueForKey:#"PdfDate"]];
[cell.contentView setTransform:CGAffineTransformMakeScale(-1, 1)];
} else {
cell.title.text =[NSString stringWithFormat:#"Today's Edition %#", [self.pdfCoverDict valueForKey:#"PdfDate"]];
}
NSURL *imageUrl = [NSURL URLWithString:[self.pdfCoverDict valueForKey:#"CoverImage"]];
[cell.imageView sd_setImageWithURL:imageUrl];
cell.imageViewWidth.constant = Portrait ? 246 : 331;
cell.imageViewHeight.constant = Portrait ? 350 : 471;
return cell;
} else if(indexPath.row == 0) {
NewsListCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"HeaderView" forIndexPath:indexPath];
cell.title.text = [self.channelKeys objectAtIndex:indexPath.section];
cell.backView.hidden = [channelID isEqualToString:#"1"] ? (indexPath.section == 0 ? YES : NO) : YES;
if([languageID isEqualToString:#"1025"]) {
[cell.contentView setTransform:CGAffineTransformMakeScale(-1, 1)];
} else {
[cell.contentView setTransform:CGAffineTransformMakeScale(-1, 1)];
[cell.title setTransform:CGAffineTransformMakeScale(-1, 1)];
}
return cell;
} else {
NSInteger row;
if(indexPath.row != 1 && indexPath.row != 2 && indexPath.row != 3) {
row = indexPath.row - 2;
} else {
row = indexPath.row - 1;
}
NSMutableArray * articles = [self.channelArticles valueForKey:[self.channelKeys objectAtIndex:indexPath.section]];
// if(row < articles.count) {
NSString *articleImage = [[articles objectAtIndex:row] valueForKey:#"ArticleMedia"];
NSString *cellIdentifier;
NSString *articleLangID = [NSString stringWithFormat:#"%#", [[articles objectAtIndex:row] valueForKey:#"LanguageID"]];
if(indexPath.section == 0) {
switch (indexPath.row)
{
case 1:
if(![articleImage isEqualToString:#""]) cellIdentifier = #"Cell1";
else cellIdentifier = #"CellNoImg";
break;
case 2:
if(![articleImage isEqualToString:#""]) cellIdentifier = #"Cell2";
else cellIdentifier = #"CellNoImg";
break;
case 3:
if(![articleImage isEqualToString:#""]) cellIdentifier = #"Cell3";
else cellIdentifier = #"CellNoImg";
break;
case 5:
if(![articleImage isEqualToString:#""]) cellIdentifier = #"Cell5";
else cellIdentifier = #"CellNoImg";
break;
case 6:
if(![articleImage isEqualToString:#""]) cellIdentifier = #"Cell5";
else cellIdentifier = #"CellNoImg";
break;
default:
if(![articleImage isEqualToString:#""]) {
if([channelID isEqualToString:#"1"]) cellIdentifier = #"Cell6";
else cellIdentifier = #"Cell5";
} else {
cellIdentifier = #"CellNoImg";
}
break;
}
} else {
if(![articleImage isEqualToString:#""]) cellIdentifier = #"Cell5";
else cellIdentifier = #"CellNoImg";
}
NewsListCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if([[[articles objectAtIndex:row] valueForKey:#"LabelName"] isEqualToString:#""]) {
cell.labelNameHeight.constant = 0;
} else {
cell.labelNameHeight.constant = 21;
}
cell.labelName.text = [[articles objectAtIndex:row] valueForKey:#"LabelName"];
cell.title.text = [[articles objectAtIndex:row] valueForKey:#"MainHeadline"];
if(indexPath.row == 3 && indexPath.section == 0) {
if(![articleImage isEqualToString:#""]) cell.titleHeight.constant = [self lineCountForLabel:cell.title labelWidth:cell.frame.size.width - Portrait ? 254 : 337] * 35;
else cell.titleHeight.constant = [self lineCountForLabel:cell.title labelWidth:cell.frame.size.width - 10] * 35;
} else {
cell.titleHeight.constant = [self lineCountForLabel:cell.title labelWidth:cell.frame.size.width - 10] * 35;
}
NSString *authorTime = [self getAuthorTime:[articles objectAtIndex:row]];
if([authorTime isEqualToString:#""]) {
cell.authorTimeHeight.constant = 0;
} else {
cell.authorTimeHeight.constant = 21;
}
cell.authorTime.text = authorTime;
NSString *details = [[articles objectAtIndex:row] valueForKey:#"Introduction"];
cell.details.attributedText = [self getAttributedString: details language: articleLangID];
if([cellIdentifier isEqualToString:#"CellNoImg"]) {
defaultLines = 5;
if([cell.authorTime.text isEqualToString:#""]) defaultLines++;
if([cell.labelName.text isEqualToString:#""]) defaultLines++;
if(cell.titleHeight.constant / 35 == 1) defaultLines++;
cell.details.numberOfLines = defaultLines;
}
if(indexPath.section == 0) {
switch (indexPath.row)
{
case 2:
cell.imageViewWidth.constant = Portrait ? 240 : 323;
cell.imageViewHeight.constant = Portrait ? 151 : 199;
break;
case 3:
cell.imageViewWidth.constant = Portrait ? 240 : 323;
cell.imageViewHeight.constant = Portrait ? 151 : 199;
case 5:
cell.imageViewWidth.constant = Portrait ? 240 : 323;
cell.imageViewHeight.constant = Portrait ? 151 : 199;
break;
case 6:
cell.imageViewWidth.constant = Portrait ? 240 : 323;
cell.imageViewHeight.constant = Portrait ? 151 : 199;
break;
default:
cell.imageViewWidth.constant = Portrait ? 240 : 323;
cell.imageViewHeight.constant = Portrait ? 151 : 199;
break;
}
} else {
cell.imageViewWidth.constant = Portrait ? 240 : 323;
cell.imageViewHeight.constant = Portrait ? 151 : 199;
}
if (![articleImage isEqualToString:#""]) {
NSURL *imageUrl = [NSURL URLWithString:articleImage];
[cell.imageView sd_setImageWithURL:imageUrl];
} else {
[cell.imageView sd_setImageWithURL:nil];
}
if([articleLangID isEqualToString:#"1025"]) {
[cell.contentView setTransform:CGAffineTransformMakeScale(-1, 1)];
cell.labelName.textAlignment = NSTextAlignmentRight;
cell.title.textAlignment = NSTextAlignmentRight;
cell.authorTime.textAlignment = NSTextAlignmentRight;
cell.details.textAlignment = NSTextAlignmentRight;
} else {
if (indexPath.row == 3) {
[cell.contentView setTransform:CGAffineTransformMakeScale(-1, 1)];
[cell.labelName setTransform:CGAffineTransformMakeScale(-1, 1)];
[cell.title setTransform:CGAffineTransformMakeScale(-1, 1)];
[cell.authorTime setTransform:CGAffineTransformMakeScale(-1, 1)];
[cell.details setTransform:CGAffineTransformMakeScale(-1, 1)];
[cell.imageView setTransform:CGAffineTransformMakeScale(-1, 1)];
}
cell.labelName.textAlignment = NSTextAlignmentLeft;
cell.title.textAlignment = NSTextAlignmentLeft;
cell.authorTime.textAlignment = NSTextAlignmentLeft;
cell.details.textAlignment = NSTextAlignmentLeft;
}
return cell;
}
}
What device is this happening on? Once your app is in the background you can expect for it to be killed at any time - it doesn't have to be using excessive memory.
If the new foreground app wants more memory, the OS will give it as much as it can. It will send a memory warning to any backgrounded apps. If they don't respond by reducing the memory footprint, or the foreground app still wants more memory, then the background apps are killed.
Your app isn't crashing, it is being killed by the OS because it is no longer the foreground app and its resources are needed elsewhere.
I have the same problem - terminated due to memory issue when the memory usage still low.
Then I found the reason: actually the memory allocated very quickly.
Just because the memory was destroyed very quickly, so it seems memory usage is still very low.
You can use instruments to see current memory usage / total memory usage
picture to explain how view memory usage / total memory usage
The picture is found from internet, but explain the problem clearly.
You will find this value in the rapid reduction, the the area of light color is quickly grow, then your app crash due to memory issue when memory usage still low.
I would check elsewhere in your code. The answer is more than likely that you have a retain cycle somewhere and are leaking memory. Search for items like closures where you may have neglected to declare self as weak (may be nil) or unowned (never nil in that instance but that self is not owned by the closure object)
[weak self]
or
[unowned self]
Instruments can help you but a detailed inspection of the objects in use (controllers, custom view classes, custom tableviewcell subclasses, tableviewrowaction delegate methods that might reference self, etc.) will be required. When you find the suspect area and make corrections, you should test the behavior on your device and see if you can spot the leak in instruments.
Verify that your fix removes the leak, test more and verify that no other leaks are happening (most likely it's more than one).
If this problem only occur when the app is not active, then it may also be caused by other (memory hungry) apps on the test device. To rule this out, disable Background App Refresh of other apps and/or close other apps and/or restart the device.
I'm facing problem with saving UITableViewCell's state and can't figure out how to solve it. Hope somebody can help me.
Explanation:
There is an API on server and I get data from it and then store it inside NSMutableArray. Each object of an array contains property ready which can be 1 or 0. So I've no problems with populating UITableView with this data but not every data object is ready (i.e 0) and I need to get progress of completion at server and after that to show it in each cell is need it. I've UIProgressView in dynamic prototype of UITableViewCell and set progress after getting. There is no problem if such "not ready" object is only one. But if there are many objects I can't show progress and I don't understand why.
So here is my code.
cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"readyCell";
AVMMovieCell *cell = [self.readyTable dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = (AVMMovieCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
AVMFilmsStore *oneItem;
oneItem = [readyArray objectAtIndex:indexPath.row];
NSNumber *rowNsNum = [NSNumber numberWithUnsignedInt:(unsigned int)indexPath.row];
if (oneItem.ready==1){
cell.progressLabel.hidden = YES;
cell.progressLine.hidden = YES;
if ([selecedCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]] )
{
if (![cell.progressLabel.text isEqualToString:#""]&& ![cell.progressLabel.text isEqualToString:#"Success"] && ![cell.progressLabel.text isEqualToString:#"Creating"]){
cell.progressLabel.hidden = NO;
cell.progressLine.hidden = NO;
} else {
cell.progressLabel.hidden = YES;
cell.progressLine.hidden = YES;
}
}
else{
if(!oneItem.isProcessing && !cell.selected){
cell.progressLabel.hidden = YES;
cell.progressLine.hidden = YES;
}
}
} else { //if processing
if (![processingCellsArray containsObject:[NSString stringWithFormat:#"%#",rowNsNum]]){
[processingCellsArray addObject:[NSString stringWithFormat:#"%#",rowNsNum]];
if (!cell.isSelected){
[cell setSelected:YES];
}
cell.progressLabel.hidden = NO;
cell.progressLine.hidden = NO;
NSArray * arrayOfThingsIWantToPassAlong =
[NSArray arrayWithObjects: cell, oneItem, indexPath, nil];
if(!isMaking){
[self performSelector:#selector(getProgress:)
withObject:arrayOfThingsIWantToPassAlong
afterDelay:0];
} else{
[self performSelector:#selector(getProgress:)
withObject:arrayOfThingsIWantToPassAlong
afterDelay:0.5];
}
isMaking = YES;
} else {
if (!cell.isSelected){
[cell setSelected:YES];
}
cell.progressLabel.hidden = NO;
cell.progressLine.hidden = NO;
NSArray * arrayOfThingsIWantToPassAlong =
[NSArray arrayWithObjects: cell, oneItem, indexPath, nil];
if(!isMaking){
[self performSelector:#selector(getProgress:)
withObject:arrayOfThingsIWantToPassAlong
afterDelay:0];
} else{
[self performSelector:#selector(getProgress:)
withObject:arrayOfThingsIWantToPassAlong
afterDelay:0.3];
}
isMaking = YES;
}
}
return cell;
}
and getProgress method:
-(void)getProgress:(NSArray*)args{
if (progManager == nil && !progStop){
__block AVMFilmsStore * oneItem = args[1];
if(!oneItem.isLocal){
__block AVMMovieCell * cell = args[0];
__block NSIndexPath *indexPath = args[2];
progManager = [AFHTTPRequestOperationManager manager];
__block NSString *token = [defaults objectForKey:#"token"];
__block NSString *header = [NSString stringWithFormat:#"Bearer %#",token];
__block NSDictionary *params = #{#"lang": NSLocalizedString(#"lang",nil),#"project":oneItem.fileId};
__block NSString *oneHundredPercent;
__block NSString *progIsText;
progManager.responseSerializer = [AFJSONResponseSerializer serializer];
[progManager.requestSerializer setValue:header forHTTPHeaderField:#"Authorization"];
if(cell.selected || isMaking) { //if I just check for "cell.selected" is always "NO"
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[progManager POST:#"http://example.com/api/project/get-progress" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
if ([[responseObject objectForKey:#"result"]isEqualToString:#"success"]){
progCreate = [responseObject objectForKey:#"progress"];
oneHundredPercent = #"100";
if ([progCreate intValue]==[oneHundredPercent intValue]){
if([processingCellsArray containsObject:[NSString stringWithFormat:#"%ld",(long)indexPath.row]]){
[processingCellsArray removeObject:[NSString stringWithFormat:#"%ld",(long)indexPath.row]];
[cell setSelected:NO];
}
[readyArray removeAllObjects];
[defaults setObject:#"false" forKey:#"isSomethingInSort"];
isMaking = NO;
[self getReadyMovies:progIsText nameLabel:oneItem.fileName];
} else{
if([progCreate intValue]>=50){
if([progCreate intValue]>=60){
self.navigationController.navigationItem.leftBarButtonItem.enabled = YES;
createMainButton.enabled = YES;
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"gotFiftyNote" object:#"50"];
[cell.progressLine setProgress:[progCreate floatValue]/100 animated:YES];
} else {
[cell.progressLine setProgress:progUploadLimit];
}
progManager = nil;
progManager.responseSerializer = nil;
progManager.requestSerializer = nil;
token = nil;
header = nil;
params = nil;
progIsText = nil;
oneItem = nil;
cell = nil;
indexPath = nil;
isMaking = YES;
progCreate = nil;
oneHundredPercent = nil;
[self getProgress:args];
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSLog(#"Error: %#", error.localizedDescription);
}];
}
}
}
}
Any suggestions will be helpful for me. I've a headache for two weeks with this problem.
I see your code but is kind of difficult to follow with those large methods. I wouldn't keep track of the processing cells in an array. Each cell has an object to represent, those object have a bool value of ready and a progress value, right?. So try something like this:
Make sure each of your cells have a progressView as a subview.
Your cell class should have a public method named styleForReady:(bool)isReady andProgress:(nsinteger)progress
Make the service call to see if they are done or not, for each model. Whenever that service call comes back, you just update the model objects in the array, and after they have the new progress values you do [self.tableView reloadData]. This would trigger numberOfRows (which should return arrayOfObjects.count) and cellForRowAtIndexPath:(which should dequeue the cell for that indexPath, grab the model representing that cell, something like arrayOfObjects[indexPath.row], and finally, call the cell to style itself based on that model doing [cell styleForReady:objectModel.ready andProgress:objectModel.progress])
Keep in mind that the controller should just keep track of the model objects, dequeue the cell and tell the cell to style passing the model. Don't put all the logic in the controller.
At first view load it shows me properly like this:
Then when I refresh table or pop the view back it messes up like this:
But If I scroll down when it passes off the table it comes back to normal.
I populate the cell like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
TableCell *cellTable = (TableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if ([[_itemsOfPath objectAtIndex:indexPath.row] isKindOfClass:[OCFileDto class]] ) {
//Add swipe menu
cellTable.leftUtilityButtons = [self leftButtons];
cellTable.delegate = self;
OCFileDto *itemDto = [_itemsOfPath objectAtIndex:indexPath.row];
[cellTable setSelectionStyle:UITableViewCellSelectionStyleNone];
cellTable.cellName.lineBreakMode = NSLineBreakByTruncatingMiddle;
cellTable.filePath = [itemDto.filePath stringByReplacingPercentEscapesUsingEncoding:(NSStringEncoding)NSUTF8StringEncoding];
cellTable.fileName = [itemDto.fileName stringByReplacingPercentEscapesUsingEncoding:(NSStringEncoding)NSUTF8StringEncoding];
if ([itemDto isDirectory]) {
cellTable.cellImg.image = [UIImage imageNamed:#"b_ic_menu_archive.png"];
cellTable.cellType = kCellTypeFolder;
cellTable.cellName.text = [cellTable.fileName substringToIndex:[cellTable.fileName length]-1];
cellTable.timeStamp.text = [self stringLasResponse:itemDto.date];
numberOfFolder++;
} else {
if ([[cellTable.fileName pathExtension] caseInsensitiveCompare:#"pdf"] == NSOrderedSame) {
cellTable.cellImg.image = [UIImage imageNamed:#"file_pdf.png"];
cellTable.cellType = kCellTypeFilePDF;
}else{
if ([[cellTable.fileName pathExtension] caseInsensitiveCompare:#"jpg"] == NSOrderedSame ||
[[cellTable.fileName pathExtension] caseInsensitiveCompare:#"png"] == NSOrderedSame ||
[[cellTable.fileName pathExtension] caseInsensitiveCompare:#"gif"] == NSOrderedSame) {
cellTable.cellImg.image = [UIImage imageNamed:#"file_image.png"];
cellTable.cellType = kCellTypeFilePicture;
}else{
cellTable.cellImg.image = [UIImage imageNamed:#"file.png"];
cellTable.cellType = kCellTypeOtherFile;
}
/*============Check if file on local or not==============*/
NSString *localPath = [NSString stringWithFormat:#"%#/%#",documentPath ,cellTable.fileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:localPath] && cellTable.cellType != kCellTypeFolder) {
cellTable.downloadedOverlay.image = [UIImage imageNamed:#"DownloadedOverlay.png"];
/*============Check if file on local or not==============*/
}
}
cellTable.cellName.text = cellTable.fileName;
cellTable.timeStamp.text = [NSString stringWithFormat:#"%#, %#", [self stringLasResponse:itemDto.date], [NSByteCountFormatter stringFromByteCount:itemDto.size countStyle:NSByteCountFormatterCountStyleMemory]];
numberOfFile++;
}
return cellTable;
}else{
// Show File Summary at last Cell
if ([[_itemsOfPath objectAtIndex:indexPath.row] isKindOfClass:[TableViewCellNoBorder class]] ) {
if ([_itemsOfPath count] > 1 ) {
NSString *tableSummary;
if (numberOfFolder == 0) {
tableSummary = [NSString stringWithFormat:#"%d files", numberOfFile];
}else{
if (numberOfFile == 0) {
tableSummary = [NSString stringWithFormat:#"%d folders", numberOfFolder];
}else{
tableSummary = [NSString stringWithFormat:#"%d files, %d folders",numberOfFile,numberOfFolder];
}
}
//Summery cell
TableViewCellNoBorder *lastCell = [_itemsOfPath objectAtIndex:indexPath.row];
lastCell.textLabel.text = tableSummary;
lastCell.textLabel.font = [UIFont systemFontOfSize:9];
lastCell.textLabel.textAlignment = NSTextAlignmentCenter;
tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
return lastCell;
}
}
//No file and folder
tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
return [_itemsOfPath objectAtIndex:indexPath.row];
}
}
*** That line I use it to add a green arrow.
I debug it and see all the files that will add my green arrow is call properly but when it shows some other cell has that green arrow in it too.
I read around it said some thing about that cell has been dequeue and it loses their state and do something like:
if (cellTable == nil) {
NSLog(#"TEST");
}
But when I Debug it, they weren't called at all.
I don't know how to deal with it. Can anybody give me a suggestion?
Thank you.
EDIT:
Ok, after checking with more detail your code, change:
if ([itemDto isDirectory]) {
cellTable.cellImg.image = [UIImage imageNamed:#"b_ic_menu_archive.png"];
cellTable.cellType = kCellTypeFolder;
cellTable.cellName.text = [cellTable.fileName substringToIndex:[cellTable.fileName length]-1];
cellTable.timeStamp.text = [self stringLasResponse:itemDto.date];
numberOfFolder++;
}
to
if ([itemDto isDirectory]) {
cellTable.cellImg.image = [UIImage imageNamed:#"b_ic_menu_archive.png"];
cellTable.cellType = kCellTypeFolder;
cellTable.cellName.text = [cellTable.fileName substringToIndex:[cellTable.fileName length]-1];
cellTable.timeStamp.text = [self stringLasResponse:itemDto.date];
numberOfFolder++;
cellTable.downloadedOverlay.image = nil;
}
I hope you understand what's going on. When you dequeue a cell, you get it as it was set up last time it was used. You have to clear everything up and set all the elements of the cell to display the new information.
End edit
Add
else {
cellTable.downloadedOverlay.image = nil;
}
to the if clause:
/*============Check if file on local or not==============*/
NSString *localPath = [NSString stringWithFormat:#"%#/%#",documentPath ,cellTable.fileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:localPath] && cellTable.cellType != kCellTypeFolder) {
cellTable.downloadedOverlay.image = [UIImage imageNamed:#"DownloadedOverlay.png"];
/*============Check if file on local or not==============*/
}
Final:
/*============Check if file on local or not==============*/
NSString *localPath = [NSString stringWithFormat:#"%#/%#",documentPath ,cellTable.fileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:localPath] && cellTable.cellType != kCellTypeFolder) {
cellTable.downloadedOverlay.image = [UIImage imageNamed:#"DownloadedOverlay.png"];
/*============Check if file on local or not==============*/
}
else {
cellTable.downloadedOverlay.image = nil;
}
This way, if you get a dequeued cell that had the image already set, you clear it up.