在Objective-c中将对象分配给弱引用?

Ros*_*ari 6 xcode objective-c nsobject ios automatic-ref-counting

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

但我的代码中出现了奇怪的行为.

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

__weak NSString *str;
str = [[NSString alloc] init];
Run Code Online (Sandbox Code Playgroud)

将保留对象分配给弱属性; 对象将在分配后释放.

Xcode警告截图

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

__weak NSString *str;
str = @"abcd";
NSLog(@"%@",str);
Run Code Online (Sandbox Code Playgroud)

没有警告截图

输出:abcd

输出截图

我的问题是 为什么它打印"abcd"作为输出.即使str是弱参考变量.谁在保持这个NSString对象的值在内存中是"abcd"?

rob*_*off 6

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

此外,字符串文字@"abcd"存储在程序的可执行文件中.它永远不会被解除分配.在retainrelease操作实际上并不改变其保留计数.其保留计数设置为表示不朽对象的幻数.因此,您的__weak变量str实际上并未设置为nil,因为它引用的对象不会被释放.这就是它打印的原因abcd.

事实上,如果你指定一个字符串文字(而不是像数组文字这样的其他文字@[a, b, c]),clang会特别禁止警告.请参阅clang源代码中的注释:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

数组文字赋值警告

继续......当你说str = [[NSString alloc] init]因为编译器识别[[NSString alloc] init]出通常返回一个新对象的代码模式时,你会收到警告.

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

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

输出:

2018-01-24 01:00:22.963109-0600 test[3668:166594] 1152921504606846975 0x7fffe55b19c0 []
Run Code Online (Sandbox Code Playgroud)

1152921504606846975是魔术保留计数,表示不朽的物体.