弱的NSString变量设置的唯一强引用零后不为零弱的NSString变量设置的唯一强引用零后不为零

2019-05-17 09:38发布

我有这个代码的问题:

__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 ,但其他两个不是。 这是为什么?

Answer 1:

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 ,但其他两个不是。


相关阅读:

什么是字符串常量和字符串文字的区别?



Answer 2:

大卫在他的回答正确率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 S(@“东西”)。

这是基本相同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创作-字面VS非字面。

可见以各种方式创建的字符串,如果它们是文字,也非字面。

- (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);
}


Answer 3:

自动释放池倒掉弱属性将仅被设置为零。

尝试:

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


文章来源: Weak NSString variable is not nil after setting the only strong reference to nil