Consider the following method and the caller code block. The method analyses a NSString
and extracts a "http://" string which it passes out by reference as an auto release object.
Without releasing g_scan_result, the program works as expected. But according to non-arc rules, g_scan_result should be released since a retain has been called against it.
My question are :
Why g_scan_result cannot be released ?
Is there anything wrong the way g_scan_result is handled in the posted coding below ?
Is it safe not to release g_scan_result as long as the program runs correctly and the XCode Memory Leak tool does not show leakage ?
Which XCode profile tools should I look into to check and under which subtitle ?
Hope somebody knowledgeable could help.
- (long) analyse_scan_result :(NSString *)scan_result target_url :(NSString **)targ_url {
NSLog (#" RES analyse string : %#", scan_result);
NSRange range = [scan_result rangeOfString:#"http://"
options:NSCaseInsensitiveSearch];
if (range.location == NSNotFound) {
*targ_url = #"";
NSLog(#"fnd string not found");
return 0;
}
NSString *sub_string = [scan_result substringFromIndex : range.location];
range = [sub_string rangeOfString : #" "];
if (range.location != NSNotFound) {
sub_string = [sub_string substringToIndex : range.location];
}
NSLog(#" fnd sub_string = %#", sub_string);
*targ_url = sub_string;
return [*targ_url length];
}
The following is the caller code block, also note that g_scan_result has been declared and initialized (on another source file) as :
NSString *g_scan_result = nil;
Please do send a comment or answer if you have suggestions or find possible errors in code posted here (or above). Xcode memory tools does not seem to show any memory leak. But it may be because I do not know where to look as am new to the memory tools.
{
long url_leng = [self analyse_scan_result:result target_url:&targ_url];
NSLog(#" TAR target_url = %#", targ_url);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Scanned Result"
message:result
delegate:g_alert_view_delegate
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
if (url_leng) {
// ****** The 3 commented off statements
// ****** cannot be added without causing
// ****** a crash after a few scan result
// ****** cycles.
// ****** NSString *t_url;
if (g_system_status.language_code == 0)
[alert addButtonWithTitle : #"Open"];
else if (g_system_status.language_code == 1)
[alert addButtonWithTitle : #"Abrir"];
else
[alert addButtonWithTitle : #"Open"];
// ****** t_url = g_scan_result;
g_scan_result = [targ_url retain];
// ****** [t_url release];
}
targ_url = nil;
[alert show];
[alert release];
[NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:#selector(activate_qr_scanner:)
userInfo:nil
repeats:NO
];
return;
}
I think the mystery has been solved. Thanks for all who are kind enough to have looked into this. The reason is before adding the UIAlertView, coding had been done (on another source file) to assign a raw output string to g_scan_result and display it directly on the current view. So when g_scan_result got released, it is releasing the wrong NSString assigned by some outdated code.
In summary, the wrong NSString got released which is the source of the problem.
The solution is just to remove a single outdated statement. The statement from the old implementation was left there as I thought it wouldn't do any harm (and may even helped to make the variable populated continuously). But it turned out to be a very silly mistake. The only excuse is having very little sleep lately. Being able to find an excuse does serve a purpose. Just hope that it doesn't have to be done very often ...
Related
Update:
I believe the problem lies somewhere in how I am storing the reference to ABRecordRef. I am currently just hanging onto the value as delivered to peoplePickerNavigationController:shouldContinueAfterSelectingPerson:property:identifier: and not CFRetaining it or anything. It's unclear from the documentation if it needs to be retained.
I'm working on an iPhone app and it interfaces with the address book using the AddressBook and AddressBookUI frameworks. I'm using the ABPeoplePickerNavigationController to present a contact list to the user to choose, and am capturing the resultant ABRecordRef as an instance variable on a custom class.
This is all working fine on the first use. However, the second time I pick someone from the contacts (even a different person), my app blows up with EXC_BAD_ACCESS on a call to ABRecordCopyValue. I am logging the pointers and they are definitely different each time a contact is selected (even if the same contact twice).
I fail to understand how this reference could be deallocated. A memory leak sure, but why does it work fine the first time and not the second?
Here's the actual call it's dying on:
- (NSString*)displayName {
return CFBridgingRelease( ABRecordCopyValue( self.contact, kABPersonFirstNameProperty ) );
}
Here's some debug output if it's helpful at all:
Printing description of self->_contact:
(ABRecordRef) _contact = 0x1f582dc0
(lldb) expr (CFTypeRef)ABRecordCopyValue(self.contact, kABPersonFirstNameProperty)
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0xd1f57cc1).
The process has been returned to the state before execution.
Turns out all I needed was to CFRetain( person ) and everything's happy go lucky again. I also added a dealloc to my class to clean up the pointer when the object goes away:
- (void)dealloc {
CFRelease( _contact );
}
My code now runs smoothly and the static analyser is happy (not that it caught the leak anyway).
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
[self displayPerson:person];
[self dismissViewControllerAnimated:YES completion:nil];
return NO;
}
you returning NO ?
Try checking to see if the value exists maybe
ie
- (void)displayPerson:(ABRecordRef)person
{
NSString* companyName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonOrganizationProperty);
NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString* display = #"";
if (companyName) {
display = companyName;
} else if (name) {
display = name;
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"No Details For Contact"
message:#"Please update contact with company and/or first name"
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
}
I've looked over the site for a while and I saw a few things but nothing worked for me. Extreme newbie here though.
I'm trying to make a 2 component picker, populated from arrays, populated from a dictionary, populated from a plist. The components populate, and I have a button that spins one component. The second button is meant to check answers (the first component has states which they try to match with capitals in the second component), but this is where it always crashes.
The error I get is as follows:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSPlaceholderString initWithFormat:locale:arguments:]: nil argument'
* First throw call stack:
(0x14b3022 0xeb3cd6 0x145ba48 0x145b9b9 0x916169 0x916ab4 0x3036 0x14b4e99 0x1914e 0x190e6 0xbfade 0xbffa7 0xbf266 0x3e3c0 0x3e5e6 0x24dc4 0x18634 0x139def5 0x1487195 0x13ebff2 0x13ea8da 0x13e9d84 0x13e9c9b 0x139c7d8 0x139c88a 0x16626 0x27ad 0x2715)
terminate called throwing an exception(lldb)
I'm not sure where I'm going wrong. I've done this with hard coded arrays, and it works fine. The code for the button is like so (please ignore the switch code, I haven't quite figured out how to make that work yet, I want the screen to turn green for correct answers and red for incorrect, but I'm more concerned with the app crashing!):
-(IBAction)checkAnswer;
{
NSInteger StateRow = [pickerCapital selectedRowInComponent:karrayStateComponent];
NSInteger CapitalRow =[pickerCapital selectedRowInComponent:karrayCapitalComponent];
NSString *state = [arrayState objectAtIndex:StateRow];
NSString *capital = [arrayCapital objectAtIndex:CapitalRow];
NSLog (#"%i",[pickerCapital selectedRowInComponent:karrayCapitalComponent]);
NSLog(#"%i", [pickerCapital selectedRowInComponent:karrayStateComponent]);
NSString *Title = [[NSString alloc]initWithFormat:#"You have selected %# as the capital of %#.", capital, state];
NSString *Message =[[NSString alloc]initWithFormat:lblMsg];
UIAlertView *alert =[[UIAlertView alloc]initWithTitle:Title message:Message delegate:#"" cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
if (StateRow == CapitalRow) {
lblMsg = #"You are correct!";
UIColor *myColor;
switch ([not sure what to use here]) {
case 0:
myColor = [UIColor greenColor];
break;
default:
break;
}
}
else {
lblMsg = #"Incorrect";
UIColor *myColor;
switch ([not sure what to use here]) {
case 0:
myColor = [UIColor redColor];
break;
default:
break;
}
}
}
If anyone can offer any sort of help on this, I would appreciate it greatly! Thank you!
Two things to check:
Where is lblMsg declared or assigned a value?
Are you sure capital and state are not nil from the calls to [arrayCapital objectAtIndex]?
Specifically: the NSString documentation states "Important: Raises an NSInvalidArgumentException if format is nil." for initWithFormat.
Having a heck of a time with this one.
I've got a super-simple Cocoa app containing one WebView, a WebScripting API defined in the page, and a single NSObject defined on that API. When I turn on the debugger tools (in the embedded WebView), I can see the API on the JavaScript window object, and I can see my "api" property defined on that -- but when I call the API's "get" method, the arguments aren't being serialized -- when the Obj-C method gets called, the arguments are missing. See below, which hopefully illustrates:
I've combed through the docs, I've (apparently) set the appropriate methods to expose everything that needs to be exposed, and I can see the method being called. There has to be something stupid I'm missing, but as a relative newbie to this environment, I'm not seeing it.
Thanks in advance for your help!
Have you set WebKitDeveloperExtras to YES in your default user defaults when you send -[NSUserDefaults registerDefaults:]?
Depending on what version of Xcode you're using you could be getting a known error. If you're using LLDB on anything but the most recent version, it might not be giving you the right variables in the debugger. The solution has been to use GDB instead of LLDB until Apple fixes the problem. But I think they fixed the problem in the latest version. I'd change the debugger to use GDB and see if you're getting the right variables in Xcode. (Product-> Edit Scheme...-> Run -> Debugger). I came across this problem in iOS, though, so I don't know its applicability to OSX. Worth a try anyway.
I originally came across the problem here: https://stackoverflow.com/a/9485349/1147934
I process javascript in the main thread of my app from a local file stored in the apps directory. I check for beginning and ending tokens for the js functions I am executing and whether the function contains a variable.
Hopefully this can give you some good ideas for your issue. You could also do alerts in the js to see if the values post correctly as you run the app (I am sure you thought of that already, but it's worth mentioning.) Happy coding! I hope this helps!
in the .h file define:
NSMutableString *processedCommand;
NSArray *commandArguments;
In the .m file:
// tokens
#define kOpenToken #"<%%"
#define kCloseToken #"%%>"
// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {
[self performSelectorOnMainThread:#selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}
// this will throw
-(NSString *)executeCommand:(NSString *)command {
NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:#""]
stringByReplacingOccurrencesOfString:kCloseToken withString:#""]
stringByTrimmingLeadingAndTrailingWhitespaces];
if ([aCommand hasPrefix:#"="])
{
// variable. get value
[self getVariableFromCommand:aCommand];
}
else {
[self executeThisCommand:aCommand];
}
NSString *returnValue = [NSString stringWithString:processedCommand];
self.processedCommand = nil;
self.commandArguments = nil;
return returnValue;
}
-(void)executeThisCommand:(NSString *)aCommand {
BOOL hasError = NO;
// clear result
self.processedCommand = nil;
self.commandArguments = nil;
BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;
#try {
// first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:#":"]];
if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:#"js-call"]) {
isFromJS = YES;
[commandParts removeObjectAtIndex:0];
}
// get our function, arguments
function = [[commandParts objectAtIndex:0] retain];
[commandParts removeObjectAtIndex:0];
if ([commandParts count] > 0){
if (isFromJS == YES) {
NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([arguments length] > 0) {
self.commandArguments = [arguments JSONValue];
}
}
else {
self.commandArguments = [NSArray arrayWithArray:commandParts];
}
}
// build invoke
SEL sel = NSSelectorFromString(function);
if ([self respondsToSelector:sel]) {
[self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];
// using invocation causes a SIGABORT because the try/catch block was not catching the exception.
// using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)
}
else {
[appDelegate buildNewExceptionWithName:#"" andMessage:[NSString stringWithFormat:#"Object does not respond to selector %#", function]];
}
}
#catch (NSException * e) {
hasError = YES;
[self updateErrorMessage:[NSString stringWithFormat:#"Error processing command %#: %#", aCommand, [e reason]]];
}
#finally {
[function release];
[commandParts release];
}
if (hasError == YES) {
[appDelegate buildNewExceptionWithName:#"executeThisCommand" andMessage:self.errorMessage];
}
}
// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {
NSString *returnValue = nil;
if (queryString != nil) {
returnValue = [queryString objectForKey:[name lowercaseString]];
}
return returnValue;
}
I am trying to call objectForKey: on an nsdictionary ivar, but I get an EXC_BAD_ACCESS error.
The nsdictionary is created using the JSON-framework and then retained. The first time I use it (just after I create it, same run loop) it works perfectly fine, but when I try to access it later nothing works. I am doing this code to try to figure out what is wrong:
if (resultsDic == nil) {
NSLog(#"results dic is nil.");
}
if ( [resultsDic respondsToSelector:#selector(objectForKey:)] ) {
NSLog(#"resultsDic should respond to objectForKey:");
}
The dictionary is never nil, but it always crashes on respondsToSelector. any ideas?
addition:
These are the other places, besides above, that the dictionary gets interacted with:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
//get the data in a usable form
NSString *jsonString = [[NSString alloc] initWithData:downloadedData encoding:NSUTF8StringEncoding];
resultsDic = [jsonString JSONValue];
[self processResults];
NSLog(#"Success. Received %d bytes of data",[downloadedData length]);
[downloadedData release];
[jsonString release];
}
- (void)processResults
{
NSArray *resultsArr = [resultsDic objectForKey:#"results"];
CLLocationCoordinate2D coordinate = [self coordinateFromResult:[resultsArr objectAtIndex:0]];
NSLog(#"lat: %f lng: %f", coordinate.latitude, coordinate.longitude);
}
- (void)dealloc {
[resultsDic release];
[super dealloc];
}
After somethings retain count is decreased to 0, the object gets deallocated. This is not the same as setting it to nil. It will not be nil. Whilst you can send messages to nil, sending a message to a released object will result in an EXC_BAD_ACCESS error. If you post some of the code where it is created and used, maybe we can help you debug it. Try retaining it twice at the beginning. it's nit an elegant solution, but it might work as a quick fix.
Sounds like a classic zombie. Run it again with the environment variable NSZombieEnabled set to YES (or use the Zombies instrument in Instruments.app). That should give you much more information about what's going on.
Im in the final stages of my first iphone sdk project. I have been working hard to remove memory leaks from my app, and have mostly succeeded at it. But there I am struggling with one of them. I have a contact screen with a button that fetches a webview, but only if there is a network connection. If not an alert pops up. This works fine in practice but l-e-a-k-s.
All the leaks point to the same place in the code. Here is the first code sample (instruments points to the first of these lines):
BOOL nett=[self connectedToNetwork];
if (!nett)
{
errorView=[[UIAlertView alloc] initWithTitle:#"Netverksfeil" message:#"Nettet er nede" delegate:self
cancelButtonTitle:#"FillerĀ“n!" otherButtonTitles:nil];
[errorView show];
[errorView release];
}
else{
iCodeBrowserViewController *browserView=[[iCodeBrowserViewController alloc]initWithNibName:#"iCodeBrowserViewController" bundle:[NSBundle mainBundle]];
[[self navigationController] pushViewController:browserView animated:YES];
[browserView release];
}
I suppose that means that the leak is somewhere inside that function...
The next spot instruments points at is in this sample:
// Create zero addy
- (BOOL) connectedToNetwork{ struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
// Recover reachability flags
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability);
if (!didRetrieveFlags)
{
printf("Error. Could not recover network reachability flags\n");
return 0;
}
BOOL isReachable = flags & kSCNetworkFlagsReachable;
BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
BOOL nonWiFi = flags & kSCNetworkReachabilityFlagsTransientConnection;
return ((isReachable && !needsConnection) || nonWiFi) ?
(([[[NSURLConnection alloc] initWithRequest:[NSURLRequest
requestWithURL: [NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0]
delegate:self]autorelease]) ? YES : NO) : NO;}
This line to be specific:
return ((isReachable && !needsConnection) || nonWiFi) ?
Can any of you see what is leaking in this code? I have copied this part from somewhere else, and managed to alter it slightly. But i must admit i dont understand all the code in that function...
Have you cleaned the project then run a "Build & Analyze"? Most of the time that will tell you about most of your memory issues as long as you've been using Objective C style functions. If you mix-and-match with C style functions it won't be as much help.
I'd guess that the NSURLRequest inside that line is the one that isn't getting released. Might help readability and maintainability to break that line up a bit.