NSString *strongObj = [[NSString alloc] init]; //alloc and initialze a new instance
strongObj = #"some value"; //set value to a instance
__weak NSString *weakObj = strongObj; //refer strong pointer to weak obj
strongObj = nil; //set strong object to nil and remove it from memory
strongObj = #"some value 2"; //assign a different value to strong obj
weakObj ; // weak obj still holds // #"some value" // this value event i've set the week obj pointer to nil
pls look at the above code and comments, the comments are my views/assumptions. Pls clarify.
Thanks
Although you are creating a strongObj instance, you are actually not using the same instance, but are reassigning the pointer to the string literal #"some value", whatever address that is. It is by no means the same instance that you allocate on the first line of your code.
Now when you assign weakObj, you will actually point it to the same string literal #"some value".
What basically happens is that weakObj is not following the strongObj, but it is following the string literal, which is not deallocated. That is why you still see "some value" in there while strongObj has been set to nil.
String literals are always alive and are never deallocated in runtime. Weak pointer is nullified when memory on which it points is deallocated, so you incorrectly expect your weakObj to be nullified.
To make it work, instead of literal strings you should use
[[NSString alloc] initWithFormat:#"some value %d", 1];
e. g.
NSString *strongObj = [[NSString alloc] initWithFormat:#"some value %d", 1]; //alloc and initialze a new instance
__weak NSString *weakObj = strongObj; //refer strong pointer to weak obj
strongObj = nil; //set strong object to nil and remove it from memory
weakObj ;
Related
While playing around with ARC, I noticed that when I have a weak String:
#property (weak, nonatomic) NSString *myString;
And then if I were to do this:
self.myString = [[NSString alloc] init];
or even
[[NSString alloc] initWithString:#""]
Xcode immediately warns me "Assigning retained object to weak property; object will be released after assignment". And while I can understand that, because its reference count is 0, why does this work with no warnings:
self.myString = #"";
What difference does it make for ARC?
Because when you have a weak property, ARC does not increase the reference count...
So when you write:
self.myString = [[NSString alloc] init];
ARC adds a release just after, because it is the same as writing:
[[NSString alloc] init];
The main difference with #"" it is that it's a static string, in a way retained somewhere else...
Each time you use #"" it points to the same object.
Edit: The difference between #"" and [[NSString alloc] initWithString:#""] is that the first is a static string and is processed at compile time. If it is used elsewhere the other use will point to the same static string. The second, is processed at runtime. It really creates a new object, with its retain count and so on... That's why ARC does its job for the created instance (you specifically call a alloc) and why it does not care for static strings.
For ' Ok, so why does it show the same warning if I do this: [[NSString alloc] initWithString:#""] ? '
When you use [[NSString alloc] initWithString:#""], system will create a new string with #"" and it will be allocated in memory heap.
According to ARC in iOS, an object must have at least one strong reference to stay in memory, when there is no strong reference (ie. reference count becomes 0), the object will be deallocated from memory and we will have no longer access to the object.
But I am getting strange behavior in my code.
I am assigning to weak reference NSString in code,
when I write [[NSString alloc] init]; Xcode give warning .
__weak NSString *str;
str = [[NSString alloc] init];
Assigning retained object to weak property; object will be released after assignment.
if I do like this, Xcode doesn't gives any warning,
__weak NSString *str;
str = #"abcd";
NSLog(#"%#", str);
Output:
abcd
My Question is:
Why it is printing "abcd" as output. even if str is a weak reference variable. Who is keeping this NSString object which value is "abcd" in memory?
When you say str = #"abcd", you're not using a code pattern that the compiler recognizes as returning a newly-allocated object, so you don't trigger the warning about a direct assignment of a new object to a __weak variable.
Furthermore, a string literal like #"abcd" is stored in your program's executable file. It's never deallocated. The retain and release operations don't actually change its retain count. Its retain count is set to a magic number indicating an immortal object. So your __weak variable str doesn't actually get set to nil, because the object it references doesn't get deallocated. That's why it prints abcd.
In fact, clang specifically suppresses a warning if you assign a string literal (as opposed to some other kind of literal like an array literal #[a, b, c]). See the comment in the clang source code:
static bool checkUnsafeAssignLiteral(Sema &S, SourceLocation Loc,
Expr *RHS, bool isProperty) {
// Check if RHS is an Objective-C object literal, which also can get
// immediately zapped in a weak reference. Note that we explicitly
// allow ObjCStringLiterals, since those are designed to never really die.
RHS = RHS->IgnoreParenImpCasts();
// This enum needs to match with the 'select' in
// warn_objc_arc_literal_assign (off-by-1).
Sema::ObjCLiteralKind Kind = S.CheckLiteralKind(RHS);
if (Kind == Sema::LK_String || Kind == Sema::LK_None)
return false;
S.Diag(Loc, diag::warn_arc_literal_assign)
<< (unsigned) Kind
<< (isProperty ? 0 : 1)
<< RHS->getSourceRange();
return true;
}
So if we change the type to NSArray and use an array literal, we get a warning:
Moving on… You get the warning when you say str = [[NSString alloc] init] because the compiler recognizes that [[NSString alloc] init] is a code pattern that typically returns a new object.
However, in the particular case of [[NSString alloc] init], you'll discover that str again doesn't get set to nil. That's because -[NSString init] is special-cased to return a global empty-string object. It doesn't actually make a new object on each call.
__weak NSString *str;
str = [[NSString alloc] init];
NSLog(#"%ld %p [%#]", CFGetRetainCount((__bridge CFTypeRef)str), str, str);
Output:
2018-01-24 01:00:22.963109-0600 test[3668:166594] 1152921504606846975 0x7fffe55b19c0 []
That 1152921504606846975 is the magic retain count indicating an immortal object.
#define TLog(_var) ({ NSString *name = ##_var; NSLog(#"%#: %# -> %p: %# retainCount:%ld", name, [_var class], _var, _var, CFGetRetainCount((__bridge CFTypeRef)(_var))); })
__weak NSString *str;
str = #"abcd";
NSLog(#"%#",str
);
TLog(str);
After debug with your code I found that [str class] is NSCFConstantString and it's retainCount is 1152921504606846975.
for the retainCount in Objective-C, If the object's retainCount equals 1152921504606846975, it means "unlimited retainCount", this object can not be released though it is assigning to weak reference.
All __NSCFConstantString object's retainCount is 1152921504606846975, which means __NSCFConstantString will not be released whether it is __weak. The NSString created using the *str = #"abcd"; will be the same object if they are same value whether how many times to be written.
I am new to objective C and iOS. I am confused with 2 resulting values when using NSString stringWithFormat and initWithFormat like bellow:
NSString* str0 = #"Hello";
__weak NSString* str1 = [[NSString alloc] initWithFormat:#"%#", str0 ]; // warning: Assigning retained object to weak variable; object will be released after assignment
__weak NSString* str2 = [NSString stringWithFormat:#"%#",str0];
NSLog(#"Result: str0 %#, str1 %#, str2 %#", str0, str1, str2);
Output: Result: str0 Hello, str1 (null), str2 Hello
Looking around network, the answers for these callers are same under ARC example: stringWithFormat vs initWithFormat under ARC
However with above code, the str2 seems to not meaning regardless weak or strong here, i.e. I can remove __weak of str2 declaration and makes same result.
My concern is that, if the creation of string by stringWithFormat is owned by framework (or by some other way out of scope of user application)?
The str1 is null, because you only have weak reference to the string, so ARC may deallocate it immediately because you have no strong references to it. In fact, compiler may even warn you of this:
warning: assigning retained object to weak variable; object will be released after assignment.
The str2 is Hello because stringWithFormat creates and "autorelease" object (an object with no strong references, but that is not deallocated until the pool is drained, i.e. after you yield back to the OS). It's not "owned by the framework", but merely hasn't be deallocated yet.
Note, looking at memory management with NSString can sometimes be problematic (especially when dealing with string literals) because there are internal optimizations that sometimes affect its memory management, but these results you show us are precisely what you'd expect.
Given the atypical NSString memory management, you may want to observe this pattern using your own, custom object:
#interface CustomObject: NSObject
+ (instancetype)customObject;
#end
#implementation CustomObject
+ (instancetype)customObject {
return [[self alloc] init];
}
#end
And
__weak CustomObject *obj1 = [[CustomObject alloc] init];
__weak CustomObject *obj2 = [CustomObject customObject];
NSLog(#"obj1=%#; obj2=%#", obj1, obj2);
You will see that obj1 will consistently be nil in this case, but obj2 is not. The basic rule of memory management is that you own any object whose names start with “alloc”, “new”, “copy”, or “mutableCopy”. Thus, these objects have ownership transferred to you and ARC will release them for you (i.e. you only have one weak reference, so it's immediately released). Objects whose names start with anything besides those prefixes, have not passed ownership to you (and thus, in Objective-C, have been passed to your app as autorelease objects that will be deallocated if there are no strong references when the pool is drained.
To the root of your question, whether the object is "owned by the framework", the answer is generally no, but there are exceptions. For example NSString has some optimizations that keeps string references around. Likewise there are UIImage methods, notably imageNamed that cache images. Etc.
But, generally, you shouldn't worry about what the OS is doing. Just make sure you resolve your own strong references and let the OS do what it will do. Generally, if it does caching, these caches are purged upon memory pressure, anyway.
My answer to your question
If you want to have a weak attribute to a property someone should have
already retained it
.h
#property (nonatomic, weak) NSString *str1;
#property (nonatomic, weak) NSString *str2;
.m
#synthesize str1,str2;
-(void)viewDidLoad
{
NSString* str0 = #"Hello";
NSString* str1 = [[NSString alloc] initWithFormat:#"%#", str0 ];
NSString* str2 = [NSString stringWithFormat:#"%#",str0];
NSLog(#"Result: str0 %#, str1 %#, str2 %#", str0, str1, str2);
}
Output
Result: str0 Hello, str1 Hello, str2 Hello
Graver answer here
Detailed Explanation from Apple Forum Document
The difference is in how the return values are memory-managed.
alloc/initWithFormat: returns a retained string that you will have to
release or autorelease yourself. stringWithFormat: returns a string
that has already been autoreleased. (An autoreleased object will be
released when the current autorelease pool disappears; that usually
happens right before the next event is processed.)
Which you should use depends on what you're doing with the string. If
it's a temporary string, if it's going to be stored inside another
object, or if it's going to be stored in your own object using the
"self.property" syntax, use stringWithFormat:; if it's going to be
assigned to a global, static or instance variable (without using
"self.property"), use alloc/initWithFormat:.
In your case, you could go either way. stringWithFormat: would allow
you to remove the explicit release call, but autoreleasing is also
very slightly slower than releasing. It's up to you to decide whether
you want your code to be smaller and simpler or longer and faster.
A weak variable does not hold a reference to an object. The weak variable will be set to nil when the object is deallocated. Since you hold no reference elsewhere, that could happen at any time.
On the other hand, iOS is free to hold other references as it likes. So there is no guarantee that the weak variable will be set to nil. Therefore, according to your code, each variable may be nil, or may be not nil and have the value "Hello".
And that's what you got. Each variable can be set to nil or contain "Hello", and that's what happened.
#gnasher729 : I expected to know in which case the weak/strong pointer to NSString stringWithFormat use correct or not, while it is very different with NSString alloc, init.
I think that to understand it clearly that would help to make sure when and how I could use properly.
By the way, I agreed with #Rob and #user3182143 that NSString stringWithFormat results an autoreleased object.
To prove that, I did change to force release that object and it now is pretty clear:
NSString* str0 = #"Hello";
__weak NSString* str1 = [[NSString alloc] initWithFormat:#"%#", str0 ];
__weak NSString* str2_weak = nil;
NSString* str2 = nil;
#autoreleasepool {
str2_weak = [NSString stringWithFormat:#"%#",str0];
str2 = [NSString stringWithFormat:#"%#",str0];
}
NSLog(#"Result: str0 %#, str1 %#, str2_weak %# str2 %#", str0, str1, str2_weak, str2);
Result: str0 Hello, str1 (null), str2_weak (null) str2 Hello
with above modified code, an autoreleasepool block to cover the result of stringWithFormat, str2_weak keep a weak reference to the autoreleased object hence it shall be null after the block; str2 actually keeps own the string hence shall be not released.
The output is now 'make sense' to me:
NSString __weak *string = [[NSString alloc] initWithFormat:#"First Name: %#", #"Braj"];
NSLog(#"string:%#", string);
NSString __weak *string2=#"Hello";
NSLog(#"string:%#", string2);
NSString __weak *string3 =[[NSString alloc]initWithString:#"Braj"];
NSLog(#"string:%#",string3);
Here, first string gives output null and other two string objects gives ans as Hello and Braj respectively.
I am confused here how to deal __weak reference.
"weak" means the pointer variable doesn't hold a reference to the object, so the pointer allows the object to be deallocated. When that happens, the pointer is set to nil.
So the first NSLog does what you should expect. There is no strong reference to the object, so it becomes nil immediately.
In the second case, you have a string literal. String literals are different: They actually never go away. They are created using some clever trickery which means no memory would be saved if they were deallocated.
The third case: [[NSString alloc] initWithString:aString] is clever. It doesn't actually allocate a new string if it isn't necessary! If aString is an immutable string, then the result is aString and not a new object. You passed a string literal to it, so the result is that string literal. Back to case 2 :-)
weak references can become nil when someone else releases the last strong reference to an object. But your code never knows who might hold a strong reference (behind the scenes), so you can never rely on a weak reference becoming nil.
1) Disabled ARC.
2) I have the following code:
- (IBAction)btnsecondClicked:(id)sender {
NSString *myname;
myname = retrieveName();
}
NSString * retrieveName()
{
NSString *tmpStr = [[NSString alloc] initWithString "StackOverFlow"];
return tmpStr;
}
I tried with Analyser, and it says
"Object Leaked: allocated object is not referenced later in this execution path and has
a retain count of +1"
point at line next to line where retrieName was invoked.
My question:
What is the retain count of the the object ? Should not it be to 2 ?
because:
the first reference count is in retrieveName()
2.the second count is in btnsecondClicked(), where myname variable holds ?
ie: myname = retrievedName () -> Does not it increase the reference count ?
In this step, you've created string NSString *tmpStr = [[NSString alloc] initWithString "StackOverFlow"];. So you've +1 reference count, because always alloc: returns object with +1 count. Then where will you release this? That's why It shows as leak.
Think in terms of ownership — focus on the semantics of what you want to say, not the way the implementation has been written. This is object-oriented programming.
Whomever calls alloc gets an owning reference. Anybody that calls retain establishes ownership. So every call to either alloc or retain must be matched by a release — either an immediate release or an autorelease if you want it taken care of autonomously.
The norm for results on the stack is to autorelease. That's even enshrined in the rule that factory methods (e.g. [NSString +string] return such a non-owning reference.
No, because passing an object around does not add nothing to the retain count.
However, if you did
- (IBAction)btnsecondClicked:(id)sender {
NSString *myname;
myname = [retrieveName() retain];
}
Then the retain count would increase to 2, because you are explicitly declaring ownership on that particular object.
- (IBAction)btnsecondClicked:(id)sender {
NSString *myname; // nil object, no retain count
myname = retrieveName(); // we received the object with retain count 1 (not 2 because you are not sending `retain` to the received object)
} // you exited the function without having global reference on the object, mynames is lost because it was created inside the function, i.e. you leaked at this step
NSString * retrieveName()
{
NSString *tmpStr = [[NSString alloc] initWithString "StackOverFlow"]; // retain count is 1
return tmpStr; // tmpStr returned with retain count of 1
}
Now here is the code fixed
- (IBAction)btnsecondClicked:(id)sender {
NSString *myname;
myname = retrieveName(); // retain count = 1 and the object is the autorelease pool
}// you exited the function, autorelease pool automatically drained, the object is released, no leak
NSString * retrieveName()
{
NSString *tmpStr = [[NSString alloc] initWithString "StackOverFlow"];
return [tmpStr autorelease]; // the object with retain count=1, and added to releasePool
}