I have corrected something that was confusing me in V2 but am still a little curious as to what V1 is doing. Looking at V1 now am I right in thinking that line is being passed by value (so essentially what I have is a local copy of line). Scanner then scans its data into the address of that local copy, the local copy is destroyed when the method exits and the external variable line is still nil?
On V2, I am passing the address of line and then scanning data into that address, is this using passing by reference, just not sure of the terminology?
// V1
NSString *line = nil;
[self scanUsing:scanner into:line];
NSLog(#"AFTER_: %#", line);
- (void)scanUsing:(NSScanner *)scanner into:(NSString *)line {
[scanner scanUpToString:#"\n" intoString:&line];
NSLog(#"INSIDE: %#", line);
}
.
// V2
NSString *line = nil;
[self scanUsing:scanner into:&line];
NSLog(#"AFTER_: %#", line);
- (void)scanUsing:(NSScanner *)scanner into:(NSString **)line {
[scanner scanUpToString:#"\n" intoString:line];
NSLog(#"INSIDE: %#", *line);
}
V1
You are passing a copy of the pointer. It points to the same memory region, so what you see is the same value. You are passing the object then, by value. You can change the content, but not create a new object, since that pointer wont exist when the method finishes.
V2
Definition of reference is different (its a C++ type) But yeah, lets say that it behaves more or less the same. In V2 a new object can be allocated inside the method, and therefore, you can change the memory region that is being pointed by it.
So:
V1, passing by value. You pass the pointer, and can change the content of the object pointed by the pointer.
V2, passing by reference. You pass a pointer to a pointer, and therefore you can alloc memory, and so change the pointer address as well.
Since this is not C++, you can't call it pass-by-reference. You can, however, call it pass-by-pointer. (Which is still by value, but you're passing a pointer by value.)
Apart from grammar nazi: the first version modifies only the local copy of line (i. e. the one inside the function); the one you actually passed in stays unchanged.
In V1, you're changing the value of the line local parameter, which only exists in the scope of the method, you're not changing your global line variable.
In V2, you effectively pass the global line pointer by reference, which allows you to change its value...
In the first example, you are passing a reference to an object.
In the second example, you are passing a reference to a reference of an object.
If you log the line-object in the first example, you would see difference, while in the second you would see the object you have set inside the method.
this is often used for NSError, where you have a basic return type, but you also want to notify if there was an error. You pass then a pointer to your error variable, and they pass it the reference to the NSError object. I hope this made at least a little sense to you :)
Related
I'ver wondered, why is it that in front of an NSError, such as below, do we put: &error and not error?
E.g.
NSArray *result = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
Hope you can explain, also is this always the way or only in certain situations this is needed? Thanks.
You need to take the address of error because the function needs to modify it. error is passed by pointer, so you need the "take address" operator & for it.
C and Objective-C pass parameters by value. If you pass error without an ampersand and the method that you call modifies it, your function that made the call would not see any changes, because the method would operate on its local copy of NSError*.
You know that you need an ampersand in front of the corresponding parameter if you look at the signature of the method and see ** there:
- (NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error
// ^ one ^^ two
The error parameter's type is (NSError **), that is a pointer to a pointer to an NSError. The error variable that you use as the argument is probably declared as NSError *, so in order to get the types to match properly, you have to use the address of operator to get a pointer to a pointer (&error). The reason the method needs a pointer to a pointer in the first place is so that it can modify the value of error and have that new value be available to you, the caller of the method.
Essentially, the root of the issue is a hack for wanting to return a second (optional) object.
How can we do this, as we can only return one thing? Well, we could return some sort of (return_value, error) tuple, but that's a bit unwieldy. We can have as many parameters as we like though, can we do something with those...
So, methods/functions can't modify their parameters (to be precise, they operate with a copy, so any modifications they make are local). That is to say (concurrency issues aside) the value of fetchRequest before the message in your question will be equal to the value of fetchRequest afterwards. Note the object pointed to by fetchRequest might change, but the value of fetchRequest itself won't.
This puts us in a bit of a bind. Except, wait, we know we can happily take the value of a parameter and modify what it points to! If you look at the declaration for executeFetchRequest:error: you'll see it takes an NSError**. That's "a pointer to a pointer to an NSError". So, we can initialise an empty/dangling NSError*, find the address of it (with the unary & operator), and pass that in. The method can then assign to the NSError* pointed to by this.
Voila, we effectively have optional additional return values.
When a method returns an object that is taken from and NSMutableArray does the object must be autoreleased? Check the following method. Also should I autorelease the nil value?
-(NSObject*)getElementByID:(NSString*)ID{
for(int i=0;i<[elements count];i++){
NSObject *element = (NSObject*) [elements objectAtIndex:i];
if([element.key isEqualToString:ID]){
return [element autorelease];
}
}
return nil;
}
You must not autorelease element because you are not an owner of it (you have not put a retain on it). You would have become an owner of it if you acquired it using alloc, new or retain. Since you acquired this object calling objectAtIndex:, you do not own it. See Three Magic Words. Calling autorelease here will cause a crash later do to over-release.
Your method name is incorrect and breaks KVC. A method that begins with get must take a pointer that will be updated with the result. This should be elementForID:. As noted above with the three magic words, naming in ObjC is very important to writing stable code
As a side note, it is traditional to use id is most cases rather than NSObject*. They mean slightly different things, but typically id is correct.
You never need to do any memory management related things to nil. So, no, you should not send autorelease to nil.
You also should not need to send autorelease to the element object that you are returning from your elements array. That object you are returning will remain in memory by virtue of elements itself having retained it. If the calling method would like to retain the value that you return, it may. But if that calling method only uses the returned value within its own scope, it is safe for it to do so without retaining it.
So, I'm fairly certain that if I plan on manipulating strings often, such as with stringByAppendingString, I should be using variables of type NSMutableString.
But what if I'm doing something like this?
UILabel *someLabel = [[UILabel alloc] init];
[someLabel setText: [[someDictionary objectForKey:#"some_key"] stringByAppendingString:#"some other string"];
I read that if you use stringByAppendingString on an NSString, you end up with leaks because the pointer associated with the initial NSString moves around, pointing to the new string created by the append, whereas with NSMutableString, your pointer always points to that mutable string.
So my question is, what is implicitly happening when I call stringByAppendingString on something that is a string, but not explicitly an NSString or an NSMutableString? Such as, in my above case, the value of some key in a dictionary. Is doing this wrong, and should I be doing something like below?
[[[NSMutableString stringWithString:[someDictionary objectForKey:#"some_key"]] stringByAppendingString:#"some other string"]]
I read that if you use
stringByAppendingString on an
NSString, you end up with leaks
because the pointer associated with
the initial NSString moves around,
pointing to the new string created by
the append, whereas with
NSMutableString, your pointer always
points to that mutable string.
That sounds like the advice of someone who didn't quite have a grasp of what is going on with the memory management. Sure, [NSString stringByAppendingString] returns a new string. But what you do with that new string is up to you. You could certainly cause a memory leak by reassigning the result to a retained property in a careless fashion, like so:
myStringProperty = [myStringProperty stringByAppendingString:#" more bits"];
The correct form would be to use self, like so:
self.myStringProperty = [myStringProperty stringByAppendingString:#" more bits"];
Follow the cocoa memory guidelines.
As for dictionaries and other collection types: treat what comes out of the dictionary appropriately given the type you know it to be. If you pull an object out which is actually an NSString, but try to use it as a NSMutableString, your app will fall over (with 'selector not found' or similar). So in that case, you do need to make a new NSMutableString from the NSString.
Interesting note: Apple chose to make NSMutableString a subclass of NSString. Something about that seems unwise to me -- if something looks to be immutable, because it has type NSString, I want it to be immutable! (But in fact it could be NSMutableString.) Compare that to Java, which has a String class and a completely separate BufferedString class.
I've always been a fan of [NSString stringWithFormat#"%#%#", a, b]; because then you clearly get a new autoreleased string and can dispose of "a" and "b" correctly.
With [someDictionary objectForKey:#"some_key"], you will be getting the type of object that was put into that dictionary originally. So blindly calling stringByAppendingString without knowledge of what's in that dictionary seems like a bad idea.
-stringByAppendingString is going to return you a new NSString that is distinct from both strings involved. In other words:
NSString *string3 = [string1 stringByAppendingString:string2];
string3 is an entirely new string. string1 isn't changed at all, nothing happens to its memory location or contents. The person who told you that probably just misunderstood what was going on.
[mutableString1 appendString:string2];
In this case, mutableString1 still points at the same object, but the contents of that object have been altered to include string2.
One last thing to keep in mind is that if you are using mutable strings, you should be careful with sharing references to it. If you pass your mutable string to some function which keeps a pointer to that mutable string and then your code changes that mutable string at some point in the future, the other reference is pointing at exactly the same object which means the other code will see the change as well. If that's what you want, great, but if not you must be careful.
One way to help avoid this problem is to declare your #property statements for NSStrings to be "copy" instead of "retain". That will make a copy of your mutable string before setting it in your property and the -copy method implicitly gives you a NON-mutable version, so it'll create an NSString copy of your NSMutableString.
If you follow the rules for memory management, you will be fine using stringByAppendingString. In a nutshell:
if you own an object, you need to release or autorelease it at some point.
you own an object if you use an alloc, new, or copy method to create it, or if you retain it.
Make sure you read up on Apple's Memory Management Rules.
In the first code sample in your question, you aren't using alloc, new, copy or retain on any of the NSStrings involved, so you don't need to do anything to release it. If outside of the code that you've included in the sample you are using alloc, new, copy or retain on any NSStrings, you would need to ensure that they are released later.
I have a question about a retain and a NSString, if I have a method who a return a NSString, and I put the return NSString in a nsstring variable, I must do a retain or not?
NSString *myString = #"";
myString = [self methodWhoReturnString]; // I must do this?
myString = [[self methodWhoReturnString]retain]; // Or I must do this?
The Apple Developer Documentation on Memory Management explains the scenarios where you retain/release objects.
Simply put, if you want the string to stick around, you need to retain it until you're finished with it. If that is just the scope of the current function, you can get away without retaining it as if the string is already autorelease'd (likely) it won't get released until your function finishes and the current AutoReleasePool is purged.
Bear in mind that an NSString * could actually be pointing to an NSMutableString *. If it matters to you if the string is changed by some other function without you realizing, be sure to copy it: NSString * myCopyOfString = [mystring copy];
If the string is set to autorelease, which it most likely is, then yes you will need to retain it somehow. I would suggest doing this though:
myString = [[self methodWhoReturnString] copy];
this ensures you have retained the data in the string not just a reference to a string that might still be controlled elsewhere. Be sure you release your copy later!
Usually, methodWhoReturnString would return an autoreleased string, which means you should retain it if you want to keep it around.
So, if methodWhoReturnString is your method, I believe that to keep with convention you should return [stringToReturn autorelease]; from that method, and then retain it if you want to keep it.
You use retain if you're going to be using myString at a later point in time (i.e. after the current method has returned) to prevent it being autoreleased.
You don't need to use retain if it's just a temporary variable used within the current method, since in that case you do want it to be autoreleased.
One special case is properties. If you call self.blah = foo, you don't need to retain foo, since the setBlah: method should do it for you.
(there's a whole load of other complexities and edge cases, but this is a good rule of thumb to get you started on understanding what to do)
Given the code you provided, you shouldn't call -retain. In your example, the return value of a method that returns an instance of NSString is assigned to myString, an automatic local variable. If the assignment had been made to an instance variable or a static variable, you would want to call either retain or copy to guarantee that the reference remains valid beyond the end of the local scope.
In this case though, the reference to the NSString instance is stored in a variable that will be destroyed automatically at the end of the local scope, so your code needn't concern itself with the object's lifetime.
Any method that has alloc, new or copy in it automatically retains and infers that you have ownership of the object. All others shouldn't. It would be helpful if you had more context though. If we are in a contained method where this string is used briefly, then you probably don't need to retain. If it is going to be used for a while, you might want to use the #synthesize syntax to make it a property of the class you are in. When you use #property and #synthesize and call something like self.myProperty = something it will automatically retain.
As everyone knows, setTitle automatically retains the string passed as a parameter. When the button caption needs to be changed, I guess that it is necesary to release the current (old) string before setting the new one. I wonder what is the most elegant way to dot it.
See my code sample (here, getPlayerHandFromGame method produces autoreleased strings which are retained when setTitle is invoked):
colourString = [pGame getPlayerHandFromGame:1 withColour:COLOUR_HEARTS];
// Split colourString into array of strings if not null.
if ([colourString length] != 0) {
listCards = [colourString componentsSeparatedByString:#" "];
for (cardCounterSameColour = 1; cardCounterSameColour <= [listCards count]; cardCounterSameColour ++) {
currentCardButton = [self buttonCardNumber:cardCounter];
// Objects are numbered from 0 in the array
[currentCardButton setTitle:[listCards objectAtIndex:cardCounterSameColour-1] forState:UIControlStateNormal];
cardCounter ++;
}
}
This portion of code will be called several times since the button caption will be updated several times. I guess that before setting the title, I should do something like this:
[currentCardButton titleForState:UIControlStateNormal release]
in order to release the string which will not be used anymore (titleForState returns a pointer to the NSString).
Is that the right way to avoid that the device memory gets loaded with unused strings ?
Many thanks,
Apple92
Any class that retains a value set on one of its properties is also responsible for releasing the old value when that property's value gets changed again. Don't worry about it.
As everyone knows, setTitle automatically retains the string passed as a parameter.
Really? I don't know it. In fact, I'll bet a pint of beer that it doesn't retain the string, but copies it.
Granted, for an NSString, -copy is probably implemented as doing a retain and returning self, but if you pass it an NSMutableString, a genuine copy will occur.
I guess that it is necesary to release the current (old) string before setting the new one.
Guess again, sucker!
Or less facetiously: any object is responsible for managing the ownership of other objects it wantsa to keep hold of. Once you have passed the title in setTitle: you do not need to worry about how the object disposes of it once it gets a new one.
Consider the code:
[currentCardButton titleForState:UIControlStateNormal]
and apply the memory management rules to the return result.
Did you obtain it with alloc, new or copy? No. Did you retain it? No (remember we are talking about the object passed back by the method,not the object you originally gave it). Therefore, you must not release it.
Take a look at the object ownership convention:
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html#//apple_ref/doc/uid/20000043-BEHDEDDB
Every class should follow these rules (Apple's does it), so in order to them you dont have to worry because you haven't done a alloc/retain in your class, and the button will retain it for it's internal use.