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?
Pointers that are not retained are often referred to as “weak” in Objective-C documentation that predates the garbage collector. These are references that are allowed to persist beyond the lifetime of the object. Unfortunately, there is no automatic way of telling whether they are still valid.
A reference to an object is any object pointer or property that lets you reach the object. There are two types of object reference: Strong references, which keep an object “alive” in memory. Weak references, which have no effect on the lifetime of a referenced object.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With