NSArray and NSString - objective-c

The book I'm currently reading has me write the following code :
-(IBAction)displaySomeText:(id)sender {
NSString *cow = #"Milk";
NSString *chicken = #"Egg";
NSString *goat = #"Butter";
NSArray *food = [NSArray arrayWithObjects:cow, chicken, goat, nil];
NSString *string = #"The shopping list is: ";
string = [string stringByAppendingString:[food componentsJoinedByString:#", "]];
[textView insertText:string];
}
I understand somewhat how arrays work but I need help understanding the following code
string = [string stringByAppendingString:[food componentsJoinedByString:#", "]];
I have never ever seen an instance where this is possible.
He has me create a 'string' object, from the NSString class, and then I'm doing this
string = [string stringByAppendingString:];
I'm confused. I have never seen an example where I create an object and then perform a method on the same object and store it in that exact same object.
For example, I know I can do this
NSSound *chirp;
chirp = [NSSound soundNamed:#"birdChirp.mp3"];
the above makes sense because I used the created object and performed a class method on it..
but I always assumed that the equivalent of the following code was NOT possible
chirp = [chirp methodNameEtc..];
I hope I explained my question well. If not I could always elaborate further.

I think this is the heart of your question "I'm confused. I have never seen an example where I create an object and then perform a method on the same object and store it in that exact same object."
To answer that question, your not actually 'storing it in the exact same object'. What you are doing is confusing pointers and objects.
Let's just look at this line:
string = [string stringByAppendingString:#"Hello"];
In this line 'string' is a pointer, not the object it points to. What this line of code is saying is:
"Object currently referenced by the pointer 'string', return to me a new NSString object whose text is your text with this text added. And when I get that new NSString object I ordered make the pointer 'string' point to that object instead."

string = [string stringByAppendingString:[food componentsJoinedByString:#", "]];
is the same as
NSString *tmpString = [food componentsJoinedByString:#", "];
string = [string stringByAppendingString:tmpString];
So in the example, the innermost square brackets are evaluated first, and then the outermost. Does that clarify it a bit? Thing of it like parentheses in math: (1*2*(1+2))... the innermost () get evaluated before you can determine that the real problem is 1*2*3. That is what is happening with [food componentsJoinedByString:#", "].

stringByAppendingString is not storing the string in the exact same object. It's one NSString object creating a new (autoreleased) NSString object.
There is no equivalent example for chirp (which is a NSSound object), unless you create your own category which extends NSSound with a method to return a brand new NSSound object.
Makes sense?
Oh, I also appreciate your Takizawa icon.

Related

What is an actual example of the difference between NSString to NSMutablestring could look like?

I'v been looking around in the questions here and could not find simple example to point me the difference while I was testing some code of my own to test the differentiation.
From what I understand, in an "immutable" string such as 'NSString', I could not preform any 'NSString' methods to modify the string, such as:
NSString *s = #"cat";
s = [NSString stringWithString:#"blamp"];
NSLog(#"%#", s);
But it does work..
Please try to give me and other newbies out there a very simple example of what won't work and why.
tnx
The statement :
s = [NSString stringWithString:#"blamp"];
actually creates a new memory location for the string "blamp" and the old address of s gets replaced by this new address.
And you get the feel that the same s is updated!!! Actually the pointer now points to some other memory addresss.
String manipulation means changing the same string : as if you try
NSString *s = #"cat";
[s appendString:#"s"];//tries to append to the same. this will through error.
//the above works with NSMutableString.

Repointing a pointer inside an Objective-C method

Firs of all, sorry if my english is not absolutely correct. It's not my native language but I'll try to explain myself the best I can.
I'm having a hard time trying to understand the following issue. Take in account the following code:
// On a class named SPOTest
- (void)referenceTest:(NSMutableString *)originalText
{
[originalText appendString:#" world!!!"]
}
// From another place
NSMutableString *myText = [NSMutableString stringWithString:#"Hello"];
NSLog(#"Contents of myText BEFORE: %#", myText);
SPOTest *myTest = [[SPOTest alloc] init];
[myTest referenceTest:myText];
NSLog(#"Contents of myText AFTER: %#", myText);
The output:
Contents of myText BEFORE: Hello
Contents of myText AFTER: Hello world!!!
I find it understandable. I'm working with pointers so if I change the thing and the end of a pointer, I'm changing that thing for all the pointers pointing to it. On the other hand, if I change the code and do this:
// On a class named SPOTest
- (void)referenceTest:(NSMutableString *)originalText
{
NSMutableString *newText = [NSMutableString stringWithString:#"Hello world!!!"];
originalText = newText;
}
// From another place
NSMutableString *myText = [NSMutableString stringWithString:#"Hello"];
NSLog(#"Contents of myText BEFORE: %#", myText);
SPOTest *myTest = [[SPOTest alloc] init];
[myTest referenceTest:myText];
NSLog(#"Contents of myText AFTER: %#", myText);
Then I get this:
Contents of myText BEFORE: Hello
Contents of myText AFTER: Hello
Why is that? I suppose the correct way to do this is to use a double indirection and an implementation similar to the one used with NSError mechanism but I want to understand why I'm obtaining this behavior. If I can change the contents and the end of myText pointer from the referenceTest: method in the first example, why can't I change the address of myText from the same method in the second example?
I know I'm missing something trivial but I can't find it and I'd like to understand this to better understand the reasoning behind NSError mechanism.
Thank you!
In the second case you're changing the local copy of that pointer. If you want to repoint it in the calling scope, you'd need to use a pointer to a pointer, i.e.:
- (void)referenceTest:(NSMutableString **)originalText
{
NSMutableString *newText = [NSMutableString stringWithString:#"Hello world!!!"];
*originalText = newText;
}
And call it thusly:
[myTest referenceTest:&myText];
And it is worth noting that stringWithString returns an autoreleased string, which means your function is too.
There's a difference between objects and pointers to objects.
Someone has created an NSMutableString object, which exists somewhere in memory. We don't really care where it is. That someone received an NSMutableString* which points to the NSMutableString object. A copy of that NSMutableString* was given to your method referenceTest. There can be any number of pointers to that NSMutableString object, but there is only one object.
The appendString method changes the NSMutableString object itself.
The stringWithString method creates a new NSMutableString object and returns a pointer to it. So now we have two objects, and newText is a pointer to the second one. When you assign newText to originalText, originalText becomes a pointer to the second NSMutableString object. However, originalText is just the parameter in your method. The pointer that the calling method holds isn't changed by this.

Understanding Instantiation in Objective-C

I’m currently trying to learn Objective-C by reading books and online tutorials, also referring to the Apple documentation but some things just don’t click. I have a question about classes, I have been using the NSString for a while now without putting too much attention on how it is used.
I was under the impression that in order for someone to be able to use a method from a certain class in Objective-C you first needed to instantiate it, something like…
ClasssName *varName = [[ClassName alloc]init];
Then you would call methods like...
[varName someMethod];
But looking at how the NSString is used I’m now I little confused, for instance here is how we would normally use it...
NSString *someString = #"some text here";
[someString stringByAppendingFormat: #"some text = %d", 3];
Following what I have read about classes we would need to do something like the following instead.
NSString *someString = [[NSString alloc]initWithString: #"some text here"];
[someString stringByAppendingFormat: #"some text = %d", 3];
Can someone explain why some classes do not require instantiation before using its methods?
Objective-C knows some abbreviations that are called literals. The compiler is aware of the special notation. A string literal is compiled into the binary and exits throughout the lifetime of an app.
For most cases it will behave like an object created on runtime.
if two literals are identical, only one object will be created and live forever
if you create NSString *string = [[NSString alloc] initWithString:#"My String"]; with #"My String" being used as a literal before, also string can point to it.
Since Apple LLVM Compiler 4.0 Objective-C knows some more literals. But in contrast to string literals these literals are created during runtime through convenient initializers.
Note that
[someString stringByAppendingFormat: #"some text = %d", 3];
does not change someString. It returns a new string object.
NSString *newString = [someString stringByAppendingFormat: #"some text = %d", 3];
The #"Text" syntax gives you an autoreleased string back, it can be thought of as a shorthand.
when you write
[[NSString alloc]initWithString: #”some text here”];
you conceptually create an autoreleased string with #”some text here” and then you create a retained new string with initWithString.

Change pointer to NSString in Objective-C

I have been experimenting a bit with Objective-C and noted some, at least to me, rather strange behavior. First I define a pointer to an NSString and add it to an NSArray:
NSString *s = #"A";
NSArray *a = [NSArray arrayWithOject: s];
I then print out the value of s as well as the contents of a:
NSLog(#"%#", s);
NSLog(#"%#", myArray);
and in both cases the output is A. Now, if I change the pointer s, say
s = #"B";
then the two NSLog statements print out B and A, respectively.
That is, the pointer in my array still points to #"A". After spending years coding in Java, this is very surprising to me. Am I missing something really fundamental here?
Thanks,
Michael Knudsen
The easiest way to understand this is that #"A" creates a new NSString object.
In your code, you set the pointer s to point to this object, and then add the original object (not the pointer) to an array. You then change the address that the pointer points to to a new address.
If you want to change the original object, then use NSMutableString and modify the actual object (instead of changing the pointer to a new object) and they will both update as you expect.
Try:
NSMutableString *s = #"A";
// Add to array
[s setString:#"B"]
That is because NSStrings are immutable, as in Java, so you are not changing the contents of the object stored at address x (the one nsarrsy has), you are pointing s to address y.
The Java and Objective-C behaviors are the same. With the following Java code:
String foo = "foo";
Vector myVector = new Vector();
myVector.add(foo);
System.out.println(foo);
System.out.println(myVector);
foo = "bar";
System.out.println(foo);
System.out.println(myVector);
The following values are printed out:
foo
[foo]
bar
[foo]
Note: The same is true if I use String[] instead of Vector, but an NSArray is more like Vector than String[].
The easiest way to see this is to see that your s = #"B"; simply point s to another string object rather than modifying the original object.
So the array still contain #"A". When you add objects to NSArray you simply make a pointer in NSArray to point to #"A". That never changes.

String allocation and instance variables

I am quite new to programming and objective C and I am having a hard time grasping a concept here about allocation, memory management and how they tie in with instance variables. I created a category for NSString called isUrl to test for a prefix of "http:// in a string.
From there, I wanted to test this on a string. However, given that situation I couldn't figure out if I should:
1) Declare a new string as an instance variable with #property to synthesize the accessors
2) Allocate a new string in my implementation
3) Set my variable and allocate it
What would be the difference between these methods and why would I perhaps want to use one over the other? -
As a side note, I am working with a book by O'Reilly and have tried to find answers around the web, but not a lot of luck thus far. I seem to find examples of everything, but I an explanation of how or why they are used is more difficult.
1)
NSString* string = [[[NSString alloc]init]autorelease];
string = #"http://www.google.com";
if ( [string isUrl]) {
NSLog(#"Caption is a URL");
NSLog(#"URL %#",string);
string = nil;
2)
NSString* string = #"http://www.googl.com";
[string retain];
if ( [string isUrl]) {
NSLog(#"Caption is a URL");
NSLog(#"URL %#",string);
string = nil;
3) Something like this....
#synthesize string;
string.name = "http://www.google.com";
if ( [string.name isUrl]) {
NSLog(#"Caption is a URL");
NSLog(#"URL %#",string);
string = nil;
String literals don't need to be released or retained.
Properties should be used whenever a value associated with an object needs to exist outside of a single function call and you don't need your program to compile under 10.4 or earlier. By your use of nil, it appears you don't need the string to persist, so you should only use a local variable. That's what local variables are for.
NSString* string = #"http://www.google.com";
if ( [string isUrl]) {
NSLog(#"Caption is a URL");
NSLog(#"URL %#",string);
}
Apple's Objective-C memory management rules are quite simple. There's really only one rule, with a couple of corollaries.
NSString* string = [[[NSString alloc]init]autorelease];
string = #"http://www.google.com";
You don't need to allocate an object before assigning a different object to the same variable. The first object will simply get deallocated and serves no purpose.
string.name = "http://www.google.com";
"" denotes a C string. For an NSString, you must use the at symbol. The above should be
string.name = #"http://www.google.com";