将对象分配给 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"
作为输出。即使 str
是 weak 参考变量。谁在内存中保留此 NSString
对象,其值为 "abcd"
?
当你说 str = @"abcd"
时,你没有使用编译器识别为 returning 新分配对象的代码模式,因此你不会触发有关直接分配的警告将新对象分配给 __weak
变量。
此外,像 @"abcd"
这样的字符串文字存储在程序的可执行文件中。它永远不会被释放。 retain
和 release
操作实际上并没有改变它的保留计数。它的保留计数设置为一个表示不朽对象的幻数。所以你的 __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
无论写入多少次,如果它们的值相同,则它们将是同一个对象。
根据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"
作为输出。即使 str
是 weak 参考变量。谁在内存中保留此 NSString
对象,其值为 "abcd"
?
当你说 str = @"abcd"
时,你没有使用编译器识别为 returning 新分配对象的代码模式,因此你不会触发有关直接分配的警告将新对象分配给 __weak
变量。
此外,像 @"abcd"
这样的字符串文字存储在程序的可执行文件中。它永远不会被释放。 retain
和 release
操作实际上并没有改变它的保留计数。它的保留计数设置为一个表示不朽对象的幻数。所以你的 __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
无论写入多少次,如果它们的值相同,则它们将是同一个对象。