UITableViewController Zombies On Scroll - objective-c

Happy Friday. Having an interesting time debugging a zombie issue. I have a UITableView that gets its data source from an NSMutableArray loaded with Word objects. (See class below). When the app loads, all is good - the 1st 8 or 9 words display in table view as expected. However when I scroll, I am getting zombies in my Word objects, as evidenced in the debugger output "<Zombie>" as the value for the Word class instance variable values. (See screenshot). This results in a crash.
TableSearch[12440:207] *** -[CFString respondsToSelector:]: message sent to deallocated instance 0x6b1fe70
Here is the Word class
//Word Class
#import "Word.h"
#implementation Word
#synthesize word;
#synthesize definition;
+ (id)wordWith:(NSString *)word Definition:(NSString *)definition
{
Word *newWord = [[[self alloc] init] autorelease];
newWord.word = word;
newWord.definition = definition;
return newWord;
}
- (void)dealloc
{
[word release];
[definition release];
[super dealloc];
}
#end
I am sure this is something dumb but I cannot see where I went wrong.
I ran "Analyze" on Instruments and no issues were reported. After the crash, I ran "malloc_history 12440 0x6b1fe70" and looked at the output but not sure what to look for except for
the class name of the objects that have the zombie, which I did not see.
Any help tracking this down is greatly appreciated.
Thanks!

Are the "word" and "definition" properties of your Word class both defined as "retain"? E.g.
#property (nonatomic, retain) NSString *word;
#property (nonatomic, retain) NSString *definition;
If you've written them as:
#property (nonatomic, assign) NSString *word;
or just
#property (nonatomic) NSString *word;
Then it would account for your crash.

Related

Objective-C class returning (null) for control values?

I am trying to make a TableViewController.. I got it to work using code from a youtube lesson: "Cocoa Programming L13-14" But then when I try to change it so that the default values aren't hard coded... but rather the values of controls in the Interface Builder, I get (null) across the board. Here is the code:
#import <Foundation/Foundation.h>
#interface Person : NSObject {
IBOutlet NSPathControl* pcSource;
IBOutlet NSPathControl* pcDestination;
IBOutlet NSTextField* tfBackupAmount;
NSURL* urlSource;
NSURL* urlDestination;
NSString* strBackupAmount;
//Old--
//NSString* name;
//int age;
}
#property NSURL* urlSource;
#property NSURL* urlDestination;
#property NSString* strBackupAmount;
//Old--
//#property (copy) NSString* name;
//#property int age;
#end
and
#import "Person.h"
#implementation Person
#synthesize urlSource;
#synthesize urlDestination;
#synthesize strBackupAmount;
//Old--
//#synthesize name;
//#synthesize age;
- (id)init {
self = [super init];
if (self) {
urlSource = [pcSource URL];
urlDestination = [pcDestination URL];
strBackupAmount = [tfBackupAmount stringValue];
NSLog(#"%#\n%#\n%#",urlSource,urlDestination,strBackupAmount);
//Old--
//name = #"Yoda";
//age = 900;
//NSLog(#"%#: %i", name, age);
}
return self;
}
#end
Everything commented //Old-- worked, and interacted fine with the TableViewController. So I am assuming all that still works fine. The 3 controls (2 NSPathControl & 1 NSTextField) are linked up to an Object class:Person in Interface Builder with the controls linked up. Why am I getting output of:
(null)
(null)
(null)
? When I get to the NSLog(); line? Where am I going wrong?
Thanks!
pcSource, pcDestination, or tfBackupAmount aren't initialized when your init method is called, so they're all nil. Sending a message to nil is legal in Objective-C, and you'll just get nil back. That means urlSource, urlDestination, and strBackupAmount are all nil too, and that's why you ge the log output you're seeing.
You need to change the log message to sometime after those variables are initialized.
Try putting the code in -viewDidLoad rather than -init. It all has to do with the order of events (-init gets called before any of the IB stuff happens.
Ok, technically this question - I found an answer for it. It is to make a custom init method. In my case this means:
Person* p = [[Person alloc] initWithurlSource:[NSURL URLWithString:#"moo"] andurlDestination:[NSURL URLWithString:#"cow"] andstrBackupAmount:#"foo"];
However this still doesn't solve my problem of getting values for IBOutlets from another Class (in this case my TableViewController class) that has been exposed as #property:
#interface AppDelegate : NSObject <NSApplicationDelegate> {
.....
.....
#property (nonatomic, retain) NSPathControl* pcSource;
#property (nonatomic, retain) NSPathControl* pcDestination;
#property (nonatomic, retain) NSTextField* tfBackupAmount;
I am still having trouble getting values for these controls in my "addButtonPressed" method with:
//ad is AppDelegate - declared in interface as AppDelegate* ad;
NSPathControl* pcSource = [ad pcSource];
NSPathControl* pcDestination = [ad pcDestination];
NSTextField* tfBackupAmount = [ad tfBackupAmount];

How to initialize the NSArray in ARC?

I tried initializing the array :
In .h file
#property (nonatomic, retain) NSArray *accounts;
In .m file :
#synthesize accounts;
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *arrList = [acAccountStore accountsWithAccountType:accountType];
// This returns array
self.accounts = [NSArray arrayWithArray:arrList]; // I tried debug after
// this and it gives me data in debugger.
// Note array List have 3 data in it.
}
Now On button click i call a method:
- (IBAction) ButtonClicked :(id) sender {
NSLog(#" data : %#",[self.accounts objectAtIndex:0]); // Breaks at this point.
// When i tried with debug it gives me (no Objective-C description available)
}
Is the initialization of array correct Or If the code is not right please let me know.
Main concern is when i do debug in viewDidLoad, the self.accounts show me proper values. But after doing the click event its empty and throws EXEC_BAD_ACCESS error.
Thanks for help in advance
hm looks fine. A couple of questions then:
Where are you calling the self.accounts = [NSArray arrayWithArray:arrList];
I assume that the array is being setup before your button is being pressed?
There's no real reason that arc should be clearing out the variable. Have you set a strong reference to it or a weak one? If you're using self. on a variable, you should have :
#property (nonatomic, strong) NSArray *accounts;
or similar to that in the .h file and then
#synthesize accounts;
in the .m file.
If you've got weak instead of strong then ARC may possibly clear the memory but it still shouldn't.
Update:
Create a property for your account store as well. I had this exact issue recently and this fixed it.
#property (nonatomic, strong) ACAccountStore *accountStore;
Original Answer
Because you're using ARC, you need to change your property declaration from
#property (nonatomic, retain) NSArray *accounts;
to:
#property (nonatomic, strong) NSArray *accounts;
With the latest LLVM compiler, you don't need to synthesize properties either. So you can remove #synthesize accounts.
You should always use defensive coding as well, so in your - buttonClicked: method, you should do:
- (IBAction)buttonClicked:(id)sender {
if (self.accounts) {
NSLog(#"data: %#", [self.accounts objectAtIndex:0]);
}
}
This makes sure that the pointer to the array is valid.
You can also check to make sure an item in an array exists before trying to read it by doing:
- (IBAction)buttonClicked:(id)sender {
if (self.accounts.count > 0)
NSLog(#"data: %#", [self.accounts objectAtIndex:0]);
}
}

Objective C Adding custom objects into NSMutableArray

I want to store a list of data records in a NSMutableArray for use in a UITableView. In other languages I would have used a simple 'type' structure to define the record structure but I understand the way to do this in Obj-C is to define a new class. I've done this as follows :
#interface CustSuppListItem : NSObject
#property (nonatomic, copy, readwrite) NSString *acCode;
#property (nonatomic, copy, readwrite) NSString *acCompany;
#property (nonatomic, copy, readwrite) NSString *acContact;
#property (nonatomic, assign, readwrite) double osBalBase;
#property (nonatomic, assign, readwrite) unsigned int acAccStatus;
#end
#implementation CustSuppListItem
#synthesize acCode, acCompany, acContact, osBalBase, acAccStatus;
#end
In the viewDidLoad of my UITableViewController I instantiate the array :
tableListDataArray = [[NSMutableArray alloc] init];
Once I have retrieved my data, I add it to the array as follows :
CustSuppListItem *custSuppItem = [[CustSuppListItem alloc] init];
[custSuppItem setAcCode:[jsonCustSuppRecord getStringForKey:#"acCode"]];
[custSuppItem setAcCompany:[jsonCustSuppRecord getStringForKey:#"acCompany"]];
[custSuppItem setAcContact:[jsonCustSuppRecord getStringForKey:#"acContact"]];
[custSuppItem setOsBalBase:[jsonCustSuppRecord getDoubleForKey:#"osBalBase"]];
[custSuppItem setAcAccStatus:[jsonCustSuppRecord getIntForKey:#"acAccStatus"]];
[tableListDataArray addObject:custSuppItem];
[custSuppItem release];
In my table cellForRowAtIndexPath method, I retrieve the data for the current cell as follows:
CustSuppListItem *listDataRecord = [tableListDataArray objectAtIndex:indexPath.row];
[cell.lblCompanyName setText:listDataRecord.acCompany]; // EXC_BAD_ACCESS here
[cell.lblAcCodeContact setText:[NSString stringWithFormat:#"%#, %#",
listDataRecord.acCode, listDataRecord.acContact]];
[cell.lblBalance setText:[Utils fmtNumber:listDataRecord.osBalBase withDecPlaces:2]];
[cell.lblStatus setText:[Utils exchAccStatusDesc:listDataRecord.acAccStatus]];
return cell;
In the dealloc method for the view controller I release the NSMutableArray :
[tableListDataArray release];
I'm very new to Obj-C so it would be great if somebody could confirm everything I've done so far makes sense and is in order. I am getting an intermittent EXC_BAD_ACCESS error when trying to read the acCompany property (see comment next to line) so something must not be right.
Any help appreciated,
Jonathan
All your code looks reasonable and correct to me at first glance.
A few things that I would look at are:
Confirm that cell definitely has a property lblCompanyName. If you're trying to assign to a property that doesn't exist then you will get this type of error. Have you defined a custom cell object type?
Confirm that it is always the acCompany property that is causing the EXC_BAD_ACCESS, and not just any property on the object. One way to do this would be to change the ordering of the lines in the cellForRowAtIndexPath method.
Confirm that the listDataRecord that's causing the crash is getting populated correctly in the first place. In other words, confirm that your jsonCustSuppRecord is always valid. What does jsonCustSuppRecord getStringForKey: return if the key doesn't exist in the jsonCustSuppRecord?
Set a breakpoint at this line: [tableListDataArray addObject:custSuppItem]; and examine the contents of the custSuppItem each time (this is an extension of point 3. above)

Whats the correct way to release a #property / IBOulet for UIViewController

I've read many different things about correct memory management for cocoa/objective-c
For instance ive read that any IBOutlets need to be set to 'nil' but something like an NSArray dosnt?
I would also like to know, is it important to call the super method before or after i release/nil everything
To put this memory issue to bed, can some please reply with the 100% correct way you would create a retained property and release it. If your not 100% sure please dont answer.
Here is what im currently doing but something is obviously wrong as i get the very frustrating EXEC_BAD_ACCESS!?! Almost like im releasing something twice?
header.h
#interface MyViewController : UIViewController {
UILabel *aLabel;
NSArray *aArray;
}
#property (nonatomic, retain) IBOutlet UILabel *aLabel;
#property (nonatomic, retain) NSArray *aArray;
method.m
#implementation MyViewController
#synthesize aLabel, aArray;
- (void)dealloc
{
[aLabel release], aLabel = nil;
[aArray release];
[super dealloc];
}
- (void)viewDidUnload
{
self.aLabel = nil; //Not sure about this bad boy???
[super viewDidUnload];
}
#end
In dealloc you have released the aLabel.Means it is not in memory.Again you are write this line ---aLabel=nil;Remove this line.So that it can't gives the Exec_badaccess.This means eventhough you don't have pointer stilll you are trying to access the pointer.

Getting “EXC_BAD_ACCESS”

I have an Address interface as this:
#interface AddressCard : NSObject
{
NSString *name;
NSString *email;
}
#property (copy, nonatomic) NSString *name, *email;
-(void) print;
-(void) setName:(NSString *) theName andEmail:(NSString *) theEmail;
-(void) dealloc;
#end
And implementation as:
#import "AddressCard.h"
#implementation AddressCard
#synthesize name, email;
-(void) setName:(NSString *) theName andEmail: (NSString *) theEmail
{
self.name = theName;
self.email = theEmail;
}
-(void) print
{
NSLog (#"==============================");
NSLog(#"| %-21s |", [self.name UTF8String]);
NSLog(#"| %-21s |", [self.email UTF8String]);
NSLog (#"==============================");
}
-(void) dealloc
{
[name release];
[email release];
[super dealloc];
}
#end
When I run it I keep getting an EXEC_BAD_ACCESS during the pool drain. I'm unable to find the cause and any help is appreciated. This is my first step into Objective-C so please bear with me.
thanks
Sunit
Since the error occurs when draining the pool, I might be suspicious that you've already deallocated the object by that point, and the object is over-released (although generally you'll get a "malloc double free" error for this) or perhaps the memory has already been overwritten by something else. I'd suggest running it with zombies enabled, as in this answer — if you have Snow Leopard you can use the Zombies tool in Instruments from Xcode's Run menu. Good luck!
That example looks quite familiar - I just worked through that book myself! The code you've posted above is fine, so the problem must be elsewhere. You might try checking out the author's forum - the source code for each of the steps for that chapter is posted there.
As others have suggested, you might want to look for extra 'release' calls.
Since you are releasing the strings in your dealloc method it would appear that your AddressCard object assumes ownership of the strings but you use this line to define the properites:
#property (copy, nonatomic) NSString *name, *email;
Using copy means that your object is not retaining the strings. Try changing that line to this:
#property (retain, nonatomic) NSString *name, *email;
Using retain means that your object will retain the strings until you release them in your dealloc.
Hopefully that will solve the problem.