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.