我对此代码有疑问:
__strong NSString *yourString = @"Your String";
__weak NSString *myString = yourString;
yourString = nil;
__unsafe_unretained NSString *theirString = myString;
NSLog(@"%p %@", yourString, yourString);
NSLog(@"%p %@", myString, myString);
NSLog(@"%p %@", theirString, theirString);
我希望此时所有指针都是nil
,但它们不是,我不明白为什么。
第一个(强)指针是nil
,但其他两个不是。那是为什么?
答案 0 :(得分:30)
<强> TL; dr:问题是字符串文字永远不会被释放,所以你的弱指针仍然指向它。
强变量将保留它们指向的值。
弱变量不会保留其值,当取消分配值时,它们会将指针设置为nil(为安全起见)。
不安全的未记录值(因为您可能通过名称读取)将不会保留该值,如果它被取消分配,则它们不会对此做任何事情,可能指向一块坏内存
使用@"literal string"
创建字符串时,它将成为一个永远不会改变的字符串文字。如果在应用程序的许多位置使用相同的字符串,则它始终是同一个对象。字符串文字不会消失。使用[[NSString alloc] initWithString:@"literal string"]
不会产生任何影响。因为它成为指向文字字符串的指针。然而值得注意的是[[NSString alloc] initWithFormat:@"literal string"];
的工作方式不同,并且会释放其字符串对象。
__strong NSString *yourString = @"Your String";
您正在创建一个指向字符串的强指针。这将确保价值不会消失。 在你的情况下它有点特别,因为字符串是一个字符串文字,技术上不会被释放 。
__weak NSString *myString = yourString;
您创建一个指向与强指针相同的弱指针。如果此时强指针指向其他东西,它指向的值将被释放,然后弱指针将改变其值,使其指向nil
。现在它仍然指向与强指针相同。
yourString = nil;
你的强指针指向nil
。没有任何东西指向旧字符串,所以如果不是因为它是一个文字字符串,它应该被释放。如果您尝试与自己创建的其他对象完全相同,则弱变量将更改为指向nil
。但是,因为字符串文字是文字的,不会消失。弱变量仍将指向它。
__unsafe_unretained NSString *theirString = myString;
创建一个新的未保留指针,指向指向字符串文字的弱指针。
NSLog(@"%p %@", yourString, yourString);
NSLog(@"%p %@", myString, myString);
NSLog(@"%p %@", theirString, theirString);
你打印所有的字符串并混淆为什么第一个值是nil
但其他两个不是。{/ p>
What's the difference between a string constant and a string literal?
答案 1 :(得分:3)
大卫在答案中100%正确。我刚刚使用GHUnit添加了四个明确的示例。
使用NSObject
作为所有对象的代理,生命周期限定符的行为符合预期。
- (void) test_usingNSObjects
{
NSObject *value1 = [[NSObject alloc] init];
NSObject *value2 = [[NSObject alloc] init];
NSObject *value3 = [[NSObject alloc] init];
__strong NSObject *sRefToValue = value1;
__weak NSObject *wRefToValue = value2;
__unsafe_unretained NSObject *uRefToValue = value3;
value1 = value2 = value3 = nil;
GHAssertNotNil(sRefToValue,
@"Strong reference to the object that was originally \
assigned to value1. Even though value1 was set to nil, the \
strong reference to the object keeps the object from being \
destroyed.");
GHAssertNil(wRefToValue,
@"Weak reference to the object that was originally assigned to \
value2. When value2 was set to nil, the weak reference does \
not prevent the object from being destroyed. The weak \
reference is also set to nil.");
// Removing the #ifdef and #endif lines will result in a EXC_BAD_ACCESS
// signal. Receiving a EXC_BAD_ACCESS signal is the expected behavior for
// that code.
#ifdef RECIEVE_EXC_BAD_ACCESS
GHAssertNotNil(uRefToValue,
@"Unsafe unretained reference to the object that was \
originally assigned to value3. When value3 was set to nil, \
the unsafe unretained reference does not prevent the object \
from being destroyed. The unsafe unretained reference is \
unaltered and the reference is invalid. Accessing the \
reference will result in EXC_BAD_ACCESS signal.");
#endif
// To avoid future EXC_BAD_ACCESS signals.
uRefToValue = nil;
}
NSString
的生命周期限定符行为(@“某事”)。这与test_usingNSObjects
基本相同,但不使用NSObject
,而是使用分配了文字字符串的NSString
。由于文字字符串不会像其他对象一样被破坏,因此可以观察到__weak
和__unsafe_unretained
变量的不同行为。
- (void) test_usingLiteralNSStrings
{
NSString *value1 = @"string 1";
NSString *value2 = @"string 2";
NSString *value3 = @"string 3";
__strong NSString *sRefToValue = value1;
__weak NSString *wRefToValue = value2;
__unsafe_unretained NSString *uRefToValue = value3;
value1 = value2 = value3 = nil;
GHAssertNotNil(sRefToValue,
@"Strong reference to the object that was originally \
assigned to value1. Even though value1 was set to nil, \
literal strings are not destroyed.");
GHAssertNotNil(wRefToValue,
@"Weak reference to the object that was originally assigned \
to value2. Even though value2 was set to nil, \
literal strings are not destroyed so the weak reference is \
still valid.");
GHAssertNotNil(uRefToValue,
@"Unsafe unretained reference to the object that was \
originally assigned to value3. Even though value3 was set \
to nil, literal strings are not destroyed so the unsafe \
unretained reference is still valid.");
}
NSString
的生命周期限定符行为。这与test_usingNSObjects
基本相同,但不使用NSObject
,而是使用分配了非文字字符串的NSString
。由于非文字字符串像其他对象一样被破坏,因此行为与test_usingNSObjects
中观察到的行为相同。
- (void) test_usingNonliteralNSStrings
{
NSString *value1 = [[NSString alloc] initWithFormat:@"string 1"];
NSString *value2 = [[NSString alloc] initWithFormat:@"string 2"];
NSString *value3 = [[NSString alloc] initWithFormat:@"string 3"];
__strong NSString *sRefToValue = value1;
__weak NSString *wRefToValue = value2;
__unsafe_unretained NSString *uRefToValue = value3;
value1 = value2 = value3 = nil;
GHAssertNotNil(sRefToValue,
@"Strong reference to the object that was originally \
assigned to value1. Even though value1 was set to nil, the \
strong reference to the object keeps the object from being \
destroyed.");
GHAssertNil(wRefToValue,
@"Weak reference to the object that was originally assigned to \
value2. When value2 was set to nil, the weak reference does \
not prevent the object from being destroyed. The weak \
reference is also set to nil.");
// Removing the #ifdef and #endif lines will result in a EXC_BAD_ACCESS
// signal. Receiving a EXC_BAD_ACCESS signal is the expected behavior for
// that code.
#ifdef RECIEVE_EXC_BAD_ACCESS
GHAssertNotNil(uRefToValue,
@"Unsafe unretained reference to the object that was \
originally assigned to value3. When value3 was set to nil, \
the unsafe unretained reference does not prevent the object \
from being destroyed. The unsafe unretained reference is \
unaltered and the reference is invalid. Accessing the \
reference will result in EXC_BAD_ACCESS signal.");
#endif
// To avoid future EXC_BAD_ACCESS signals.
uRefToValue = nil;
}
NSString
创作 - 文字与非文字。显示以各种方式创建的字符串,如果它们是文字的,也不是非文字的。
- (void) test_stringCreation
{
NSString *literalString = @"literalString";
NSString *referenced = literalString;
NSString *copy = [literalString copy];
NSString *initWithString = [[NSString alloc] initWithString:literalString];
NSString *initWithFormat = [[NSString alloc] initWithFormat:@"%@", literalString];
// Testing that the memory addresses of referenced objects are the same.
GHAssertEquals(literalString, @"literalString", @"literal");
GHAssertEquals(referenced, @"literalString", @"literal");
GHAssertEquals(copy, @"literalString", @"literal");
GHAssertEquals(initWithString, @"literalString", @"literal");
GHAssertNotEquals(initWithFormat, @"literalString",
@"nonliteral - referenced objects' memory addresses are \
different.");
// Testing that the objects referenced are equal, i.e. isEqual: .
GHAssertEqualObjects(literalString, @"literalString", nil);
GHAssertEqualObjects(referenced, @"literalString", nil);
GHAssertEqualObjects(copy, @"literalString", nil);
GHAssertEqualObjects(initWithString, @"literalString", nil);
GHAssertEqualObjects(initWithFormat, @"literalString", nil);
// Testing that the strings referenced are the same, i.e. isEqualToString: .
GHAssertEqualStrings(literalString, @"literalString", nil);
GHAssertEqualStrings(referenced, @"literalString", nil);
GHAssertEqualStrings(copy, @"literalString", nil);
GHAssertEqualStrings(initWithString, @"literalString", nil);
GHAssertEqualStrings(initWithFormat, @"literalString", nil);
}
答案 2 :(得分:-2)
在自动释放池耗尽后,弱属性将仅设置为nil。
尝试:
@autoreleasepool {
_strong NSString *yourString = @"Your String";
__weak NSString *myString = yourString;
yourString = nil;
__unsafe_unretained NSString *theirString = myString;
}
NSLog(@"%p %@", yourString, yourString);
NSLog(@"%p %@", myString, myString);
NSLog(@"%p %@", theirString, theirString);