Runtime error when NSString Object is released - cocoa

In my Function below if I remove the tempString release statement it works just fine but with it, there is ALWAYS a runtime error. It is a simple function that displays an array in an NSTextField either _stackDisp1 or _stackDisp2 but for some reason releasing the string creates a runtime error Any help?
- (void) displayArr:(NSMutableArray*)stack{
NSTextField *myObj;
if([stack count] <= 10) myObj = _stackDisp1;
else myObj = _stackDisp2;
NSString *tempString = [[NSString alloc]initWithString:#""];
for(NSString *i in stack){
tempString = [NSString stringWithFormat:#"%#\n%#",tempString,i];
}
[myObj setStringValue:tempString];
[tempString release];
}

That's because
tempString = [NSString stringWithFormat:#"%#\n%#",tempString,i];
creates a new autoreleased object assigning it to your variable tempString. The pointer to the first object gets lost and you end up over-releasing an autoreleased object. Just change the initial assignment to
NSString *tempString = #"";
and remove the [tempString release] line.

In the for loop you're assigning tempString to an autoreleased string:
tempString = [NSString stringWithFormat:#"%#\n%#",tempString,i];
releasing it manually results in a BAD_ACCESS.
Also you are probably looking for this:
- (void) displayArr:(NSMutableArray*)stack{
NSTextField *myObj = ([stack count] <= 10) ? _stackDisp1 : _stackDisp2;
[myObj setStringValue:[stack componentsJoinedByString:#"\n"]];
}
The declaration/assignment of myObj was a bit too verbose for my taste,
so I used a ternary operator instead (it's use is not essiential though. Just a matter of style.).

Related

Multiplying double variable by double in Xcode

I'm making in app that includes a user entering the price of an object, and the app outputs the price including tax. However, every time it outputs 0, and I get a run-time error involving the variable, myDouble. I'm trying to take the value put in a text field and multiplying it by 1.06 (I'm starting out with a set tax rate), and then setting a label to the new value. Here is my code:
-(IBAction)textFieldReturn:(id)sender
{
[sender resignFirstResponder];
NSString *myString = inputtext2.text;
double myDouble = [myString doubleValue];
myDouble = myDouble*1.06;
NSLog(#"myDouble: %lf", myDouble);
price.text = [NSString stringWithFormat:#"%g", myDouble];
}
Most likely your inputtext2 variable is nil, so it is returning nothing. If this is an outlet, check the connections in interface builder. If the outlet is not connected, the variable will be nil, so the string value will be nil, so the double value will be 0.
I think you are going to want to use a NSNumberFormatter and check for valid entry.
pseudo code, uncompiled:
-(IBAction)textFieldReturn:(id)sender
{
[sender resignFirstResponder];
NSString *myString = inputtext2.text;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle: NSNumberFormatterDecimalStyle];
NSNumber *myNumber = [formatter numberFromString: myString];
double myDouble = 0;
if (myNumber != nil) {
myDouble = [myNumber doubleValue];
}
NSString *outputString = [formatter stringFromNumber:myDouble];
price.text = outputString;
}
This should default your number to 0 if the text entered is 0 or not a valid number.

Check if NSString instance is contained in an NSArray

I have an array with a bunch of strings and I want to check if a certain string is contained in the array. If I use the containsObject: message on the array, I'm getting correct results. Do all NSString objects with the same string point to the same object? Or why is the containsObject: working?
NSArray *stringArray = [NSArray arrayWithObjects:#"1",#"2",#"3",anotherStringValue, nil];
if([stringArray containsObject:#"2"]){
//DO SOMETHING
}
Yes, hard-coded NSStrings (string literals) (that is any #"..." in your source code) are turned into strings that exist indefinitely while your process is running.
However NSArray's containsObject: methods calls isEqual: on its objects, hence even a dynamically created string such as [NSString stringWithFormat:#"%d", 2] would return YES in your sample snippet.
This is because NSString's isEqual: (or more precisely its isEqualToString:) method is implemented to be content aware (vs. comparing pointer identities) and thus returns YES for any pair of strings containing the very same sequence of characters (at time of comparison), no matter how and when they were created.
To check for equal (pointer-)identity you'd have to enumerate your array and compare via
NSString *yourString = #"foo";
BOOL identicalStringFound = NO;
for (NSString *someString in stringArray) {
if (someString == yourString) {
identicalStringFound = YES;
break;
}
}
(which you most likely wouldn't want, though).
Or in a more convenient fashion:
BOOL identicalStringFound = [stringArray indexOfObjectIdenticalTo:someString] != NSNotFound;
(you most likely wouldn't want this one either).
Summing up:
So the reason you're getting a positive reply from containsObject: is NOT because literal strings share the same constant instance, BUT because containsObject: by convention calls isEqual:, which is content aware.
You might want to read the (short) documentation for isEqual: from the NSObject protocol.
containsObject: performs a value check, not a pointer check. It uses the isEqual: method defined by NSObject and overridden by other objects for testing. Therefore, if two strings contain the same sequence of characters, they will be considered the same.
The distinction between pointer testing and value testing is very important in some cases. Constant strings defined in source code are combined by the compiler so that they are the same object. However, strings created dynamically are not the same object. Here is an example program which will demonstrate this:
int main(int argc, char **argv) {
NSAutoreleasePool *p = [NSAutoreleasePool new];
NSString *constantString = #"1";
NSString *constantString2 = #"1";
NSString *dynamicString = [NSString stringWithFormat:#"%i",1];
NSArray *theArray = [NSArray arrayWithObject:constantString];
if(constantString == constantString2) NSLog(#"constantString == constantString2");
else NSLog(#"constantString != constantString2");
if(constantString == dynamicString) NSLog(#"constantString == dynamicString");
else NSLog(#"constantString != dynamicString");
if([constantString isEqual:dynamicString]) NSLog(#"[constantString isEqual:dynamicString] == YES");
else NSLog(#"[constantString isEqual:dynamicString] == NO");
NSLog(#"theArray contains:\n\tconstantString: %i\n\tconstantString2: %i\n\tdynamicString: %i",
[theArray containsObject:constantString],
[theArray containsObject:constantString2],
[theArray containsObject:dynamicString]);
}
The output of this program is:
2011-04-27 17:10:54.686 a.out[41699:903] constantString == constantString2
2011-04-27 17:10:54.705 a.out[41699:903] constantString != dynamicString
2011-04-27 17:10:54.706 a.out[41699:903] [constantString isEqual:dynamicString] == YES
2011-04-27 17:10:54.706 a.out[41699:903] theArray contains:
constantString: 1
constantString2: 1
dynamicString: 1
You can use containsObject to findout if certain string is exist,
NSArray *stringArray = [NSArray arrayWithObjects:#"1",#"2",#"3",anotherStringValue, nil];
if ( [stringArray containsObject: stringToFind] ) {
// if found
} else {
// if not found
}

Cocoa - Trim all leading whitespace from NSString

(have searched, but not been able to find a simple solution to this one either here, or in Cocoa docs)
Q. How can I trim all leading whitespace only from an NSString? (i.e. leaving any other whitespace intact.)
Unfortunately, for my purposes, NSString's stringByTrimmingCharactersInSet method works on both leading and trailing.
Mac OS X 10.4 compatibility needed, manual GC.
This creates an NSString category to do what you need. With this, you can call NSString *newString = [mystring stringByTrimmingLeadingWhitespace]; to get a copy minus leading whitespace. (Code is untested, may require some minor debugging.)
#interface NSString (trimLeadingWhitespace)
-(NSString*)stringByTrimmingLeadingWhitespace;
#end
#implementation NSString (trimLeadingWhitespace)
-(NSString*)stringByTrimmingLeadingWhitespace {
NSInteger i = 0;
while ((i < [self length])
&& [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[self characterAtIndex:i]]) {
i++;
}
return [self substringFromIndex:i];
}
#end
This is another solution using Regular Expressions (requires iOS 3.2):
NSRange range = [string rangeOfString:#"^\\s*" options:NSRegularExpressionSearch];
NSString *result = [string stringByReplacingCharactersInRange:range withString:#""];
And if you want to trim the trailing whitespaces only you can use #"\\s*$" instead.
This code is taking blanks.
NSString *trimmedText = [strResult stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",trimmedText);
Here is a very efficient (uses CoreFoundation) way of doing it (Taken from kissxml):
- (NSString *)trimWhitespace {
NSMutableString *mStr = [self mutableCopy];
CFStringTrimWhitespace((CFMutableStringRef)mStr);
NSString *result = [mStr copy];
[mStr release];
return [result autorelease];
}
NSString *myText = #" foo ";
NSString *trimmedText = [myText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSLog(#"old = [%#], trimmed = [%#]", myText, trimmedText);
Here's what I would do, and it doesn't involve categories!
NSString* outputString = inputString;
NSRange range = [inputString rangeOfCharacterFromSet: [NSCharacterSet whitespaceCharacterSet]
options:0];
if (range.location == 0)
outputString = [inputString substringFromIndex: range.location + range.length];
This is much less code.
I didn't really have much time to test this, and I'm not sure if 10.4 contains the UTF8String method for NSString, but here's how I'd do it:
NSString+Trimming.h
#import <Foundation/Foundation.h>
#interface NSString (Trimming)
-(NSString *) stringByTrimmingWhitespaceFromFront;
#end
NSString+Trimming.m
#import "NSString+Trimming.h"
#implementation NSString (Trimming)
-(NSString *) stringByTrimmingWhitespaceFromFront
{
const char *cStringValue = [self UTF8String];
int i;
for (i = 0; cStringValue[i] != '\0' && isspace(cStringValue[i]); i++);
return [self substringFromIndex:i];
}
#end
It may not be the most efficient way of doing this but it should work.
str = [str stringByReplacingOccurrencesOfString:#" " withString:#""];

Problem getting size of Website Xcode

When i try and compile I come up with a warning that reads initialization makes pointer from integer without a cast. No clue why. I am just trying to get the size of a website.
#import "Lockerz_RedemptionViewController.h"
#implementation Lockerz_RedemptionViewController
-(IBAction)startLoop:(id) sender {
NSData *dataNew = [NSData dataWithData:[NSData dataWithContentsOfURL:[NSURL
URLWithString:#"http://www.google.com/"]]];
NSUInteger *len = [dataNew length]; //error is here
NSLog(#"%#", len);
}
NSUInteger is just a wrapper for an unsigned int, alter your code to this (i.e. remove the * as it's not a pointer to an object)
NSUInteger len = [dataNew length];
Also I think you're going a bit overboard with your initialisation, why not just do
NSData *dataNew = [NSData dataWithContentsOfURL:[NSURL
URLWithString:#"http://www.google.com/"]];
That should return you an autoreleased object containing the data you need

Getting URL From beginSheetModalForWindow:

I'm using an OpenPanel to get a file path URL. This works:
[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
NSURL *pathToFile = nil;
if (returnCode == NSOKButton)
pathToFile = [[oPanel URLs] objectAtIndex:0];
}];
This doesn't, resulting in an 'assignment of read-only variable' error:
NSURL *pathToFile = nil;
[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
if (returnCode == NSOKButton)
pathToFile = [[oPanel URLs] objectAtIndex:0];
}];
return pathToFile;
In general, any attempt to extract pathToFile from the context of oPanel has failed. This isn't such a big deal for small situations, but as my code grows, I'm forced to stuff everything -- XML parsing, core data, etc -- inside an inappropriate region. What can I do to extract pathToFile?
Thanks.
This doesn't, resulting in an 'assignment of read-only variable' error:
NSURL *pathToFile = nil;
[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
if (returnCode == NSOKButton)
pathToFile = [[oPanel URLs] objectAtIndex:0];
}];
return pathToFile;
Yes, because you're trying to assign to the copy of the pathToFile variable that gets made when the block is created. You're not assigning to the original pathToFile variable that you declared outside the block.
You could use the __block keyword to let the block assign to this variable, but I don't think this will help because beginSheetModalForWindow:completionHandler: doesn't block. (The documentation doesn't mention this, but there's no reason for the method to block, and you can verify with logging that it doesn't.) The message returns immediately, while the panel is still running.
So, you're trying to have your completion-handler block assign to a local variable, but your method in which you declared the local variable will probably have returned by the time block runs, so it won't be able to work with the value that the block left will leave in the variable.
Whatever you do with pathToFile should be either in the block itself, or in a method (taking an NSURL * argument) that the block can call.
you can also runModal after you begin the sheet you just need to make sure you end the sheet later. This way you don't have to bend to apple's will, it isn't deprecated and it should still work perfectly.
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel beginSheetModalForWindow:window completionHandler:nil];
NSInteger result = [openPanel runModal];
NSURL *url = nil;
if (result == NSFileHandlingPanelOKButton)
{
url = [openPanel URL];
}
[NSApp endSheet:openPanel];
It seems a little bit like black magic coding but it does work.

Resources