Objective C: convert a NSMutableString in NSString - objective-c

I have an NSMutableString, how can I convert it to an NSString?

Either via:
NSString *immutableString = [NSString stringWithString:yourMutableString];
or via:
NSString *immutableString = [[yourMutableString copy] autorelease];
//Note that calling [foo copy] on a mutable object of which there exists an immutable variant
//such as NSMutableString, NSMutableArray, NSMutableDictionary from the Foundation framework
//is expected to return an immutable copy. For a mutable copy call [foo mutableCopy] instead.
Being a subclass of NSString however you can just cast it to an NSString
NSString *immutableString = yourMutableString;
making it appear immutable, even though it in fact stays mutable.
Many methods actually return mutable instances despite being declared to return immutable ones.

NSMutableString is a subclass of NSString, so you could just typecast it:
NSString *string = (NSString *)mutableString;
In this case, string would be an alias of mutalbeString, but the compiler would complain if you tried to call any mutable methods on it.
Also, you could create a new NSString with the class method:
NSString *string = [NSString stringWithString:mutableString];

Related

Why is NSString stringWithString returning pointer to copied string?

I'm trying to copy an NSString value out of an NSMutableArray into a new variable. NSString stringWithString is returning an NSString with the same memory address as the object in my array. Why?
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSMutableArray *arr = [NSMutableArray arrayWithObject:#"first"];
NSLog(#"string is '%#' %p", [arr objectAtIndex:0], [arr objectAtIndex:0]);
// copy the string
NSString *copy = [NSString stringWithString:[arr objectAtIndex:0]];
NSLog(#"string is '%#' %p", copy, copy);
}
return 0;
}
1) Whenever you're creating a string using the #"" syntax, the framework will automatically cache the string. NSString is a very special class, but the framework will take care of it. When you use #"Some String" in multiple places of your app, they will all point to the same address in memory. Only when you're using something like -initWithData:encoding, the string won't be cached.
2) The other answers suggested that you should use -copy instead, but -copy will only create a copy of the object if the object is mutable. (like NSMutableString)
When you're sending -copy to an immutable object (like NSString), it'll be the same as sending it -retain which returns the object itself.
NSString *originalString = #"Some String";
NSString *copy = [originalString copy];
NSString *mutableCopy1 = [originalString mutableCopy];
NSString *mutableCopy2 = [mutableCopy copy];
NSString *anotherString = [[NSString alloc] initWithString:originalString];
--> originalString, copy, mutableCopy2 and anotherString will all point to the same memory address, only mutableCopy1 points do a different region of memory.
Since NSString instances are not mutable, the +stringWithString: method is simply returning the input string with an incremented reference count.
If you really want to force the creating of a new, identical string, try:
NSString * copy = [NSString stringWithFormat:#"%#", [arr objectAtIndex:0]];
There is little point in doing so, though, unless you need the pointer to be unique for some other reason...

Going crazy with UITextField

I'm literally going crazy whit these six rows of code.
NB: nome and prezzo are 2 textFields
NSString *itemName = (NSString *) [rowVals objectForKey:#"name"];
NSString *itemPrice = (NSString *) [rowVals objectForKey:#"price"];
nome.text = itemName;
nome.userInteractionEnabled = YES;
prezzo.text = itemPrice;
prezzo.userInteractionEnabled = YES;
Don't know why when itemPrice is copied in one of those label, the program go in SIGABRT.
Instead if I try to read the content with an NSLog(#"%#",itemPrice); it return the exact value, so it means that is a valid NSString.
The only solution I found is passing through a NSNumber:
NSNumber *itemPrice = (NSNumber *) [rowVals objectForKey:#"price"];
prezzo.text = [[NSString alloc] initWithFormat:#"%#", itemPrice];
There is another way to use directly the NSString?
Probably the value in the #"price" field is NSNumber, and not an NSString. The NSLog method will still provide a correct result, since %# is used for any NSObject subclass, not just NSString.
How about this:
NSString *itemPrice = [[rowVal objectForKey:#"price"] stringValue];
prezzo.text = itemPrice;
The problem might be the object type returned by [rowVals objectForKey:#"price"]. When you place the (NSString *) cast before the method call, you're telling the compiler what type of object is returned, but not actually converting it into an NSString. The line you use below does convert from NSNumber (or whatever other object) to a string: [[NSString alloc] initWithFormat:#"%#", itemPrice]
You might be storing NSNumber's object in NSDictionary instead of NSString.
There could be 2 ways: one would be to convert NSNumber to NSString while adding it to dictionary or the other way would be to convert NSNumber to NSString while assigning it to "itemName".
you may do the conversion for second option like:
NSString *itemPrice = [[rowVals objectForKey:#"price"]stringValue];

assigning NSNumber object to NSString object won't cause any error or warning

I've put NSNumber object into NSDictionary object, and then pop and assign it to a variable declared as NSString instance.
NSString *test;
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1000] forKey:#"key"];
test = [dict valueForKey:#"key"];
NSLog(#"%#, type of %#", test, NSStringFromClass([test class]));
return 0;
After running the above code, I've found that the type of test, declared as (NSString *) is __NSCFNumber. Why did it happen and why compiler did give no warning or errors?
Do I have to NSString constructor, such as NSStringWithFormat..., in order to keep test as NSString's instance?
The reason this did not fail or warn you is because valueForKey: returns an object of type id which you can assign to any Objective-C type. You need to be aware of what type of value you are getting back from your collections to safely use them. In this case you know it contains an NSNumber so you should expect an NSNumber and if you need a string you will need to do the proper conversions.
NSNumber *test = [dict valueForKey:#"key"];
NSLog(#"%#, type of %#", test, NSStringFromClass([test class]));
//If you need a string
NSString *testStr = [test stringValue];

I am having trouble with compatibility between NSArray and NSMutableArray?

I am having trouble with compatibility between NSArray and NSMutableArray?
--> Incompatible Objective-C types assigning "struct NSArray *", expected "struct NSMutableArray"
NSMutableArray *nsmarrRow;
NSString *nsstrFilePath = [[NSBundle mainBundle] pathForResource: nsstrFilename ofType: nsstrExtension];
NSString *nsstrFileContents = [NSString stringWithContentsOfFile: nsstrFilePath encoding:NSUTF8StringEncoding error: NULL];
//break up file into rows
nsmarrRow = [nsstrFileContents componentsSeparatedByString: nsstrRowParse];//<--Incompatible Objective-C types assigning "struct NSArray *", expected "struct NSMutableArray"
I have tried making the "NSString declaration" to "NSMutableString"... made more problems.
thanks
You can't get a mutable array by doing
nsmarrRow = [nsstrFileContents componentsSeparatedByString: nsstrRowParse];
You will need to,
nsmarrRow = [NSMutableArray arrayWithArray:[nsstrFileContents componentsSeparatedByString: nsstrRowParse]];
Changing NSString to NSMutableString won't give you an NSMutableArray object. You will get an NSArray object just as in case of NSString. You will have to use that to get an NSMutableArray using the above method.
The componentsSeparatedByString method returns an NSArray. Try the following:
[NSMutableArray arrayWithArray:[nsstrFileContents componentsSeparatedByString: nsstrRowParse]];

How do I convert NSMutableArray to NSArray?

How do I convert NSMutableArray to NSArray in objective-c?
NSArray *array = [mutableArray copy];
Copy makes immutable copies. This is quite useful because Apple can make various optimizations. For example sending copy to a immutable array only retains the object and returns self.
If you don't use garbage collection or ARC remember that -copy retains the object.
An NSMutableArray is a subclass of NSArray so you won't always need to convert but if you want to make sure that the array can't be modified you can create a NSArray either of these ways depending on whether you want it autoreleased or not:
/* Not autoreleased */
NSArray *array = [[NSArray alloc] initWithArray:mutableArray];
/* Autoreleased array */
NSArray *array = [NSArray arrayWithArray:mutableArray];
EDIT: The solution provided by Georg Schölly is a better way of doing it and a lot cleaner, especially now that we have ARC and don't even have to call autorelease.
I like both of the 2 main solutions:
NSArray *array = [NSArray arrayWithArray:mutableArray];
Or
NSArray *array = [mutableArray copy];
The primary difference I see in them is how they behave when mutableArray is nil:
NSMutableArray *mutableArray = nil;
NSArray *array = [NSArray arrayWithArray:mutableArray];
// array == #[] (empty array)
NSMutableArray *mutableArray = nil;
NSArray *array = [mutableArray copy];
// array == nil
you try this code---
NSMutableArray *myMutableArray = [myArray mutableCopy];
and
NSArray *myArray = [myMutableArray copy];
Objective-C
Below is way to convert NSMutableArray to NSArray:
//oldArray is having NSMutableArray data-type.
//Using Init with Array method.
NSArray *newArray1 = [[NSArray alloc]initWithArray:oldArray];
//Make copy of array
NSArray *newArray2 = [oldArray copy];
//Make mutablecopy of array
NSArray *newArray3 = [oldArray mutableCopy];
//Directly stored NSMutableArray to NSArray.
NSArray *newArray4 = oldArray;
Swift
In Swift 3.0 there is new data type Array. Declare Array using let keyword then it would become NSArray And if declare using var keyword then it's become NSMutableArray.
Sample code:
let newArray = oldArray as Array
In objective-c :
NSArray *myArray = [myMutableArray copy];
In swift :
var arr = myMutableArray as NSArray
NSArray *array = mutableArray;
This [mutableArray copy] antipattern is all over sample code. Stop doing so for throwaway mutable arrays that are transient and get deallocated at the end of the current scope.
There is no way the runtime could optimize out the wasteful copying of a mutable array that is just about to go out of scope, decrefed to 0 and deallocated for good.
If you're constructing an array via mutability and then want to return an immutable version, you can simply return the mutable array as an "NSArray" via inheritance.
- (NSArray *)arrayOfStrings {
NSMutableArray *mutableArray = [NSMutableArray array];
mutableArray[0] = #"foo";
mutableArray[1] = #"bar";
return mutableArray;
}
If you "trust" the caller to treat the (technically still mutable) return object as an immutable NSArray, this is a cheaper option than [mutableArray copy].
Apple concurs:
To determine whether it can change a received object, the receiver of a message must rely on the formal type of the return value. If it receives, for example, an array object typed as immutable, it should not attempt to mutate it. It is not an acceptable programming practice to determine if an object is mutable based on its class membership.
The above practice is discussed in more detail here:
Best Practice: Return mutableArray.copy or mutableArray if return type is NSArray
i was search for the answer in swift 3 and this question was showed as first result in search and i get inspired the answer from it
so here is the swift 3 code
let array: [String] = nsMutableArrayObject.copy() as! [String]