I'm doing some work using the RedPark Serial Cable for iOS. This is a cable that allows serial communication between an iOS device and another device like a microcontroller.
There is a method that comes with the RedPark SDK that reads the bytes available on the Serial Line. This method is called anytime the cable receives informatino.
(void)readDataBytes(UInt32 bytes) {
NSString *s = [[NSString alloc] initWith...bytes];
NSString *editedString = [self extractSubStringMethod:s];
[myArray addObject:editedString];
}
The microcontroller is sending the information in the following format
<msg>xxxxxxxxxxxxx</msg>
I want to be able to extract the x's from the message (which is taken in as an NSString). At the moment I'm using NSRange to extract everything after position 4 (the first x) and before the final "< /msg>" I'm not convinced it works and was wondering is there any other ideas?
Finally, I have an NSThread which is running alongside this, I have the messages being added to an NSMutableArray. So what I want is, the NSThread to be manipulating/displaying the message information when there is a message received from the cable. I have something like the following
//Thread method,
while([myArray count] > 0) //Don't believe this is neccesary but its in anyway
{
for(int i = 0; i < [myArray count]; i++){
NSString *string = [myArray objectAt(i)];
[self displayString:string];
[myArray removeObjectAt(i);
}
}
I believe it's crashing around the above... [self displayString:string] just sets the value of a label, something like
-(void)displayString(NSString *string) {
label.text = [string charAt(1)];
}
The code above is just from memory as I've left my Mac at home and I'm in work.
Any suggestions/help would be appreciated
Instead of
while([myArray count] > 0) //Don't believe this is neccesary but its in anyway
{
for(int i = 0; i < [myArray count]; i++){
NSString *string = [myArray objectAt(i)];
[self displayString:string];
[myArray removeObjectAt(i);
}
}
try like this:
while ([myArray count] > 0)
{
[self displayString: myArray[0]];
[myArray removeObjectAtIndex: 0];
}
To address your first concern, [string substringWithRange:NSMakeRange(5, string.length - 6)] should be sufficient and should work without any issues. You could use an NSRegularExpression to match the content inside the tags, but that would be overkill for such a simple task (and would have a performance hit).
Secondly, UIKit methods must only be called from the main thread (see documentation):
Note: For the most part, UIKit classes should be used only from an application’s main thread. This is particularly true for classes derived from UIResponder or that involve manipulating your application’s user interface in any way.
You appear to be setting the text of a label in a different thread. This is what is causing the crash. You need to call your displayString: method like this:
[self performSelectorOnMainThread:#selector(displayString:) withObject:string waitUntilDone:YES];
This will execute the method on the main thread instead of on a background thread.
You must not update UI on secondary thread.
Mutable classes are not thread safe.
while([myArray count]){
NSString *string = [myArray objectAt(0)];
[self performSelectorOnMainThread:#selector(displayString:) withObject:string waitUntilDone:NO];
[myArray removeObjectAt(0);
}
Related
I'm using this code to load content to NSArray and it seem to work fine however Instrument to detect leaks point that there is a problem that I can't put my finger on:
- (void) loadPlan: (NSString *) fName
{
short j1;
fName= [NSString stringWithFormat:#"/%#", fName];
[self NewCase];
NSArray *arrayPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDirectory = [arrayPaths objectAtIndex:0];
NSString *filePath = [docDirectory stringByAppendingString:fName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if (!fileExists) return;
NSString *fileContents = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding error:nil];
NSArray *chunks = [fileContents componentsSeparatedByString: #"#"];
for (i = 0; i <= 100; i++)
{
InputV[i] = [[chunks objectAtIndex: i+5] doubleValue];
}
...
for (j1 = 0; j1 <= 10; j1++)
{
GroupMode[j1] = [[chunks objectAtIndex: 206+j1] retain];
}
...
}
and on a init method someplace i have:
for (j1 = 0; j1 <= 10; j1++)
{
GroupMode[j1] = [[NSString alloc] initWithFormat:#""];
}
Instrument points to the NSAraay *chunks line code but i'm not sure what's the issue. Do i need to release it at some point ?
I appreciate any help.
In the comments you mention being able to call release. Therefore you are not using ARC and since I noticed you tagged with iphone you are not using GC. This leaves manual memory management.
The problem seems to be that either the chunks array or some chunks themseves are overretained (or underreleased). You are not showing all the code, so it's hard to say.
Make sure you do not retain either of them somewhere else in the code you did not show. Maybe show us the rest of the loadPlan method implementation.
edit: Now that you added more code, I can also expand this answer.
Answer this question: where is the retain call to the chunks matched with a release?
Also what is the declaration of GroupMode? It seems to be just an array of pointers. If so you probably need to release the old value before setting the new one.
Let me try another answer based on what you posted.
I'm assuming GroupMode is an instance variable of some class and is declared like this:
NSString* GroupMode[11];
The second loop in loadPlan should be:
for (j1 = 0; j1 <= 10; j1++)
{
NSString* aChunk = [chunks objectAtIndex: 206+j1];
if ( GroupMode[j1] != aChunk ) {
[GroupMode[j1] release];
GroupMode[j1] = [aChunk retain];
}
}
You should do something similar every time you change an element of GroupMode and you should make sure you release all GroupMode held objects in the dealloc method of that class.
I however suggest you do not use plain arrays and instead switch to using NSArray and/or NSMutableArray.
Take a look at this answer:
"componentsSeparatedByString" Memory leak
The problem is probably that something using the results is over-retaining the stuff from chunks. Instruments is pointing at this line because it's where the memory was first allocated, but it may not be the source of your problem.
I started developing a project in ios-5, I have created database in my application.
Here I create bdgDBArr in appDelegate that contains values like 93,55,68,95,45...
I want to create string like badge_id= #"93,55,68,95,45"
here appDelegate.sb is NSString type and sb1 is NSMutableString type
This is my code
NSMutableString *sb1 = [[NSMutableString alloc] init];
if (![appDelegate.bdgDBArr count]==0) {
for (int i=0; i < [appDelegate.bdgDBArr count]; i++) {
if (!i==0) {
[sb1 appendString:#","];
}
[sb1 appendString:[[appDelegate.bdgDBArr objectAtIndex:i] valueForKey:#"key1"]];
}
}
else {
[sb1 appendString:#""];
}
appDelegate.sb = sb1;
NSLog(#"appDelegate.sb showSB===%#",appDelegate.sb);
[sb1 release]; //error wait_fences: failed to receive reply: 10004003
sb1 = nil;
This code is working perfectly and get the output 93,55,68,45
but at the same time I got this error in NSLog
wait_fences: failed to receive reply: 10004003
Any ideas?
I can't help with your problem but you can — and should — reduce your code down to a one-liner: appDelegate.sb = [appDelegate.bdgDBArr componentsJoinedByString:#","]; which is much more expressive and does the right thing.
And while we're there:
Objective-C makes it rather easy to write code that can be read like prose. Don't break that by using member/variable names like sb or bdgDBArr.
Oh and there is an operator to test for inequality: != use that instead of negating the result of an equality test. Your future self and every other person looking at your code will be thankful.
just to try: use autorelease instead of release. does it change something?
is your property appDelegate.sb used in app delegate with a #syntetise setter/getter, or did you used your own code for the setter? in this case post your code, please.
Hi friends I am using this code
-(void)searchItem:(NSMutableArray*)items
{
if ([[[items objectAtIndex:1]objectForKey:#"Success"]isEqualToString:#"True"]) {
NSMutableArray *searcharr=[[NSMutableArray alloc]init];
for (int i=2; i<[items count]; i++)
{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc]init];
[searcharr addObject:[items objectAtIndex:i]];
NSLog(#"data fetch array is====> /n%#",searcharr);
[pool release];
}
searchItemsArray=[[NSMutableArray alloc]initWithArray:searcharr];
[searcharr release];
searcharr=nil;
}
I have released searchItemsArray in dealloc method.The items array i am getting from a webservice.It contains images and other data.I was using the for loop without NSAutoreleasePool,But when i used the instrument with simulator,i was getting the leak here.I just want to know that the code which i have given here with pool is correct or not.My app was also crashing as i was feeding this data and images in tableview cell.So please help me out.
And one more thing should i always use NSAutorelease pool,while looping ......Thanks
if the method "-(void)searchItem:(NSMutableArray*)items" get's called many times then searchItemsArray will be allocated each time resulting a memory leak. If you call release in the dealloc that means you will release only the instance created at the last call of the method.
you could do something similar to this:
-(void)searchItem:(NSMutableArray*)items
{
if ([[[items objectAtIndex:1]objectForKey:#"Success"]isEqualToString:#"True"])
{
if( nil == searchItemsArray )
searchItemsArray = [[NSMutableArray alloc]init];
else
[searchItemsArray removeAllObjects];
for (int i=2; i<[items count]; i++)
{
[searchItemsArray addObject:[items objectAtIndex:i]];
}
}
If you created the items array with alloc/init then after the call of "-(void)searchItem:(NSMutableArray*)items" you need to release it also.
If in a loop you create many many autoreleased objects that you will not use later then the autorelease pool would be an option.
Every time -autorelease is sent to an object, it is added to the inner-most autorelease pool. When the pool is drained, it simply sends -release to all the objects in the pool.
Autorelease pools are simply a convenience that allows you to defer sending -release until "later". That "later" can happen in several places, but the most common in Cocoa GUI apps is at the end of the current run loop cycle.
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
for(int i = 0; i < 100; i++)
{
NSString *string = [[[NSString alloc] init] autorelease];
/* use the string */
}
[pool drain]; //or release if you don't plan to use the pool later.
The code wasn't compiled sorry for the possible errors.
You can find more info about autorelease pools at this link
I've recently created a new class for my iPhone application which will hold information read from a text file containing the street address and GPS points of points of interest.
The issue though is that whenever I add code to initialize the class my application loads up and the instantly quits with no errors in the console. When I remove it, everything is fine. I simply cannot see anything wrong with the code.
Here is the constructor:
#import "GPSCoordinate.h"
#implementation GPSCoordinate
-(GPSCoordinate*) initWithData:(NSString *)rawData size:(int)size
{
self = [super init];
location = [NSMutableArray arrayWithCapacity:size];
coordinates = [NSMutableArray arrayWithCapacity:(int)size];
NSArray *tokens = [rawData componentsSeparatedByString:#"#"];
for (int i = 0; i < size - 1; i++) {
//Sub tokens
NSString *line = [tokens objectAtIndex:i];
NSArray *lineTokens = [line componentsSeparatedByString:#":"];
//Store address
[location addObject:[lineTokens objectAtIndex:0]];
//Store GPS coords
NSString *coords = [lineTokens objectAtIndex:1];
coords = [[coords stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:#""]
stringByReplacingCharactersInRange:NSMakeRange([coords length]-2, 1) withString:#""];
NSArray *coordsTokens = [coords componentsSeparatedByString:#" "];
CLLocationCoordinate2D coord;
coord.latitude = [[coordsTokens objectAtIndex:0] doubleValue];
coord.longitude =[[coordsTokens objectAtIndex:1] doubleValue];
[coordinates addObject:coords];
[line release];
[lineTokens release];
[coords release];
[coordsTokens release];
}
return self;
}
#end
Here is the call I make to it in another class:
self.gps = [[GPSCoordinate alloc] initWithData:gpsRawData size:[[gpsRawData componentsSeparatedByString:#"#"] count]];
Where am I going wrong with this?
I see a number of problems.
You're not checking the return value of [super init].
You're storing autoreleased arrays in what are presumably ivars (location and coordinates).
You're passing a separate size parameter which is calculated from the rawData outside of the call, but -initWithData: makes the exact same calculation inside the method. The size: parameter seems completely superfluous here.
You're skipping the last token entirely. You should take that for loop and make the condition simply i < size. Alternately if you're targetting iOS 4.0 or above you can turn the entire loop into
[tokens enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSString *line = obj;
// rest of loop body
}];
Since you don't seem to need the index inside the loop, you could also just use a for-in loop (this will work on pre-4.0 iOS devices):
for (NSString *line in tokens) {
// body of loop
}
You're not checking that your data is valid. If a line contains "foo", your program will crash when it tries to access [lineTokens objectAtIndex:1]. Similarly it'll crash if you have the string "foo:" as it tries to remove the first character of the coordinates variable. In fact anything less than 2 characters after the colon will crash. It'll also crash if there's no spaces after the colon.
And finally, all those calls to -release at the end will crash. All 4 of those objects are autoreleased objects, so by calling -release on them now you're simply guaranteeing that the app will crash when the autorelease pool is drained.
You're also storing coords (e.g. the string) in your coordinates array. Presumably you meant to store coord, though you'll need to wrap it in an NSValue in order to store it in an NSArray.
I see several issues.
1) Most fundamentally, you are releasing a lot of objects that you didn't allocate. For example:
NSString *line = [tokens objectAtIndex:i];
....
[line release];
is incorrect. Review the Cocoa Memory Management Rules.
2) Why are you doing [[gpsRawData componentsSeparatedByString:#"#"] count to pass the size to
your initWithData:size: method, when you're just going to have to repeat the -componentsSeparatedByString: call inside your method. Passing a separate "size" doesn't gain you anything, involves a redundant parse of the input, and opens up more possible bugs (what if the caller passes in a "size" that doesn't match the number of "#"s in the input - you aren't handling that error condition).
3) I also see that you are assigning latitude/longitude to CLLocationCoordinate2D coord; but not doing anything with it. Is that deliberate?
Update: I edited the code, but the problem persists...
Hi everyone,
this is my first post here - I found this place a great ressource for solving many of my questions. Normally I try my best to fix anything on my own but this time I really have no idea what goes wrong, so I hope someone can help me out.
I am building an iPhone app that parses a couple of xml files using TouchXML. I have a class XMLParser, which takes care of downloading and parsing the results. I am getting memory leaks when I parse an xml file more than once with the same instance of XMLParser.
Here is one of the parsing snippets (just the relevant part):
for(int counter = 0; counter < [item childCount]; counter++) {
CXMLNode *child = [item childAtIndex:counter];
if([[child name] isEqualToString:#"PRODUCT"])
{
NSMutableDictionary *product = [[NSMutableDictionary alloc] init];
for(int j = 0; j < [child childCount]; j++) {
CXMLNode *grandchild = [child childAtIndex:j];
if([[grandchild stringValue] length] > 1) {
NSString *trimmedString = [[grandchild stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[product setObject:trimmedString forKey:[grandchild name]];
}
}
// Add product to current category array
switch (categoryId) {
case 0:
[self.mobil addObject: product];
break;
case 1:
[self.allgemein addObject: product];
break;
case 2:
[self.besitzeIch addObject: product];
break;
case 3:
[self.willIch addObject: product];
break;
default:
break;
}
[product release];
}
}
The first time, I parse the xml no leak shows up in instruments, the next time I do so, I got a lot of leaks (NSCFString / NSCFDictionary).
Instruments points me to this part inside CXMLNode.m, when I dig into a leaked object:
theStringValue = [NSString stringWithUTF8String:(const char *)theXMLString];
if ( _node->type != CXMLTextKind )
xmlFree(theXMLString);
}
return(theStringValue);
I really spent a long time and tried multiple approaches to fix this, but to no avail so far, maybe I am missing something essential?
Any help is highly appreciated, thank you!
The issue is likely in this line:
[self.mobil addObject:[product copy]];
By calling for a copy of product you're creating a new NSMutableDictionary instance with a retain count of 1. The mobil instance, however, will increment the copy's retain count when you send it the addObject: message, so the retain count of the copy is now 2. Generally speaking, an object is responsible for handling its own object memory, so any time you message setFoo: or addObject:, you can just pass the object directly, even if its autoreleased or you are planning to release it right after the call; it is the receiver's responsibility to retain the object you're passing if it needs to hold onto it.
Because you did not assign the copy to any variable, you do not have a pointer that you can use to decrement the copy's retain count now that you're no longer interested in it, so even if mobil releases the product copy at some point, the copy will never reach a retain count of 0. Your [product release] statement at the end of the for loop releases the original product object, not the copy you created.
Instead, try the following and see if instruments is happier:
[self.mobil addObject:product];
In simple terms, every time you use copy, you also have to use release/autorelease somewhere.
And in this instance, the even easier answer is to not use copy in the first place, since you aren't doing anything with the original version of product after you've copied it.
I fixed the problem myself. It was kind of stupid, but maybe someone might come across the same thing, so I am going to post it here.
1) I had mutable array set up as instance variables like this:
#interface XMLParser : NSObject {
// ...
NSMutableArray *mobil;
// ...
}
#property(nonatomic, retain) NSMutableArray *mobil;
#end
Everytime I wanted to restore new data inside I did:
self.mobil = nil;
Which did not what I wanted to do, so this is the better approach:
[self.mobil removeAllObjects];
2) The dealloc method has to be like this to fix the leaks (because mobil is defined as a property):
-(void)dealloc {
[mobil release];
self.mobil = nil;
}
Whew, that has been a lot of work to find out - hope it saves someone else some time :-)