将对象分配给 Objective-C 中的弱引用?

Assigning object to weak reference in Objective-C?

根据iOS中的ARC,一个对象必须至少有一个strong引用才能留在内存中,当没有strong引用时(即引用计数变为 0),对象将从内存中释放,我们将无法再访问该对象。

但是我的代码出现了奇怪的行为。

我在代码中分配给 weak 引用 NSString, 当我写 [[NSString alloc] init]; Xcode 时给出警告 .

__weak NSString *str;
str = [[NSString alloc] init];

Assigning retained object to weak property; object will be released after assignment.

如果我这样做,Xcode 不会给出任何警告,

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

输出: abcd

我的问题是:

为什么打印 "abcd" 作为输出。即使 strweak 参考变量。谁在内存中保留此 NSString 对象,其值为 "abcd"

当你说 str = @"abcd" 时,你没有使用编译器识别为 returning 新分配对象的代码模式,因此你不会触发有关直接分配的警告将新对象分配给 __weak 变量。

此外,像 @"abcd" 这样的字符串文字存储在程序的可执行文件中。它永远不会被释放。 retainrelease 操作实际上并没有改变它的保留计数。它的保留计数设置为一个表示不朽对象的幻数。所以你的 __weak 变量 str 实际上并没有设置为 nil,因为它引用的对象没有被释放。这就是它打印 abcd.

的原因

事实上,如果您分配字符串文字(与数组文字等其他类型的文字相反 @[a, b, c]),clang 会专门抑制警告。请参阅 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;
}

因此,如果我们将类型更改为 NSArray 并使用数组文字,我们会收到警告:

继续……当您说 str = [[NSString alloc] init] 时会收到警告,因为编译器识别出 [[NSString alloc] init] 是一种代码模式,通常 return 是一个新对象。

然而,在 [[NSString alloc] init] 的特殊情况下,您会发现 str 再次没有设置为 nil。那是因为 -[NSString init] 是 return 全局空字符串对象的特例。它实际上并没有在每次调用时创建一个新对象。

    __weak NSString *str;
    str = [[NSString alloc] init];
    NSLog(@"%ld %p [%@]", CFGetRetainCount((__bridge CFTypeRef)str), str, str);

输出:

2018-01-24 01:00:22.963109-0600 test[3668:166594] 1152921504606846975 0x7fffe55b19c0 []

那个1152921504606846975是表示不朽对象的魔法保留计数。

#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);

调试你的代码后我发现 [str class]NSCFConstantString 并且它的 retainCount 是 1152921504606846975.

对于Objective-C中的retainCount,如果对象的retainCount等于1152921504606846975,则表示"unlimited retainCount",虽然该对象是弱引用,但无法释放。

所有__NSCFConstantString对象的retainCount为1152921504606846975,也就是说__NSCFConstantString不管是__weak都不会被释放。使用*str = @"abcd";创建的NSString无论写入多少次,如果它们的值相同,则它们将是同一个对象。