I have AsyncSocket like this.
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *message = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
[data getBytes:&tdata];
if (tdata > 5) {
if(bheader){
if(!charS){
if([message isEqualToString:#"S"]){
CMSG = message;
charS=YES;
}
}
else{
NSMutableString *tmp = [[NSMutableString alloc] initWithString:#""];
[tmp appendString:CMSG]; <<<<< This is code error at loop 2,
[tmp appendString:message]; the first loop success but second is fail
CMSG = tmp;
[tmp release];
}
}
else{
if (message){
cmessage = [[NSString alloc]initWithFormat:#"%#%#",cmessage,message] ;
}
else
NSLog(#"Error converting received data into UTF-8 String");
cdata++;
if(cdata==idata) {
msgComplete=YES;
}
}
if (msgComplete) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:cmessage forKey:kNotificationMessage];
[notificationCenter postNotificationName:kNotification object:self userInfo:userInfo];
cmessage=#"";
CMSG=#"";
msgComplete=NO;
bheader=YES;
cdata=0;
charS=NO;
[cmessage release];
}
}
[sock readDataToLength:1 withTimeout:-1 tag:0];
}
This code fail at second loop at [tmp appendString:CMSG]; Thread 1: Program received signal "SIGABRT"
How fix this error ?
Thanks gurus,
CMSG is assigned to tmp but tmp is released right afterwards. CMSG needs to be retained throughout the loop.
CMSG = tmp; //<< should be CMSG = [tmp retain];
[tmp release];
Now you have another potential problem. You are using a deprecated method here
[data getBytes:&tdata];
if (tdata > 5) {
getBytes: was deprecated because of a potential buffer overrun and the result of getBytes is not appropriate to use for if(tdata > 5). If you want to check the length of bytes get that that from the NSData object.
I see an error that may or may not be the ultimate problem here. In the final conditional statement, you have:
cmessage=#"";
...
[cmessage release];
I don't think you should be releasing that string. I don't actually know all of the ins and outs of NSString objects that are defined in that way (as opposed to stringWithFormat: or initWithString:), but I don't think that you have ownership of that string object. You shouldn't have to release it.
Remove that release message and see if it helps.
Related
Trying to split the string in to array, but it is giving error "[__NSArrayI componentsSeparatedByString:]: unrecognized selector sent to instance 0x11741b20'". The string contains the value, that comes from first index of array then the string needs to be split and store in array.
This is array value.
mcommarr:(
":comment",
":comment",
":comment"
NSString *strr = [[NSString alloc]init];
strr = [self.mCommArr objectAtIndex:indexVal];
NSArray *arr2 = [str componentsSeparatedByString:#","];
Here is the complete method in which i am using this.
-(void)loadData:(int)indexVal;
{
indexVal=serialIndexVal;
serialIndexVal++;
NSLog(#"arrLike:%d", [self.mArrLike count]);
NSLog(#"arrPid:%d", [self.mArrPid count]);
status = [NSString stringWithFormat:#"get"];
[self.mButtonsStatusDict setObject:status forKey:#"status"];
[self.mButtonsPidDict setObject:[self.mArrPid objectAtIndex:indexVal] forKey:#"pid"];
[self.activityIndicator startAnimating];
#try
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSString *status = [NSString stringWithFormat:#"get"];
[self.mButtonsStatusDict setObject:status forKey:#"status"];
[self.mButtonsPidDict setObject:[self.mArrPid objectAtIndex:indexVal] forKey:#"pid"];
self.mButtonsCommentsDict = [MyEventApi showComments:self.mButtonsPidDict];
self.mButtonsDict = [MyEventApi likeDislike:self.mButtonsUidDict post:self.mButtonsPidDict postStatus:self.mButtonsStatusDict];
dispatch_sync(dispatch_get_main_queue(), ^{
[self.activityIndicator stopAnimating];
NSLog(#"buttons data dict:%#", self.mButtonsDict);
if([self.mButtonsDict count] == 0)
{
NSLog(#"server problem no response");
[self.mArrLike addObject: #"0"];
[self.mArrDislike addObject: #"0"];
}else{
[self.mArrLike addObject: [self.mButtonsDict valueForKey:#"like"]];
[self.mArrDislike addObject: [self.mButtonsDict valueForKey:#"dislike"]];
}
if([self.mButtonsCommentsDict count] == 0)
{
NSLog(#"server problem no response");
[self.mCommArrTot addObject: #"0"];
}
else{
self.dictComm = [self.mButtonsCommentsDict valueForKey:#"comments"];
[self.mCommArr addObject:[self.dictComm valueForKey:#"comment"]];
NSLog(#"count:%d",[self.mCommArr count]);
// NSString *strTot = [NSString stringWithFormat:#"%d",tot];
// [self.mCommArrTot addObject:strTot];
NSLog(#"dictcomm:%#", self.dictComm );
NSLog(#"mcommarr:%#", [self.mCommArr objectAtIndex:indexVal]);
strr = [[NSString alloc]init];
strr = [self.mCommArr objectAtIndex:indexVal];
//NSString *strr = [[NSString stringWithFormat:#"%#", [self.mCommArr objectAtIndex:indexVal]];
// NSArray *arr1 = [self string:strr];
// NSArray *splitArray=[self.mCommArr[0] componentsSeparatedByString:#","];
//[strr componentsSeparatedByString:#","];
// NSLog(#"arrSep:%#", arr1);
//int count = [arr1 count];
//NSLog(#"arrcount:%d", count);
// NSString *strTot = [NSString stringWithFormat:#"%d",count];
// [self.mCommArrTot addObject:strTot];
//NSLog(#"mcommarrtot:%#", [self.mCommArrTot objectAtIndex:indexVal]);
}
// NSLog(#"arrLike:%#", [self.mArrLike objectAtIndex:indexVal]);
// NSLog(#"arrDisLike:%#", [self.mArrLike objectAtIndex:indexVal]);
[self.mPublicFriendTable reloadData];
});
});
}
#catch (NSException *exception) {
NSLog(#"main: Caught %#: %#", [exception name], [exception reason]);
}
#finally {
}
}
It get killed when try to split. Why so, i am not getting. If anyone has faced such situation please guide what is wrong her.
You can split the string into an NSArray like below...
NSString *yourString = #"comment,comment,comment";
NSArray *strArray = [yourString componentsSeparatedByString:#","];
NSLog(#"\n\n Array is ==>> %#",strArray);
"[__NSArrayI componentsSeparatedByString:]:
Your error says you tried to send the above method to NSArray, which doesn't has.
As you want to split the array at index 0. you should probably do as :
NSArray *splitArray=[yourArray[0] componentsSeparatedByString:#","];
Here yourArray is the array that you get from Server.
NSString *strr = [[NSString stringWithFormat:#"%#", [self.mCommArr objectAtIndex:indexVal]];
NSArray *arr2 = [strr componentsSeparatedByString:#","];
I think you are passing str right now, which can be an array (as your error points out).
Let me know the results.
Suppose I have a function
- (NSString *)fullNameCopy {
return [[NSString alloc] initWithFormat:#"%# %#", self.firstName, self.LastName];
}
Can somebody tell me how to call this function, how to assign its value to a new object, and how then to release it, avoiding memory leaks, and bad access.
Would it be like
NSSting *abc = [object fullNameCopy];
// Use it and release
[abc release];
or I should alloc abc string too ?
Update:
The point here, Can I return non-autorelease objects from a function and then release them in the calling function. As per Obj-C function naming conventions, a function name containing alloc or copy should return object assuming that calling function has the ownership.
As in above case, my function "fullNameCopy" return a non-autoreleased abject, and I want to release them in the calling function.
You are right. Since the method name contains the word ‘copy’, Cocoa convention dictates that the method returns an object that is owned by the caller. Since the caller owns that object, it is responsible for releasing it. For example:
- (void)someMethod {
NSString *abc = [object fullNameCopy];
// do something with abc
[abc release];
}
Alternatively, you could use -autorelease instead of -release:
- (void)someMethod {
NSString *abc = [[object fullNameCopy] autorelease];
// do something with abc
}
Refer this post
UPDATE:
- (NSString *)fullNameCopy {
NSString *returnString = [NSString stringWithFormat:#"%# %#", self.firstName, self.LastName]; // Autorelease object.
return returnString;
}
-(void) someFunction {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *fullName = [self fullNameCopy];
[pool release]
}
Like This:
- (NSString *)fullName {
NSString * retVal = [[NSString alloc] initWithFormat:#"%# %#", self.firstName, self.LastName];
return [retVal autoRelease];
}
Then
NSSting *abc = [object fullName];
return [[[NSString alloc] initWithFormat:#"%# %#", self.firstName, self.LastName]autorelease];
I followed this guide here to doing background processing:
http://evilrockhopper.com/2010/01/iphone-development-keeping-the-ui-responsive-and-a-background-thread-pattern/
And the only 1 line of code which was put to background processing was:
sound = flite_text_to_wave([cleanString UTF8String], voice);
But for some reason i got a bad access signal.
Debugging it shows that it crashes on that line too. This is the code I now have in that part. Bear in mind that most of this is just default Flite stuff from the sfoster project which has had no problems before, when it was all together, not separated into 3.
-(void)speakText:(NSString *)text //This is called by my app
{
cleanString = [NSMutableString stringWithString:#""];
if([text length] > 1)
{
int x = 0;
while (x < [text length])
{
unichar ch = [text characterAtIndex:x];
[cleanString appendFormat:#"%c", ch];
x++;
}
}
if(cleanString == nil)
{ // string is empty
cleanString = [NSMutableString stringWithString:#""];
}
//The next line i've put in from the link
[self performSelectorInBackground:#selector(backgroundTextToSpeech) withObject:nil];
}
-(void)backgroundTextToSpeech {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
//The following line is the one it crashes on
sound = flite_text_to_wave([cleanString UTF8String], voice);
[self performSelectorOnMainThread:#selector(backToForegroundTextToSpeech) withObject:nil waitUntilDone:YES];
[pool release];
}
-(void)backToForegroundTextToSpeech {
NSArray *filePaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *recordingDirectory = [filePaths objectAtIndex: 0];
// Pick a file name
tempFilePath = [NSString stringWithFormat: #"%#/%s", recordingDirectory, "temp.wav"];
// save wave to disk
char *path;
path = (char*)[tempFilePath UTF8String];
cst_wave_save_riff(sound, path);
// Play the sound back.
NSError *err;
[audioPlayer stop];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:tempFilePath] error:&err];
[audioPlayer setDelegate:self];
[audioPlayer prepareToPlay];
}
Any ideas what i've done wrong/what i can do differently to stop this happening?
EDIT: A picture of the debugger after changing it round with some code posted below:
first thing that jumps out:
your class needs to hold a retain count for cleanString.
typically, this is accomplished via a retained (or copied, which is generally preferable with NSStrings and other concrete/immutable types) property:
#interface MONSpeaker : NSObject
{
NSString * cleanString;
}
#property (copy) NSString * cleanString;
#end
#implementation MONSpeaker
#synthesize cleanString;
/* ... */
- (void)dealloc
{
[cleanString release], cleanString = nil;
[super dealloc];
}
-(void)speakText:(NSString *)text // This is called by my app
{
NSMutableString * str = [NSMutableString stringWithString:#""];
if([text length] > 1)
{
int x = 0;
while (x < [text length])
{
unichar ch = [text characterAtIndex:x];
[str appendFormat:#"%c", ch];
x++;
}
}
if(str == nil) // why not check for nil at creation instead?
{ // string is empty
str = [NSMutableString stringWithString:#""];
}
self.cleanString = str;
// The next line i've put in from the link
[self performSelectorInBackground:#selector(backgroundTextToSpeech) withObject:nil];
}
-(void)backgroundTextToSpeech {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// The following line is the one it crashes on
sound = flite_text_to_wave([self.cleanString UTF8String], voice);
[self performSelectorOnMainThread:#selector(backToForegroundTextToSpeech) withObject:nil waitUntilDone:YES];
[pool release];
}
#end
ok - so this isn't 100% thread-safe, but it is idiomatic.
the variant which is more resistant to threading issues would pass the string as an argument to backgroundTextToSpeech:(NSString *)text. backgroundTextToSpeech:(NSString *)text would then create a copy of the argument text (and of course release the copy before pool is destroyed).
You don’t understand how autoreleasing works. You assign the cleanString variable an autoreleased object and then start some processing on background that uses this value. But when the method that started the background processing returns the control to the main run loop, the autoreleased string stored in the cleanString gets deallocated and the background thread runs into a zombie.
You could simplify the code using Grand Central Dispatch:
- (void) startProcessing {
NSString *source = [NSString stringWithWhatever…];
dispatch_queue_t targetQ =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(targetQ, ^{
sound = flite_text_to_wave…;
dispatch_async(dispatch_get_main_queue(), ^{
[self speechProcessingDone];
});
});
}
The advantages are that you don’t have to maintain your own autorelease pool, you don’t have to add extra methods just to have one line executed in background and the block will retain your string, so that you won’t crash.
But you should certainly not use GCD just to get around the autoreleasing issue without knowing what’s happening. Memory management is the base, you must be 100% sure what you are doing.
If this is above your head, make sure you know how memory management works in Cocoa, for example by reading this Objective-C tutorial by Scott Stevenson. You have to do this, there is no way around.
Then come back to the code and you will see that the mutable strings that you store into cleanString are autoreleased, meaning they will get deallocated sometime soon after you leave the current function. After running the selector in background, you exit the current function and the string stored in cleanString gets deallocated. Soon after that the background thread gets to the following line:
sound = flite_text_to_wave([cleanString UTF8String], voice);
But as the object stored in cleanString was already deallocated, you get the crash. The solution is simply to retain the object until you are done with it. You can retain it before running the background thread and release when the background thread ends.
I want to declare an NSString object to use within an alert, but its actual content depends on various factors, determined by some variable. I'm wondering how best to approach this. In most cases I've done something like this:
- (void)info {
NSString *targetString = [[NSString alloc] init];
switch (self.target) {
case 1:
targetString = #"ONE";
break;
case 2:
targetString = #"TWO";
break;
case 3:
targetString = #"THREE";
break;
default:
targetString = #"";
break;
}
NSString *message = [[NSString alloc] initWithFormat:#"Text: %#", targetString];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Info"
message:message
delegate:self
cancelButtonTitle:#"Ok!"
otherButtonTitles:nil];
alert.tag = kInfoAlert;
[alert show];
[alert release];
[targetString release];
[message release];
}
However when I run this through the build analyser, I get messages telling me the string is leaking memory:
First of all it says:
Value stored to 'targetString' during
its initialization is never read
Then:
Potential leak of an object allocated
on line 137 and stored into
'targetString'
These 2 comments are at line 136 and 137, where line 136 is
NSString *targetString = [[NSString alloc] init];
An alternative might be to declare the string as
NSString *targetString;
and set it in each case as
targetString = [NSString stringWithFormat:#"ONE"];
etc
Or even allocing the String in each case in order to release it at the end...
Well, what would be the best approach here?
Thanks,
Michael :)
The reason for your memory leak is because you are needlessly allocating a string with this line
NSString *targetString = [[NSString alloc] init];
and then setting it to a literal object. Define targetString as nil because when you set it to another value like targetString = #"ONE" you are no longer referencing the empty string you allocated and that causes a memory leak. As for your approach of the switch case for determining the value that is fine.
I believe this would be enough:
NSString *targetString = nil;
And you don't need to release targetString then.
How about this instead of the switch:
- (NSString*) stringForIndex: (NSUInteger) index
{
NSParameterAssert(index < 4);
id strings[] = {#"none", #"one", #"two", #"three"};
return strings[index];
}
This problem is probably not specific to MFMailComposeViewController, but that is where I am having the problem...
I am building the NSString
"myEmailString" for the messageBody of
the MFMailComposeViewController and
storing it in an iVar before
displaying the
MFMailComposeViewController as a
modal view controller.
I pass the string into the MFMailComposeViewController, then present it as a modal view controller.
When the modal view controller is dismissed, my iVar becomes invalid,
and the app crashes when I release the emailString iVar in dealloc
Code below, what am I doing wrong?
-(void)buildEmailMessage {
int mySection;
int myRow;
NSString *buildString = [NSString stringWithFormat:#"<b><p>Ten Essentials Check List</b><br />%#</p>", [myList valueForKey:#"listName"]];
for (mySection = 0; mySection < [[fetchedResultsController sections] count]; mySection ++) {
NSString *sectionName = [NSString stringWithFormat:#"<p><b>%# Group</b></p><ul>", [[[fetchedResultsController sections] objectAtIndex:mySection] name]];
buildString = [buildString stringByAppendingString:sectionName];
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:mySection];
for (myRow = 0; myRow < [sectionInfo numberOfObjects]; myRow ++) {
// Get the managedObject
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:myRow inSection:mySection];
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
//Get the related Item object
Item *item = [managedObject valueForKey:#"item"];
NSString *itemName = [NSString stringWithFormat:#"<li>%#</li>", item.itemName];
buildString = [buildString stringByAppendingString:itemName];
}
buildString = [buildString stringByAppendingString:#"</ul>"];
}
myEmailString = [NSString stringWithString:buildString];
NSLog(#"email string = :\n%#", myEmailString);
[self showPicker];
}
#pragma mark -
#pragma mark Send Mail
-(void)showPicker {
// This code can run on devices running iPhone OS 2.0 or later
// The MFMailComposeViewController class is only available in iPhone OS 3.0 or later.
// So, we must verify the existence of the above class and provide a workaround for devices running
// earlier versions of the iPhone OS.
// We display an email composition interface if MFMailComposeViewController exists and the device can send emails.
// We launch the Mail application on the device, otherwise.
NSLog(#"Checking OS for MFMailComposeViewController");
Class mailClass = (NSClassFromString(#"MFMailComposeViewController"));
if (mailClass != nil)
{
// We must always check whether the current device is configured for sending emails
if ([mailClass canSendMail])
{
[self displayComposerSheet];
}
else
{
[self launchMailAppOnDevice];
}
}
else
{
[self launchMailAppOnDevice];
}
}
// Displays an email composition interface inside the application. Populates all the Mail fields.
-(void)displayComposerSheet {
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
picker.navigationBar.barStyle = UIBarStyleBlack;
[picker setSubject:#"Here is your gear check list!"];
// Attach an image to the email
NSString *path = [[NSBundle mainBundle] pathForResource:#"Checkmark_icon" ofType:#"png"];
NSData *myData = [NSData dataWithContentsOfFile:path];
[picker addAttachmentData:myData mimeType:#"image/png" fileName:#"Checkmark_icon"];
// Fill out the email body text
//***** NOTE: This is where I pass the value from my iVar *****
// into the MFMailComposeViewController
//
NSString *emailBody = [NSString stringWithString:myEmailString];
[picker setMessageBody:emailBody isHTML:YES];
NSLog (#"DIsplaying Composer Sheet");
[self presentModalViewController:picker animated:YES];
[picker release];
}
// Dismisses the email composition interface when users tap Cancel or Send. Proceeds to update the message field with the result of the operation.
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
//message.hidden = NO;
// Notifies users about errors associated with the interface
switch (result)
{
case MFMailComposeResultCancelled:
NSLog (#"Result: canceled");
break;
case MFMailComposeResultSaved:
NSLog (#"Result: saved");
break;
case MFMailComposeResultSent:
NSLog (#"Result: sent");
break;
case MFMailComposeResultFailed:
NSLog (#"Result: failed");
break;
default:
NSLog (#"Result: not sent");
break;
}
[self dismissModalViewControllerAnimated:YES];
// ***** NOTE: Line below was added to fix the invalid iVar problem *****
myEmailString = #"";
}
#pragma mark -
#pragma mark Workaround
// Launches the Mail application on the device.
-(void)launchMailAppOnDevice {
NSString *recipients = #"mailto:first#example.com?cc=second#example.com,third#example.com&subject=Here is your gear check list!";
NSString *body = myEmailString;
NSString *email = [NSString stringWithFormat:#"%#%#", recipients, body];
email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}
- (void)dealloc {
[managedObjectContext release];
[fetchedResultsController release];
[tableview release];
[myList release];
[myEmailString release];
[super dealloc];
}
how is your ivar declared? is it declared as a property? in any case, it is not automatically retained for you.
Either you need to do
myEmailString = [[NSString stringWithString:buildString] retain];
or
self.myEmailString = [NSString stringWithString:buildString];
if you have myEmailString declared as
#property (nonatomic, retain) NSString *myEmailString
Think about it: if all ivars were automatically retained for you, then how would you have a variable that you didn't want to retain? That's why it doesn't work that way.
when you are creating the myEmail string in buildEmailMessage you are never retaining the string. Thus after leaving the function it is autoreleased. Your retain count then when dealloc is called will be 0, which will cause the crash. If you want to keep the variable you will need to have the line as follows
myEmailString = [[NSString stringWithString:buildString] retain];
then you can call [myEmailString release] safely
stringWithString: creates a new string and autoreleases it before returning it to you. Unless you retain the returned string, you don't need to release it in your dealloc method.
You should be retaining your string before storing it in your iVar:
myEmailString = [[NSString stringWithString:buildString] retain];
It becomes invalid without this due to it being autoreleased later during the execution of your program. This will also ensure it's still allocated when your destructor is called, preventing release crashing.