If I use this to set up NSData then the writeData method crashes.
NSString *test = #"The quick brown fox jumped over the lazy dog\r\n";
NSData *data = [test dataUsingEncoding:NSUTF8StringEncoding];
[asyncSocket writeData:data withTimeout:10 tag:4];
However if I use this one then it works... but I need the NSString so I can enter a formatted string to send...
char bytes[] = "The quick brown fox jumped over the lazy dog\r\n";
NSData* data = [[NSData alloc] initWithBytes:bytes length:sizeof(bytes)];
[asyncSocket writeData:data withTimeout:10 tag:4];
So what did I do wrong?
The NSString and the NSDate were not setup with alloc and init so they were gone when they got to the write data. I changed the NSDate to alloc and init and all works well now. These idea came from the several people that answered this. thanks for the help!
The latter is null terminated, the former is not. This is probably the issue.
I know its bit late but I thought it would be good to know why this works - When you call writeData method with your data buffer, the asyncsocket code creates a new object by sublassing NSObject - AsyncWritePacket using the data passed down. It then uses retain and takes ownership of the data buffer you are passing hence why you dont want to release it.
#implementation AsyncWritePacket
- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i
{
if((self = [super init]))
{
buffer = [d retain];
timeout = t;
tag = i;
bytesDone = 0;
}
return self;
#end
Related
I have 100 images in my resource bundle named like image1.jpg,image2.jpg.
Basically what i am trying to do is create path names to those images dynamically inside a for loop.
While testing in simulator,the images loaded fine and the app did not crash.But while testing the app with instruments i was shocked to see the heavy memory leak that was happening while i was creating the path1 object.
I am pasting the entire method here for reference
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
for(int i=1 ; i<100 ; i++){
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSBundle mainBundle] pathForResource:str ofType:#"jpg"];
[self.arrayImages addObject:path1];
}
}
return self;
}
As i have not made use of any alloc inside the loop i dont have any ownership and hence no right to release the object.What is the reason for this memory leak??
Kindly explain the problem and provide the necessary solution in order to fix it..
As always,any help is highly appreciated..
arrayImages is retaining path1, and so if you do not release arrayImages it will leak. How are you creating arrayImages, and are you releasing it anywhere?
Edited based on comments:
Make sure you release arrayImages in your -dealloc method like so: [arrayImages release]; (note the lack of self).
There is no leak in the code you've shown.
There are (at least) two possibilities:
You have a leak in code you didn't paste into your question
Everything is fine and Instruments gave you a false-positive
Your loop will create a lot of autoreleased variables. These won't be deallocated until after the loop has finished, but that's how it's supposed to work.
The reason for the leak would be this line right here:
NSString *str = [NSString stringWithFormat:#"Century%d",i];
By using convenience methods in Objective-C, what happens in the background is the following:
NSString *str = [[[NSString alloc] initWithFormat:#"Century%d", i] autorelease];
Not using alloc/init to create a weak reference is a misconception. You are always the owner of a created object, no matter how you create it. The convenience method simply does the alloc/init and autoreleases it for you.
Here's what I would suggest you do to avoid leaking memory:
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
for(int i = 1 ; i < 100 ; i++) {
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSString alloc] initWithString:[[NSBundle mainBundle] pathForResource:str ofType:#"jpg"]];
[self.arrayImages addObject:path1];
[path1 release];
}
[tmpPool drain];
}
return self;
}
Let me know if this works better for you.
-EDIT- Allocating the path1 object and releasing it after adding to arrayImages.
I'm working on a bible iPhone app. Here are the basics of what I'm doing so far:
My XMLParser parses an xml file and creates a Bible object
Specifically, the xml is stored in each Chapter object.
Once parsing is done, the viewController grabs a Chapter from the Bible and displays its innerHtml in a UIWebview.
This works, but whenever I try to access the Bible object outside of -(void)viewDidLoad; it either gives me a BAD_ACCESS error, or the results for what I'm asking for is unreadable. I think this is a memory management problem..
Here's what I'm doing in the viewController
- (void)viewDidLoad
{
[super viewDidLoad];
//Create parser and XML data object.
//Then, parse that data
finalBible = [[Bible alloc]init];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"KJV" ofType:#"html"];
NSData *myData = [NSData dataWithContentsOfFile:filePath];
//INT I WANTED TO USE TO ITERATE WHEN BUTTON WAS PRESSED TO ADVANCE TO NXT CHAPTER
chapToShow = 2;
parser = [[XMLParser alloc] init];
nsParser = [[NSXMLParser alloc] initWithData:myData];
//set delegate for NSXMLParser
[nsParser setDelegate:parser];
//PARSE THE XML FILE AND BUILD BIBLE OBJECT - PARSED OK!!
if ([nsParser parse]) {
NSLog(#"Parsed with no errors!! :)");
}else{
NSLog(#"Error parsing document");
}
//IT SEEMS MY PROBLEMS MAY BE IN THIS TRADE OFF.
//I WANT TO STORE THE PARSER'S BIBLE IN THIS NEW BIBLE OBJECT.
finalBible = [parser getBible];
//Test querying bible via pullVerse method - IT WORKS!!
NSLog(#"%#",[finalBible pullVerse:#"65.021.000"]);
NSString *firstChap = [[[[finalBible getTestament:0]getBook:#"Genesis"]getChapterWithInt:3]getInnerHtml];
//Try and load Genesis 1 - THIS WORKS!!
NSLog(#"...Loading Genesis 1...");
[bibleView loadHTMLString:firstChap baseURL:nil];
//LOADING THE VERSION WORKS HERE!!
NSLog(#"Version = %#", [finalBible getVersion]);
}
- (IBAction)buttonPressed:(id)sender {
NSLog(#"Now reading chapter %d", chapToShow);
//HERE I'M TRYING TO GET THE BIBLE VERSION BUT THE APP CRASHES AS A RESULT
NSLog(#"Testing the bible: Version = %# \n OK", [finalBible getVersion]);
//NOTE: I've even tried [[parser getBible] getVersion] and it still doesn't work.
// I don't release the parser till the view's dealloc method, so I'm not sure why I
// can't access it here...
}
Of course, I'd be happy to post any other code. I just didn't want to over-stuff the page with code, so I pasted only where I think the problem lies, or at least where it's occurring.
You allocate finalBible, but then you set finalBible = [parser getBible] so the previous allocation is pointless. Also, it appears as though [parser getBible] returns an autoreleased object, so you should call [[parser getBible] retain] to make sure it does not leave memory.
This is exactly what you need to do in your .h file add this line:
#property(nonatomic, retain) Bible finalBible;
Then in your .m file add this line at the top:
//This generates the methods (get, set) for your instance variable
#synthesize finalBible;
Then drop this line:
finalBible = [[Bible] alloc]init];
This is because if you keep it, there will be a memory leak right here:
finalBible = [parser getBible];
Because now you are pointing to a new memory location, and the previous memory location had an object with a retain count of 1 provided by alloc init, and since there will no longer be any reference to this object it will never be released causing a memory leak.
Although if you use this line:
self.finalBible = [parser getBible];
Because that uses the setter, a setter releases the previous value and retains the new one, so there would not be a memory leak, but it would still be pointless to allocate that object.
Since getBible does not have the new, alloc or init keyword it should return an autoreleased object, which is why the object is released in the next run loop (not guaranteed but most likely), which is why you cannot access it outside of the viewDidload() method.
You can resolve this problem by making "finalBible" variable a property of the class using
#property(nonatomic, retain) Bible finalBible; //this is in the .h file
#synthesis finalBible; // at the top of .m file
All the reference to "finalBible" should be made as "self.finalBible"
NSURL *xmlUrl = [[NSURL alloc] initWithString:#"http://www.xml-document.xml"];
NSString *converted = [[NSString alloc] initWithContentsOfURL:xmlUrl encoding:NSISOLatin1StringEncoding error:nil];
converted = [converted stringByReplacingOccurrencesOfString:#"&" withString:#"&"];
converted = [converted stringByDecodingXMLEntities];
The last line takes up 98.3% of the memory in Instruments > Leaks.
And it's smashing my Log window with:
__NSAutoreleaseNoPool(): Object 0x6d10ba0 of class UIView autoreleased with no pool in place - just leaking
Why? I think that method has worked good before...
After some more googling I found that it has to be because of these methods:
[self performSelectorInBackground:#selector(load) withObject:loadingIndicator];
[self performSelectorInBackground:#selector(getEvents) withObject:nil];
So I've tried allocating an NSAutoReleasePool and the release it after the work is done in the methods.
Now i receive EXC_BAD_ACCESS message.
This did not happended before I decided to run those methods in the background. So what's the problem here?
I have very frustrating problem.
When I try to release NSKeyedUnarchiver object after decoding an NSArray, "EXC_BAD_ACCESS" error occurs.
But when I don't release it or decode other object (e.g. NSString) everything go well.
I don't understand it... For me, it looks like "decodeObjectForKey" method changes something in "decoder" object (but not allways?!). And in debugger, the only variable which changes after calling this method is "_replacementMap". But I have no idea how to fix this bug.
I hope you can help me.
Here is sample code:
+ (NSArray *)decodeArticles {
NSString *archivePath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"Articles.archive"];
NSData *decoderData = [[NSData alloc] initWithContentsOfFile:archivePath];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:decoderData];
NSArray *savedArticles = [[decoder decodeObjectForKey:#"articles"] copy];
if (!savedArticles) {
savedArticles = [[NSArray alloc] init];
}
[decoder finishDecoding];
//[decoder release];
return savedArticles;
}
A few more things:
1. need to release the decoderData; should be able to do this after initializing decoder; think the static analyzer should point this out.
2. try moving [decoder finishDecoding]; [decoder release]; before the if block
3. convention might suggest you return an autoreleased object since the implementation is a class method. you might consider adding a path argument to the method.
4. change copy to autorelease.
5. the objects in your array can definitely affect the encoding process. which may explain why some "standard" objects encode and decode the way you would expect.
I need to save my own created class to file, I found on the internet, that good approach is to use NSKeyedArchiver and NSKeyedUnarchiver
My class definition looks like this:
#interface Game : NSObject <NSCoding> {
NSMutableString *strCompleteWord;
NSMutableString *strWordToGuess;
NSMutableArray *arGuessedLetters; //This array stores characters
NSMutableArray *arGuessedLettersPos; //This array stores CGRects
NSInteger iScore;
NSInteger iLives;
NSInteger iRocksFallen;
BOOL bGameCompleted;
BOOL bGameOver;
}
I've implemented methods initWithCoder: and encodeWithCoder: this way:
- (id)initWithCoder:(NSCoder *)coder
{
if([coder allowsKeyedCoding])
{
strCompleteWord = [[coder decodeObjectForKey:#"CompletedWord"] copy];
strWordToGuess = [[coder decodeObjectForKey:#"WordToGuess"] copy];
arGuessedLetters = [[coder decodeObjectForKey:#"GuessedLetters"] retain];
// arGuessedLettersPos = [[coder decodeObjectForKey:#"GuessedLettersPos"] retain];
iScore = [coder decodeIntegerForKey:#"Score"];
iLives = [coder decodeIntegerForKey:#"Lives"];
iRocksFallen = [coder decodeIntegerForKey:#"RocksFallen"];
bGameCompleted = [coder decodeBoolForKey:#"GameCompleted"];
bGameOver = [coder decodeBoolForKey:#"GameOver"];
}
else
{
strCompleteWord = [[coder decodeObject] retain];
strWordToGuess = [[coder decodeObject] retain];
arGuessedLetters = [[coder decodeObject] retain];
// arGuessedLettersPos = [[coder decodeObject] retain];
[coder decodeValueOfObjCType:#encode(NSInteger) at:&iScore];
[coder decodeValueOfObjCType:#encode(NSInteger) at:&iLives];
[coder decodeValueOfObjCType:#encode(NSInteger) at:&iRocksFallen];
[coder decodeValueOfObjCType:#encode(BOOL) at:&bGameCompleted];
[coder decodeValueOfObjCType:#encode(BOOL) at:&bGameOver];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
if([coder allowsKeyedCoding])
{
[coder encodeObject:strCompleteWord forKey:#"CompleteWord"];
[coder encodeObject:strWordToGuess forKey:#"WordToGuess"];
[coder encodeObject:arGuessedLetters forKey:#"GuessedLetters"];
//[coder encodeObject:arGuessedLettersPos forKey:#"GuessedLettersPos"];
[coder encodeInteger:iScore forKey:#"Score"];
[coder encodeInteger:iLives forKey:#"Lives"];
[coder encodeInteger:iRocksFallen forKey:#"RocksFallen"];
[coder encodeBool:bGameCompleted forKey:#"GameCompleted"];
[coder encodeBool:bGameOver forKey:#"GameOver"];
}
else
{
[coder encodeObject:strCompleteWord];
[coder encodeObject:strWordToGuess];
[coder encodeObject:arGuessedLetters];
//[coder encodeObject:arGuessedLettersPos];
[coder encodeValueOfObjCType:#encode(NSInteger) at:&iScore];
[coder encodeValueOfObjCType:#encode(NSInteger) at:&iLives];
[coder encodeValueOfObjCType:#encode(NSInteger) at:&iRocksFallen];
[coder encodeValueOfObjCType:#encode(BOOL) at:&bGameCompleted];
[coder encodeValueOfObjCType:#encode(BOOL) at:&bGameOver];
}
}
And I use these methods to archive and unarchive data:
[NSKeyedArchiver archiveRootObject:currentGame toFile:strPath];
Game *currentGame = [NSKeyedUnarchiver unarchiveObjectWithFile:strPath];
I have two problems.
1) As you can see, lines with arGuessedLettersPos is commented, it's because every time I try to encode this array, error comes up(this archiver cannot encode structs), and this array is used for storing CGRect structs.
I've seen solution on the internet. The thing is, that every CGRect in the array is converted to an NSString (using NSStringFromCGRect()) and then saved. Is it a good approach?
2)This is bigger problem for me. Even if I comment this line and then run the code successfully, then save(archive) the data and then try to load (unarchive) them, no data is loaded. There aren't any error but currentGame object does not have data that should be loaded.
Could you please give me some advice? This is first time I'm using archivers and unarchivers.
Thanks a lot for every reply.
The problem with loading and saving solved another way...
Instead of implementing - (id)initWithCoder:(NSCoder )coder and - (void)encodeWithCoder:(NSCoder)coder I used this solution:
NSMutableData *data = [NSMutableData alloc];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self.strCompleteWord forKey:#"CompleteWord"];
[archiver encodeObject:self.strWordToGuess forKey:#"WordToGuess"];
[archiver encodeObject:self.arGuessedLetters forKey:#"GuessedLetters"];
//[coder encodeObject:self.arGuessedLettersPos forKey:#"GuessedLettersPos"];
[archiver encodeInteger:self.iScore forKey:#"Score"];
[archiver encodeInteger:self.iLives forKey:#"Lives"];
[archiver encodeInteger:self.iRocksFallen forKey:#"RocksFallen"];
[archiver encodeBool:self.bGameCompleted forKey:#"GameCompleted"];
[archiver encodeBool:self.bGameOver forKey:#"GameOver"];
[archiver finishEncoding];
[data writeToFile:strPath atomically:YES];
[data release];
and
NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:strPath];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
self.strCompleteWord = [[unarchiver decodeObjectForKey:#"CompletedWord"] copy];
self.strWordToGuess = [[unarchiver decodeObjectForKey:#"WordToGuess"] copy];
self.arGuessedLetters = [[unarchiver decodeObjectForKey:#"GuessedLetters"] retain];
//self.arGuessedLettersPos = [[unarchiver decodeObjectForKey:#"GuessedLettersPos"] retain];
self.iScore = [unarchiver decodeIntegerForKey:#"Score"];
self.iLives = [unarchiver decodeIntegerForKey:#"Lives"];
self.iRocksFallen = [unarchiver decodeIntegerForKey:#"RocksFallen"];
self.bGameCompleted = [unarchiver decodeBoolForKey:#"GameCompleted"];
self.bGameOver = [unarchiver decodeBoolForKey:#"GameOver"];
[unarchiver finishDecoding];
[data release];
And this works totally fine :)
I might be missing it, but I don't see any obvious bugs in this code.
Here are some ideas that might help:
Add some NSLog statements, and watch the debug output (open with command-shift-R in xcode) to see if your encode/decode methods are actually being called.
Check that the archive file is saved: Running in the simulator, you can save to any local path you want, such as /tmp/my_archive_file. Try to save to that file, and see if (a) the file exists with the right timestamp, and (b) you print out the file, you can see some recognizable strings (like "RocksFallen") in amongst the binary gooblygoo.
I also don't think it's necessary to check for allowsKeyed(En)coding since you know that's always going to be true when you're explicitly using NSKeyed(Un)archiver to do your dirty work for you. So you can throw away the other half of your code.
About coding those arrays of CGRects: I don't think you can directly add structs to an NSMutableArray, right? So they must be some kind of object, which means you can add NSCoding support to that object. If it's not your own object, you can subclass it and add that protocol, or try adding it as a category.
Thanks for reply Tyler.
I looked in the saved file and there are recognizable strings like RocksFallen and so on, also there are some recognizable values that might be saved in string variables.. So this seems to be good.
I tried to put some NSLogs in the code and not everything seems to be good. So...
When I launch simulator for the first time, there's no archive file, yes it is obvious, because nothing is saved. Then I change something and exit the application. When the application is being terminated, data might be archived, everything's fine. NSLog statements are written in the console, file is on the disk. But what is strange is that when I launch the application then, decode method(initwithcoder) is not being called.. Don't know why. Then I exit the simulator and run the simulator again. When I run it again, decode method is being called at the launchtime. But only at the first launchtime after running simulator, when I then work with simulator like exit the app and run it again, initWithCoder method is not being called and that's very strange for me..
So there are problems unarchiving it i think.
The best way to archive structs (that are not holding any pointers) with would be to wrap the values in an NSData block.
here, i'm dumping a struct array to the archiver:
for (int i = 0; i < items; i++) {
NSString *keyValue = [NSString stringWithFormat:#"YourStruct%i", i ];
NSData *dataset = [NSData dataWithBytes:(void*)&activeprofile[i] length:(sizeof(profileset))];
[encoder encodeObject:dataset forKey:keyValue]; // makes a nice, solid data block
}
and reading the data back in:
for (int i = 0; i < items; i++) {
NSString *keyValue = [NSString stringWithFormat:#"YourStruct%i", i ];
NSData *dataset = [decoder decodeObjectForKey:keyValue];
[dataset getBytes:&activeprofile[i] length:sizeof(profileset)];
}
There you have it: easy as pie and extremely flexible with datatypes!
A similar method for non-keyed archiving can be found here: http://borkware.com/quickies/one?topic=NSCoder Though I suggest sticking with keyed archiving, as NSArchive is not supported on the iPhone and has been classified as legacy code.