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

时间:2018-01-24 06:34:48

标签: ios objective-c xcode automatic-ref-counting nsobject

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

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

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

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

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

Xcode warning screenshot

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

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

No Warning Screenshot

输出:  ABCD

Output Screenshot

我的问题是 为什么它打印“abcd”作为输出。即使str是一个弱引用变量。谁将此NSString对象保留在内存中的值为“abcd”?

2 个答案:

答案 0 :(得分:5)

当您说str = @"abcd"时,您没有使用编译器识别为返回新分配的对象的代码模式,因此您不会触发有关将新对象直接分配给a的警告__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并使用数组文字,我们会收到警告:

array literal assignment warning

继续......当你说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);

输出:

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

1152921504606846975是表示不朽物体的魔法保留计数。

答案 1 :(得分: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);

使用您的代码进行调试后,我发现[str class]NSCFConstantString,而且它的retainCount为1152921504606846975

对于Objective-C中的retainCount,如果对象的retainCount等于1152921504606846975,则表示&#34;无限制retainCount&#34;,虽然该对象分配给弱引用但无法释放

所有__NSCFConstantString对象的retainCount为1152921504606846975,这意味着__NSCFConstantString无论是 __ weak 都不会被释放。使用NSString创建的*str = @"abcd";将是相同的对象,如果它们是相同的值,无论要写多少次。