I'm using TouchXML to parse an element in iOS. I retrieve a response from a web service using an NSInvocationOperation, then parse and display the results. Everything works fine as the background thread displays results on the main thread using [self performSelectorOnMainThread:#selector(displayLoginresult:) withObject:res waitUntilDone:NO]; but then I get an error:
2011-07-18 11:58:06.108 billsApp[873:7107] *** -[CFString release]: message sent to deallocated instance 0x5d809b0
The code to parse the element is:
-(LoginResult *) tryLogin:(NSString *)userName withPassword:(NSString*)password{
NSURL *url = [UrlUtility TryLogin:userName passwordHash:password];
CXMLDocument *responseObj = [UrlUtility xmlDocWithUrl:url];
if(responseObj == [NSNull null])
return [NSNull null];
CXMLElement *eleUser = [responseObj nodeForXPath:#"//User" error:nil];
CXMLElement *eleResult = [responseObj nodeForXPath:#"//Result" error:nil];
LoginResultType resultType;
//NSLog(#"Result: ");
//NSLog(eleResult );
// NSLog([[eleResult stringValue] lowercaseString]);
if ([[[eleResult stringValue] lowercaseString ] isEqualToString: #"successful"]){
resultType = Successful;
} else {
resultType = InvalidUsernameOrPassword;
}
LoginResult *res = [[LoginResult alloc] init];
res.result = resultType;
for (CXMLElement *resultElement in [responseObj children] ) {
NSLog([NSString stringWithFormat:#"%# %#", [resultElement name], [resultElement stringValue]]);
}
//todo: fix enum parsing =[LoginResult loginResultTypeStringToEnum: [eleResult stringValue]];
if(eleUser != nil) {
CXMLElement *eleClientID = [eleUser nodeForXPath:#"ClientID" error:nil];
CXMLElement *eleCompanyName = [eleUser nodeForXPath:#"CompanyName" error:nil];
CXMLElement *eleCompanyContact = [eleUser nodeForXPath:#"CompanyContact" error:nil];
CXMLElement *eleIsAgent = [eleUser nodeForXPath:#"IsAgent" error:nil];
CXMLElement *eleParentID = [eleUser nodeForXPath:#"ParentID" error:nil];
NSInteger *clientId = [[eleClientID stringValue] integerValue];
NSString *companyName = [eleCompanyName stringValue];
NSString *companyContact = [eleCompanyContact stringValue];
bool isAgent = [Utils stringToBool:[eleIsAgent stringValue]];
NSInteger *parentId = [[eleParentID stringValue] integerValue];
User *user = [[User alloc] initWithData:clientId companyName:companyName companyContact:companyContact isAgent:isAgent parentId:parentId];
res.user = user;
// release elements
// [eleClientID release];
// [eleCompanyName release];
// [eleCompanyContact release];
// [eleIsAgent release];
// [eleParentID release];
//release raw values
// [companyName release];
// [companyContact release];
}
// [eleResult release];
// [eleUser release];
return res;
}
Part of me wants to say it's a bug with TouchXML, but I find that very unlikely. Is there any way to further track down the error?
EDIT: The definitions for the properties on the User class is:
#property (nonatomic, readwrite) NSInteger clientId;
#property (nonatomic, retain) NSString *companyName;
#property (nonatomic, retain) NSString *companyContact;
#property (nonatomic, readwrite) bool isAgent;
#property (nonatomic, readwrite) NSInteger parentId;
And the instance is initialized with:
-(User*)initWithData:(NSInteger *)clientId companyName:(NSString *)company companyContact:(NSString*)contact isAgent:(bool)agent parentId:(NSInteger*)parentId {
//[self = super init];
self.clientId= clientId;
self.companyName= company;
self.companyContact= contact;
self.isAgent = agent;
self.parentId = parentId;
return self;
}
And the LoginResult class is:
#interface LoginResult : NSObject {
LoginResultType result;
User *user;
NSString * const loginResultTypeArray[4];
}
#property (nonatomic, readwrite) LoginResultType result;
#property (nonatomic, retain) User *user;
Just a try: are you correctly retaining companyName and companyContatct in your User class?
EDIT:
Next thing I would check is loginResultTypeArray. How are string assigned to it? I guess that this advice will also sound trivial to you, but it is really difficult to come up with useful suggestion with so little code...
Can't you get some idea about which CFString is actually being released? If it is not an autoreleased object, possibly the stack trace could point at the method which is sending the release message... this would be very helpful...
Otherwise, I would try and NSLog some of your NSStrings addresses, so that you can compare them with the address you find in the error log (and, again, try and find out which string was actually reused after deallocation).
Finally, another approach to find out which string is used after deletion could be using method swizzling to replace NSString's dealloc with a method of yours that, before calling the swizzled dealloc, does some logging of the objec. This will produce much log info, but knowing the address of the string you could find easily what you need. Find here info about swizzling.
This was a nightmare to track down. I had a method which returned an NSString *, which was then parsed by another method to produce an XML document, then release by the second method. I actually needed to autorelease it in the first method.
Related
I am trying to get the data from 'Employee' Entity which has empId, Name, deptId as attributes.
// Used to Populate data in table
Employee : NSObject
#property (nonatomic, retain) NSString *name;
#property int empId;
#property int deptId;
#synthesize empId, name, deptId;
&
CDEmployee : NSManagedObject
#property (nonatomic, retain) NSString *name;
#property int empId;
#property int deptId;
- (void)convertMyData:(Employee *)emp;
#dynamic empId, name, deptId;
- (void)convertMyData:(Employee *)emp
{
self.empId = emp.empId;
self.name = emp.name;
self.deptId = emp.deptId;
}
// My code to fetch & convert data retured from db to Employee class
-(NSArray *)getAllEmployees:(NSManagedObjectContext*)context
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(empId > %#)",[NSNumber numberWithInt:-1],[NSNumber numberWithInt:1]];
NSFetchRequest* req = [self createRequest:context]; // Request is correct
[req setPredicate:predicate];
NSFetchedResultsController* fetchContr = [[NSFetchedResultsController alloc] initWithFetchRequest:req managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
NSError* error = nil;
[fetchContr performFetch:&error];
NSArray *fetchedObjects = [fetchContr fetchedObjects];
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
for(int i = 0; i < [fetchedObjects count]; i++)
{
CDEmployee *cdEmp = [[fetchedObjects objectAtIndex:i] retain]; // just tried retaing
if (cdEmp)
{
Employee *emp = [[Employee alloc] init];
NSLog(#"Employee - %#", emp); // Shows tht is has object
[cdEmp convertMyData:emp]; //-----> Crashing here
[result addObject:emp];
[emp release];
}
}
[fetchContr release];
return result;
}
I am fetching results from core data & I am getting correct results back, but when I convert my core data result back to Employee(NSObject) class i am getting [NSManagedObject convertMyData:]: unrecognized selector sent to instance.
I tried adding another method say -(void)helloWorld to CDEmployee class & tried [cdEmp helloWorld]; but got the same crash.
Not getting why its causing the problem. I have method defined & implemented at proper place & its not even giving warning to me at compile time.
Here is a sample code that converts NSManagedObject to NSDictionary.
NSDictionary is even more easier to handle and operate on the data than NSObject. Also refer this Git Library for other similar iOS utility class methods.
+(NSDictionary *)convertManagedObjectToDictionary:(NSManagedObject *)managedObject{
if (!managedObject){
NSLog(#"Managed object is nil");
return nil;
}
else{
unsigned int objectsCount;
objc_property_t *objectProperties = class_copyPropertyList([managedObject class], &objectsCount);
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
for(int i = 0; i < objectsCount; i++) {
objc_property_t property = objectProperties[i];
NSString *name = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
id object = [managedObject valueForKey:name];
[dictionary setObject:((object == nil) ? [NSNull null] : object) forKey:name];
}
free(objectProperties);
return dictionary;
}
}
I tried in different way & got resolved http://www.raywenderlich.com/934/core-data-tutorial-for-ios-getting-started
First, you should really first think about what you want to accomplish from a non-technical perspective. What is it, actually?
Second, NSManagedObject is a subclass of NSObject, which means you already have a NSObject. Congratulations, you are done!
Also, in your code you call fetchMyData but did not show us where you define it or what the code looks like. Maybe a NSManagedObject category would be in order here? But still, this begs the question why you would want to do this. Even if you were to convert into a Foundation object, such as NSDictionary as suggested in the other answer, you would still have a problem with modelling relationships...
Finally, what SDK are you using? You can't be serious if you are still not using ARC.
I trying to learn how to save array of objects using NSKeyedArchiver and I coded a small application to do that and I logged to see if the array was saved but everytime I get 0 for array count and here is the code.
ViewController.h
#interface ViewController : UIViewController
{
IBOutlet UITextField *text;
IBOutlet UITextField *textName;
IBOutlet UITextField *textAge;
IBOutlet UILabel *name;
IBOutlet UILabel *age;
BOOL flag;
BOOL choice;
NSString *documentDirectory;
NSMutableArray *anArray;
Person *p;
NSData *data;
}
-(BOOL) dataFilePath;
-(IBAction)readPlist;
-(IBAction) writePlist;
#property (strong,nonatomic)IBOutlet UITextField *text;
#property (strong,nonatomic)IBOutlet UITextField *textName;
#property (strong,nonatomic)IBOutlet UITextField *textAge;
#property (strong,nonatomic)IBOutlet UILabel *name;
#property (strong,nonatomic)IBOutlet UILabel *age;
#property (strong,nonatomic)NSString *documentDirectory;
#property (strong,nonatomic)NSMutableArray *anArray;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
#synthesize text,documentDirectory,textAge,textName,name,age,anArray;
- (void)viewDidLoad
{
[super viewDidLoad];
// checking if the file was created and show a message if its created or not.
if ([self dataFilePath]) {
NSLog(#"File Created !");
} else {
NSLog(#"File Not Created !");
}
NSLog(#"File location : %#",documentDirectory);
choice = YES;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL) dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentDirectory = [path objectAtIndex:0];
documentDirectory = [documentDirectory stringByAppendingPathComponent:#"MilmersĀData.dat"];
return TRUE;
}
- (IBAction)writePlist
{
p.name = textName.text;
p.age = [textAge.text intValue];
[anArray addObject:p];
for (int i=0; i<[anArray count]+1; i++) {
Person *pp = [[Person alloc]init];
pp=[anArray objectAtIndex:i];
NSLog(#"Name: %#",pp.name); // checking the names in pp object but getting null
}
data = [NSKeyedArchiver archivedDataWithRootObject:anArray];
[data writeToFile:documentDirectory options:NSDataWritingAtomic error:nil];
NSLog(#"Array length: %d",[anArray count]); //Always got array count zero.
}
-(IBAction)readPlist
{
NSString *filePath = documentDirectory;
NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(#"The array is: %#",array); // it shows that there is nothing in the array
}
#end
I wrote the class for writing .plist files originally but I knew later that I cant store objects in .plist file so I tried so that with archive, thats why the method name have plist in it.
Thank you in advance
Looks like you aren't ever creating an instance of p to add to the array. Try:
Person *p = [[Person alloc] init];
p.name = textName.text;
p.age = [textAge.text intValue];
[anArray addObject:p];
your index limit was also wrong in this loop
for (int i=0; i<[anArray count]; i++) {
NSLog(#"Name: %#", [[anArray objectAtIndex:i] name]);
}
you should really have been seeing a couple of different crashes...
Try adding this in viewDidLoad
[[NSFileManager defaultManager] createFileAtPath:documentDirectory contents:nil error:nil];
It looks like you never do this, and using archives to write to files only works if the file already exists (make sure you only do this once, otherwise every time that view is loaded the file will be emptied of all the data in it). And when you do this
if ([self dataFilePath])
It's pointless, because no matter what it always returns yes, whether the file exists or not.
Does your Person class implement NSCoding?
Specifically you need to implement something like the following in Person.m:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) {
return nil;
}
self.name = [decoder decodeObjectForKey:#"name"];
self.age = [decoder decodeObjectForKey:#"age"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.age forKey:#"age"];
}
I've made a container class to store a single tweet. Its initialized by passing in a dictionary object which is a single tweet.
I then store an array of these 'tweets' which I process through to display in a table.
The project is now finished and I am reviewing everything at the moment and I was wondering is there a better way to do this in the future. Is the memory handled correctly. I declare the string member vars with 'copy' and later in the dealloc I use a 'release' rather than just setting them to 'nil'.
Is my init ok or could that be improved?
Tweet.h
#import
#interface Tweet : NSObject
{
NSString * _userName;
NSString * _tweetText;
NSString * _tweetURL;
}
#property (nonatomic, copy) NSString * userName;
#property (nonatomic, copy) NSString * tweetText;
#property (nonatomic, copy) NSString * tweetURL;
- (id) initWithDict:(NSDictionary *)productsDictionary;
#end
Tweet.m
#implementation Tweet
#synthesize userName = _userName;
#synthesize tweetText = _tweetText;
#synthesize tweetURL = _tweetURL;
- (id) initWithDict:(NSDictionary *)productsDictionary
{
NSDictionary *aDict = [productsDictionary objectForKey:#"user"];
self.userName = [aDict objectForKey:#"screen_name"];
self.tweetText = [productsDictionary objectForKey:#"text"];
NSRange match;
match = [self.tweetText rangeOfString: #"http://"];
if (match.location != NSNotFound)
{
NSString *substring = [self.tweetText substringFromIndex:match.location];
NSRange match2 = [substring rangeOfString: #" "];
if (match2.location == NSNotFound)
{
self.tweetURL = substring;
}
else
{
self.tweetURL = [substring substringToIndex:match2.location];
}
}
else
{
self.tweetURL = nil;
}
return self;
}
-(void) dealloc
{
[self.tweetText release];
[self.tweetURL release];
[self.userName release];
[super dealloc];
}
#end
Many Thanks,
Code
At first sight, I see no inherent flaws here. That looks fine. I would prefer to do:
-(void) dealloc
{
[_tweetText release];
[_tweetURL release];
[_userName release];
[super dealloc];
}
But what you do is good as well.
So in my model I have the following code... I am successfully able to return each individual value. I want to know how am I able to return the entire speakerTable []... Maybe some advice. Thanks!
typedef struct {
NSUInteger speakerID;
NSString * speakerName;
NSString * speakerPosition;
NSString * speakerCompany;
} SpeakerEntry;
static const SpeakerEntry speakerTable [] =
{
{0, #"name", #"position", #"company"},
{1, #"name", #"position", #"company"},
{-1, nil, nil, nil}
};
This works correctly...
-(NSString *) stringSpeakerCompanyForId:(NSUInteger) identifier{
NSString * returnString = nil;
if ([self helpCount] > identifier) {
returnString = speakerTable[identifier].speakerCompany;
}
return returnString;
}
This does not work at all..
-(id) getSpeaker{
//if ([speakerTable[0].speakerName isKindOfClass:[NSString class]])
// NSLog(#"YES");
NSArray * myArray3 = [NSArray arrayWithArray:speakerTable];
return myArray3;
}
arrayWithArray expects an NSArray, not a C array.
The first one works because you are using it like a C array.
Alternatively - don't use a struct, use an object instead:
Create a class called Speaker.
In Speaker.h
#interface Speaker : NSObject {}
#property (nonatomic, assign) NSUinteger id;
#property (nonatomic, copy) NSString name;
#property (nonatomic, copy) NSString position;
#property (nonatomic, copy) NSString company;
- (void)initWithId:(NSUInteger)anId name:(NSString *)aName position:(NSString *)aPosition company:(NSString *)aCompany;
#end
in Speaker.m
#import "Speaker.h"
#implementation Speaker
#synthesize id, name, position, company;
- (void)initWithId:(NSUInteger)anId name:(NSString *)aName position:(NSString *)aPosition company:(NSString *)aCompany {
if (!([super init])) {
return nil;
}
id = anId;
NSString name = [[NSString alloc] initWithString:aName];
NSString position = [[NSString alloc] initWithString:aPosition];
NSString company = [[NSString alloc] initWithString:aCompany];
return self;
}
- (void)dealloc {
[name release];
[position release];
[company release];
[super dealloc];
}
#end
And now in your calling code you can create an immutable array of speakers with:
Speaker *speaker0 = [[Speaker alloc] initWithId:0 name:#"name0" position:#"position0" company:#"company0"];
Speaker *speaker1 = [[Speaker alloc] initWithId:1 name:#"name1" position:#"position1" company:#"company1"];
Speaker *speakerNull = [[Speaker alloc] initWithId:-1 name:nil position:nil company:nil];
NSArray *speakerArray [[NSArray arrayWithObjects: speaker0, speaker1, speakerNull] retain]
[speaker0 release];
[speaker1 release];
[speakerNull release];
note: this is typed straight in, so feel free to mention/correct typos or errors
The method arrayWithArray takes in an NSArray as an argument, not a C array.
I create a NSMutableString called mutableScoreHolder outside my do-while loop.
I alloc it, 'copy' it into an other object called a 'Match' that contains a NSString.
When I build and analyze the code it reports a potential memory leak of my mutableScoreHolder.
NSString * scoreHolder;
NSMutableString * mutableScoreHolder;
count = 1;
numMatchCounter = 0;
int startOfScoreIndex = 0;
int endOfScoreIndex = 0;
int numberOfGoals=0;
do
{
match = [substring rangeOfString: #"points_bg"];
if (match.location == NSNotFound)
{
break;
}
if ((match.location + match.length) > ([substring length]))
{
break;
}
substring = [substring substringFromIndex:(match.location + match.length)];
match2 = [substring rangeOfString: #">"];
startOfScoreIndex = match2.location+1;
match2 = [substring rangeOfString: #"<"];
endOfScoreIndex = match2.location;
if ((count % 2) == 1)
{
scoreHolder = [substring substringWithRange:NSMakeRange(startOfScoreIndex, (endOfScoreIndex-startOfScoreIndex))];
mutableScoreHolder = [[NSMutableString alloc] initWithString:scoreHolder];
numberOfGoals += [scoreHolder intValue];
}
else
{
Match *aMatch = [theMatchScoresArray objectAtIndex:(numMatchCounter)];
numberOfGoals += [[substring substringWithRange:NSMakeRange(startOfScoreIndex, (endOfScoreIndex-startOfScoreIndex))] intValue];
[scoreHolder stringByAppendingString:#" - "];
[mutableScoreHolder appendString:#" - "];
[scoreHolder stringByAppendingString:[substring substringWithRange:NSMakeRange(startOfScoreIndex, (endOfScoreIndex-startOfScoreIndex))]];
[mutableScoreHolder appendString:[substring substringWithRange:NSMakeRange(startOfScoreIndex, (endOfScoreIndex-startOfScoreIndex))]];
aMatch.theScore = [mutableScoreHolder copy];
aMatch.matchGoalCount = numberOfGoals;
numMatchCounter++;
numberOfGoals=0;
}
count++;
}
while ( match.length != 0 );
Here is my Match Object Class.
#interface Match : NSObject
{
NSString *teamName1;
NSString *teamName2;
NSString *theScore;
int matchGoalCount;
NSMutableArray *scorersArray;
NSMutableArray *scorerOrderArray;
NSString *matchStatus;
NSString *matchDate;
bool matchNotStarted;
}
#property(nonatomic) int matchGoalCount;
#property(nonatomic) bool matchNotStarted;
#property(nonatomic, retain) NSString *teamName1;
#property(nonatomic, retain) NSString *teamName2;
#property(nonatomic, retain) NSString *theScore;
#property(nonatomic, retain) NSString *matchStatus;
#property(nonatomic, retain) NSString *matchDate;
#property(nonatomic, retain) NSMutableArray *scorersArray;
#property(nonatomic, retain) NSMutableArray *scorerOrderArray;
-(id)init;
#end
#import "Match.h"
#implementation Match
#synthesize teamName1;
#synthesize teamName2;
#synthesize theScore;
#synthesize scorersArray;
#synthesize matchDate;
#synthesize matchStatus;
#synthesize matchGoalCount;
#synthesize scorerOrderArray;
#synthesize matchNotStarted;
-(id)init
{
//NSLog(#"****** MATCH INIT ******");
self = [super init];
scorersArray =[[NSMutableArray alloc] init];
scorerOrderArray =[[NSMutableArray alloc] init];
return self;
}
-(void) dealloc
{
[scorersArray release];
[scorerOrderArray release];
[teamName1 release];
[teamName2 release];
[theScore release];
[matchStatus release];
[matchDate release];
[scorersArray release];
[scorerOrderArray release];
[super dealloc];
}
#end
I do find there there is a string leaked when i run instruments checking for leaks. So I think this 'potential leak' might be the leak I see.
Because the scores has a retain
#property(nonatomic, retain) NSString *theScore;
And there is a string copied into it
aMatch.theScore = [mutableScoreHolder copy];
Could that give a retain count of 2? And so then leak?
Sorry for the complicated question! Has my head spinning trying to get my head around it.
Thanks
-Code
You're definitely leaking here, for 2 separate reasons.
The first is you're alloc/init'ing an NSMutableString and stuffing it into mutableScoreHolder. This is a local variable, and as soon as this value goes out of scope (or gets replaced the next time a new array is created) the old value is leaked. This should be an autoreleased value instead, as in [NSMutableString stringWithString:scoreHolder].
The second is you're copying the string and stuffing the resulting value into a property. What you should do is redeclare that property as copy
#property(nonatomic, copy) NSString *theScore;
and then just assign mutableScoreHolder to that property directly
aMatch.theScore = mutableScoreHolder
With your existing code, you copy the array, and then the property retains it. With this change, the property copies it directly, and no extra retains are used.
Note, in general it's a good idea to declare properties with supported types as copy. This includes things like NSString, NSArray, NSDictionary, etc. If you're assigning an already-immutable object to the property, the copy falls back to retain instead and there's no performance hit. But in situations like yours where you're assigning mutable objects, it will copy it as appropriate and keep an immutable snapshot in the property.
And there is a string copied into it
aMatch.theScore = [mutableScoreHolder copy];
Could that
give a retain count of 2? And so then
leak?
exactly. You could change the property of theScore from retain to copy to fix this. THen you can use aMatch.theScore = mutableScoreHolder;
and there is (at least) one other leak:
mutableScoreHolder = [[NSMutableString alloc] initWithString:scoreHolder];
this gets never released.