How to fix this mutating error? - ios

I'm getting this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with replaceCharactersInRange:withString:'
But I can't figure out what immutable object I'm mutating.
NSRange subRange = [self.label.text rangeOfString: #"="];
int numA = 5;
int numB = 3;
NSMutableString *mixed = [NSString stringWithFormat: #"%i %i", numA, numB];
NSMutableString *string = [NSString stringWithString: self.label.text];
subRange = [string rangeOfString: #"="];
if (subRange.location != NSNotFound)
[string replaceCharactersInRange:subRange withString:mixed];

Your NSMutableString creation calls aren't balanced properly. You're promising the compiler that you're creating a NSMutableString but you ask an NSString to create an instance.
For example:
NSMutableString *mixed = [NSString stringWithFormat: #"%i %i", numA, numB];
needs to be:
NSMutableString *mixed = [NSMutableString stringWithFormat: #"%i %i", numA, numB];

Your NSMutableStrings are actually instances of NSString. This is runtime detail, though there should at least be a warning for those lines. Change to:
NSMutableString *string = [self.label.text mutableCopy];

You need to create a NSMutableString like this:
[NSMutableString stringWithString: self.label.text];

Related

Extracting URLs from NSString into NSMutableString

I have a file with the following content:
cool name http://myurl1.tld/subdir/dir/var/ awesome\nanother cool name http://sub.domain.tld/dir/ nice\nsome nice name http://myname.tld/var/folder/ some\n
I read the file with this Objetive-C code:
NSData* data = [NSData dataWithContentsOfFile:[NSString stringWithFormat:#"../files/name.ext"]];
NSString* raw = [[NSString alloc]
initWithBytes:[data bytes]
length:[data length]
encoding:NSUTF8StringEncoding];
To extract all URLs from the above sample I use:
NSDataDetector* detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray* matches = [detector matchesInString:raw options:0 range:NSMakeRange(0, [raw length])];
My goal is to append all extracted URLs into one single string and separate them with a semicolon (without any other stuff from the file) with this loop:
NSMutableString *allURLStrings = [NSMutableString string];
for (NSString *s in matches) {
[allURLStrings appendString:s];
[allURLStrings appendString:#";"];
}
Whenever I try to run the code I get the following output:
terminating with uncaught exception of type NSException (lldb)
I tried to use the loop like this, but then the substringForCurrMatch only contains a slash (/):
NSMutableString * allRepoString = [NSMutableString string];
for (NSTextCheckingResult *s in matches) {
NSString* substringForCurrMatch = [s.URL path];
[allRepoString appendString:substringForCurrMatch];
[allRepoString appendString:#";"];
}
When I inspect s in the loop it shows me the correct object:
Is there a way to get this code working?
EDIT complete error message:
2016-08-11 23:00:19.272 AppName[34831:2892247] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[fetchurlsource allURLStrings]: unrecognized selector sent to instance 0x14c55ea00'
*** First throw call stack:
(0x181fa2db0 0x181607f80 0x181fa9c4c 0x181fa6bec 0x181ea4c5c 0x1000e8b6c 0x1000e8ee0 0x187100c40 0x187100844 0x18710759c 0x187104a88 0x18717afa4 0x1873a63ac 0x1873aa5f0 0x1873a7764 0x1839437ac 0x183943618 0x1839439c8 0x181f5909c 0x181f58b30 0x181f56830 0x181e80c50 0x18716f94c 0x18716a088 0x1000edb10 0x181a1e8b8)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
None of the code you posted so far can result in the exception you posted.
But the following code is incorrect:
NSMutableString * allRepoString = [NSMutableString string];
for (NSTextCheckingResult *s in matches) {
NSString* substringForCurrMatch = [s.URL path];
[allRepoString appendString:substringForCurrMatch];
[allRepoString appendString:#";"];
}
You do not want to call the path method on s.URL. To get the full URL as a string, use absoluteString. This will give you the URL as a string. path just gives you the path portion of the URL.
NSMutableString * allRepoString = [NSMutableString string];
for (NSTextCheckingResult *s in matches) {
NSString* substringForCurrMatch = [s.URL absoluteString];
[allRepoString appendString:substringForCurrMatch];
[allRepoString appendString:#";"];
}

Assigning charAtIndex to stringWithCharacters gives invalid cast warning and bad access error

I'm trying to grab firstname and lastname from firstname+lastname.
int loop=0;
NSMutableString *firstname = [[NSMutableString alloc]init];
NSMutableString *fullName = [[NSMutableString alloc]initWithString:#"Anahita+Havewala"];
for (loop = 0; ([fullName characterAtIndex:loop]!='+'); loop++) {
[firstname appendString:[NSString stringWithCharacters:(const unichar *)[fullName characterAtIndex:loop] length:1]];
}
NSLog(#"%#",firstname);
I tried typecasting from unichar to const unichar* because characterAtIndex returns a unichar but stringWithCharacters accepts a const unichar.
This causes a cast from smaller integer type warning and the app crashes (bad access) when this line is encountered.
Why are string operations so complicated in Objective C?
You can easily get first name and last using componentsSeparatedByString: method.
NSMutableString *fullName = [[NSMutableString alloc]initWithString:#"Anahita+Havewala"];
NSArray *components = [fullName componentsSeparatedByString:#"+"];
NSString *firstName = components[0];
NSString *lastName = components[1];
Note: You need to do proper array bounds check. Also you can use NSScanner for the same purpose.
Try this out:
NSMutableString *firstname = [[NSMutableString alloc] init];
NSMutableString *fullName = [[NSMutableString alloc] initWithString:#"Anahita+Havewala"];
for (NSUInteger loop = 0; ([fullName characterAtIndex:loop]!='+'); loop++) {
unichar myChar = [fullName characterAtIndex:loop];
[firstname appendString:[NSString stringWithFormat:#"%C", myChar]];
}
NSLog(#"%#", firstname);

Assigning NSMutableString from NSString Warning

I have the following three lines of code in my program:
NSMutableString *stringOne = [NSMutableString stringWithFormat:#"Hello "];
NSMutableString *stringTwo = [NSMutableString stringWithFormat:#"World"];
NSMutableString *sayIt = [stringOne stringByAppendingString:stringTwo];
Despite the fact that it works, the third line is causing the warning Assigning NSMutableString * from NSString *.
I am at a loss here since everything I am using is an NSMutableString.
Not quite. stringByAppendingString does not return an NSMutableString. Instead do the following:
NSMutableString *sayIt = [NSMutableString stringWithString:stringOne];
[sayIt appendString:stringTwo];
Or
NSMutableString *sayIt = [[stringOne stringByAppendingString:stringTwo] mutableCopy];
I just realized that stringByAppendingString: returns an NSString despite calling it from an NSMutableString object. Since I am dealing with NSMutableString, I should have used that class's appendString: method. The new code that works without a warning is:
NSMutableString *stringOne = [NSMutableString stringWithFormat:#"Hello "];
NSMutableString *stringTwo = [NSMutableString stringWithFormat:#"World"];
[stringOne appendString:stringTwo];

NSDecimalNumbers in Array Causing "Unrecognized Selector..." messages

Ive been playing around with obj-c for a few months now and feel comfortable with a lot of things but cant make sense of something that seems very simple. I need to be able to go from an NSString split into an array then get the NSDecimalNumbers from that array. If I try to perform any calculations, I get an "unrecognized selector sent to instance." If I simply print each decimal number everything goes smoothly. If I put the values manually into NSDecimalNumbers rather than pulling from the array that also works. The error and code are below.
2013-10-14 15:54:42.174 test[30829:c07] -[__NSCFString
decimalNumberByAdding:]: unrecognized selector sent to instance
0x75a81f0 2013-10-14 15:54:42.176 test[30829:c07] * Terminating app
due to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSCFString decimalNumberByAdding:]: unrecognized selector sent to
instance 0x75a81f0'
* First throw call stack: (0x1c90012 0x10cde7e 0x1d1b4bd 0x1c7fbbc 0x1c7f94e 0x2075 0x10e1705 0x152c0 0x15258 0xd6021 0xd657f 0xd56e8
0x44cef 0x44f02 0x22d4a 0x14698 0x1bebdf9 0x1bebad0 0x1c05bf5
0x1c05962 0x1c36bb6 0x1c35f44 0x1c35e1b 0x1bea7e3 0x1bea668 0x11ffc
0x1bfd 0x1b25) libc++abi.dylib: terminate called throwing an exception
-(IBAction)buttonPushed: (id) sender
{
NSString* s = #"25.55, 109.24";
NSArray* a = [s componentsSeparatedByString: #" "];
NSDecimalNumber* dec1 = [a objectAtIndex: 0];
NSDecimalNumber* dec2 = [a objectAtIndex: 1];
NSDecimalNumber* sum = [dec1 decimalNumberByAdding: dec2];
statusText.text = [NSString stringWithFormat: #"%#", sum, nil];
}
NSDecimalNumber* dec1 = [a objectAtIndex: 0];
doesn't give you a NSDecimalNumber object. It gives you a string (which was created via the call to componentsSeparatedByString)
Try doing this:
NSDecimalNumber* dec1 = [NSDecimalNumber decimalNumberWithString: [a objectAtIndex: 0]];
and see if you have better luck.
you never created a NSDecimalNumber object
NSArray* a = [s componentsSeparatedByString: #", "];
will of course create an aray of strings.
Just because you type the pointer as decimal number, doesnt make the obejct to be one
NSDecimalNumber* dec1 = [a objectAtIndex: 0];
dec1 is still a NSString. Try
NSDecimalNumber* dec1 = [NSDecimalNumber decimalNumberWithString:[a objectAtIndex: 0]];
NSDecimalNumber* dec2 = [NSDecimalNumber decimalNumberWithString:[a objectAtIndex: 1]];
NSDecimalNumber* sum = [dec1 decimalNumberByAdding: dec2];
statusText.text = [NSString stringWithFormat: #"%#", sum];
When you call [a objectAtIndex: 0] that is just returning a string, that you're trying to cast to a NSDecimalNumber.
Instead, try using something like this:
-(IBAction)buttonPushed: (id) sender
{
NSString* s = #"25.55, 109.24";
NSArray* a = [s componentsSeparatedByString: #", "];
NSDecimalNumber* dec1 = [NSDecimalNumber decimalNumberWithString:[a objectAtIndex: 0]];
NSDecimalNumber* dec2 = [NSDecimalNumber decimalNumberWithString:[a objectAtIndex: 1]];
NSDecimalNumber* sum = [dec1 decimalNumberByAdding: dec2];
statusText.text = [NSString stringWithFormat: #"%#", sum, nil];
}
Also note that I'm splitting the string with ", " and not just " ", otherwise the first string will be "25.55,"
See the NSDecimalNumber documentation for the details: https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/classes/NSDecimalNumber_Class/Reference/Reference.html#//apple_ref/occ/clm/NSDecimalNumber/decimalNumberWithString:

Unable to extracting a substring from an NSString

I have an input string in the format
"Jerry Lane"(angle bracket)jerry.lane#gmail.com(bracket closed),"Harry Potter"(angle bracket)harry.potter#gmail.com(bracket closed),"Indiana Jones",(angle bracket)indiana.jones#gmail.com(bracket closed),"Tom Cruise"(angle bracket)tom.cruise#gmail.com(bracket closed)
Here, i am supposed to first separate the string on the basis of comma delimiter, which would give me a separate string like
"Jerry Lane"(angle bracket)jerry.lane#gmail.com(bracket closed)
Then i need to save extract the string between the <> brackets, which is essentially the string "jerry.lane#gmail.com". I am using the following code, but it is giving me the following error:
Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFConstantString substringWithRange:]: Range or index out of bounds'
-(NSArray *)parseString:(NSString *)string
{
if(string)
{
NSArray *myArray = [string componentsSeparatedByString:#","];
for(NSMutableString *myString in myArray)
{
NSRange start,end;
start = [myString rangeOfString:#"<"];
end = [myString rangeOfString:#">"];
if(start.location != NSNotFound && end.location != NSNotFound)
{
NSString *emailAddress = [myString substringWithRange:NSMakeRange(start.location,end.location)];
NSString *name = [myString substringToIndex:start.location];
NSDictionary *myDictionary = [[NSDictionary alloc] init];
[myDictionary setValue:emailAddress forKey:#"Dhruvil Vyas"];
[testArray addObject:myDictionary];
}
}
}
return testArray;
}
The arguments that substring takes are the start position and the length
Not the start position and the end position.
More Info
borrrden's answer is correct. Here is another way to do this.
-(NSArray *)parseString:(NSString *)string
{
if(string)
{
NSArray *myArray = [string componentsSeparatedByString:#","];
for(NSMutableString *myString in myArray)
{
NSArray *tempNameArray = [myString componentsSeparatedByString:#"<"];
NSString *email = [tempNameArray objectAtIndex:1];
NSArray *tempMailArray = [email componentsSeparatedByString:#">"];
NSString *emailAddress = [tempMailArray objectAtIndex:0];
NSString *name = [tempNameArray objectAtIndex:0];
NSDictionary *myDictionary = [[NSDictionary alloc] init];
[myDictionary setValue:emailAddress forKey:#"Dhruvil Vyas"];
[testArray addObject:myDictionary];
}
}
return testArray;
}

Resources