I am trying to wrap my head around NSString and NSMutableString, and how they affect memory.
In my research, I've concluded that if I create an NSString object and give it a value, then change the value later, the original object is replaced by another with the new value.
My question is, in the case of changing the value of an NSString. When the value of the NSString is changed and the object pointed to is replaced by a new object, what happens to the original object? Is this a memory leak?
Thanks! V
NSStrings can contain string literals, which are compile time constants, and string objects, which instead are instantiated dynamically at runtime.
In case of string literals, there is no dynamic instantiation, so they won't affect memory at runtime. Assigning a literal value to a NSString variable
NSString *myString = #"string1";
then assigning another literal value
myString = #"string2";
doesn't make any change to the amount of available memory.
In case of string objects instead memory does change. If you have a variable of type NSString and assign it a dynamic string, such as
NSString *myString = [NSString stringWithFormat:#"String %d", 1];
a new NSString instance is allocated, initialized with the provided string, and assigned to the myString variable.
If later you assign a new dynamic value
myString = [NSString stringWithFormat:#"String %d", 2];
a new NSString instance is created, but it doesn't replace the old one. Both are instantiated and in memory.
When using ARC though the first NSString instance, if no longer referenced, will be released because not being used. This is handled automatically, so nothing must be done from code.
Maybe you're more interested in the fact that at some time the new and old instances use memory - but that's temporary until the unused instance is released.
To answer to your concern, no, there is no memory leak. String literals persist for the entire app lifetime, since they are constants. String objects are allocated and released (deallocated), and deallocation is automatically handled by ARC.
Of course this is a general concept that applies to any dynamically instantiated class, not just NSString - but doesn't apply to primitive data type (int, boolean, float, etc.), because variables of those types hold the actual value, not a pointer to the area in memory where the value is. In case you are wondering, primitive data types don't generate memory leaks.
The original NSString will be released by the system and so, won't cause any leak.
Related
these code will get a warning: assigning retained object to weak variable object will be released after assignment
__weak NSString *str = [[NSString alloc] initWithFormat:#"1234"];
NSLog(#"url:%#",str);
but the nslog will print 1234 normally,seems that the object isn't released after assignment , so when will the release happend?
You can get the behavior you expect by setting OBJC_DISABLE_TAGGED_POINTERS to YES in the program's environment. For example, you can set it in your scheme in Xcode like this:
What's going on (if you don't set that environment variable) is the Objective-C runtime supports tagged pointer strings. This means that short strings of common characters are encoded entirely in the 64-bit object reference, stored in the str variable. There is no heap allocation. Since there is no heap allocation for the string, and since the string cannot itself have references to other objects, the runtime knows it doesn't actually need to arrange for the __weak variable to be set to nil, so it doesn't.
By setting that environment variable, you disable the use of all tagged pointers, including tagged pointer strings. So I wouldn't recommend it for production code.
You can read more about tagged pointer strings in this excellent article by Mike Ash.
I have a question about the parameter that #synchronized take, I have read the Apple document about synchronisation but still I don't have a clear idea.
I have a case that #synchronized will take a string property inside some object like this :
#synchronized(someObject.A)
since A is a NSString object and in some cases will carry the same value but from different someObject is this will guarantee the locking for all objects with same A values?
It's worth noting that NSString has some special cases that are handled magically.
NSString *s1 = #"Test string";
NSString *s2 = #"Test string";
Here, s1 and s2 are actually compiled to access the same memory address, even though they are different variables and could be instantiated in completely different places within the application.
However, if you are loading the data on-the-fly or using one of the construction methods for NSString instead of hard-coding it, strings that match character-for-character will not share the same memory.
You can consider this the difference between comparing with == and isEqualToString:. #synchronized only ever uses the == result.
So, to answer your question: maybe.
If you are using hard-coded values of the form #"some string" within your application, your #synchronized command will link to all objects that share the same textual value for A.
If you are creating NSString objects by any other means, your #synchronized command will only link to objects that point to the exact same NSString object.
The synchronization will be done on whatever object someObject.A is currently referencing. The important piece is the actual object you use #synchronized on.
If you assign the same string to two completely difference properties and you then use #synchronized on those two completely different properties, it will work since both point to the same string.
The following example may help:
// In one method
#synchronized(someObject.A) {
}
// In another method
NSString *foo = someObject.A;
#synchronized(foo) {
}
The above two blocks will be thread safe on the same string object.
NSString *pDescText = #"blablabla";
pDescText = [NSString stringWithFormat:#"%# %#",skProduct.localizedDescription,formattedPrice];
Does this produce a memory leak when I reassign pDescText in line 2?
If you are using ARC, you don't need to worry about these kinds of leaks.
If you are using MRC, you don't have a leak because:
You only need to release objects that you have a received a reference to by sending messages which contain new alloc retain or copy. In neither of these lines have you done this so you don't have an owned reference to pDescText. Furthermore, in the first line you are using a string literal, which is something that yo don't have to worry about memory management for.
And I have to say this pDescText is not a good name to use for a variable in Cocoa. We don't use hungarian notation (so the p to indicate a pointer isn't needed) and full, descriptive variable names are the norm. So instead of pDescText a more experienced Cocoa developer would use descriptiveText or even productDescription or description if that is enough for the context.
No, NSString convenience constructors return an autoreleased object.
I know there are differences between C/C++ and Objective-C, but what is the case when it comes to the pointer-mark *? Is this not used for pointers in Objective-C, or are all objects pointers in this language?
I.E, if I try NSString string = #"Hello";, XCode tells me to use NSString *string instead. Does this still mean it is a pointer?
Let's say I have this standard method(as in, this is the way most methods look):
-(void)method:(NSString*)s;
then I would send something like this:
NSString *string = #"Hello";
[self method:string];
Would I save data time or allocation or access or whatever by doing everything like this:
-(void)method:(NSString**)s;
//and use it like this:
NSString *string = #"Hello";
[self method:&string];
Or is it a waste? If they are already pointers, I would guess they would be pointing to pointers this way.
?
Objects themselves aren't pointers, they're areas of memory with data in them. The variables you're talking about are pointers to those objects:
NSString *string = #"Hello";
Here, 'string' is a pointer to anNSString literal object. When passing this as a variable you're passing a reference to the object. The contents of the object (if it's a mutable object, which NSString isn't) can be edited, but the reference can't.
By using NSString ** you're adding an extra level of indirection. Now you're passing a pointer to a pointer. The only reason you want to do that is when you want to allow the object (if there is one) to be edited as well as the reference to the object. For example, when calling a method that optionally returns an error:
NSError *error = nil;
[someone doStuff:(NSString *)string error:(NSError **)error];
Now someone can instantiate an NSError and return it to you.
You should not use pointers to pointers unless this is your intention.
I need to understand why in this code i get a memory leak if i assign value to ws_data variable using self.ws_data and not if i use only ws_data.
self.ws_data is a #property (copy)NSString *, released on dealloc.
dispatch_queue_t ws_queue = dispatch_queue_create("ws check win", NULL);
dispatch_async(ws_queue, ^{
self.ws_data = [[NSString alloc]initWithContentsOfURL:url];
});
dispatch_release(ws_queue);
Thank you!
self.variableName accesses the variable through its accessors. Because your ws_data NSString is set to copy in its property declaration, the string is retained when you set it through that declaration.
Not using self. references the ivar without going through those accessors. It also means the variable isn't copied, so it will vanish when references to it disappear (like at the end of your method).
Odds are you want the copy. You just need to release it when you're done, as you do in your dealloc.
Are you retaining it somewhere else, making this access a second retention?
If I understand things correctly (and I quite possibly don't as I've not be doing iOS development for very long at all) in this instance, as you're using the copy attribute on the property, what you're doing when you use self.ws_data is effectively calling the copy method on an already alloced NSString, which is creating a separate instance of the NSString with a retain count of one.
However, the original NSString (that's being alloced in your above sample) isn't being released at any point, hence the leak.
You could use...
self.ws_data = [[[NSString alloc]initWithContentsOfURL:url] autorelease];
...instead, I'd have thought.