7

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.

Xcode warning screenshot

if I do like this, Xcode doesn't gives any warning,

__weak NSString *str;
str = @"abcd";
NSLog(@"%@", str);

No Warning Screenshot

Output: abcd

Output Screenshot

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?

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
  • Objective-C string literals are treated differently. They are constants kept in memory so none of the normal memory management rules apply to them. – rmaddy Jan 24 '18 at 07:00
  • Probably a duplicate of https://stackoverflow.com/questions/10922888/weak-nsstring-variable-is-not-nil-after-setting-the-only-strong-reference-to-nil – rmaddy Jan 24 '18 at 07:02
  • I don't think it's a duplicate, because it's interesting to understand why there's a warning in one case but not the other. – rob mayoff Jan 24 '18 at 07:19

2 Answers2

6

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:

array literal assignment 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.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • That means in both cases object will never be deallocated ? – Prashant Tukadiya Jan 24 '18 at 07:24
  • Yes, in both cases you are setting `str` to an immortal object. – rob mayoff Jan 24 '18 at 07:25
  • @robmayoff this would mean, the @"abcd", which itself is an object, will stay in the memory as long as the scope(a class or a method) in which it is defined in. But it will be nullified as soon as its parent gets out of the picture. Or since the retain count is such a big number, it will never go out and keep eating the memory? Because even if the parent gets out it will only decrease the retain count by 1 – nr5 Oct 21 '18 at 05:04
0
#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.

Dhiru
  • 3,040
  • 3
  • 25
  • 69
Spirits
  • 16
  • 1
  • 4
  • `1152921504606846975` doesn't really mean "unlimited". It is an implementation artifact and may change at any time. In general, `retainCount` is utterly useless. – bbum Jan 27 '18 at 17:49
  • 1
    No worries. In general, one should ignore both the class and the allocation type used for an object that is sourced from the frameworks. Far better to just treat it as a delta'd reference count. I.e. strong is +1 and when the strong goes out of scope or is nil'd it is -1. Let the system do its thing and if you need to debug, use the object graph inspector to determine what is holding a strong reference. – bbum Jan 27 '18 at 23:42