Objective c 将唯一的强引用设置为nil后,弱NSString变量不是nil

Objective c 将唯一的强引用设置为nil后,弱NSString变量不是nil,objective-c,memory-management,nsstring,automatic-ref-counting,weak-references,Objective C,Memory Management,Nsstring,Automatic Ref Counting,Weak References,我对此代码有问题: __strong NSString *yourString = @"Your String"; __weak NSString *myString = yourString; yourString = nil; __unsafe_unretained NSString *theirString = myString; NSLog(@"%p %@", yourString, yourString); NSLog(@"%p %@", myString, myString); N

我对此代码有问题:

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

tl;dr:问题是字符串文字永远不会被释放,因此您的弱指针仍然指向它


理论 strong变量将保留它们指向的值

变量将不会保留其值,当值被释放时,它们会将指针设置为nil(为了安全起见)

不安全的未恢复的值(您可能可以通过名称读取)将不会保留该值,如果该值被解除分配,它们将不会对其进行任何处理,可能会指向坏内存


文字和常量 当您使用
@“literal string”
创建字符串时,它将成为一个永远不会更改的字符串literal。如果在应用程序中的多个位置使用同一字符串,则它始终是同一个对象。字符串文字不会消失。使用
[[NSString alloc]initWithString:@“literal string”]
不会有什么不同。因为它成为指向文本字符串的指针。但是值得注意的是,
[[NSString alloc]initWithFormat:@“literal string”]的工作方式不同,将释放其字符串对象

逐行: 您正在创建一个指向字符串的强指针。这将确保该值不会消失。在您的例子中,它有点特殊,因为字符串是一个字符串文本,从技术上讲,不会被释放

您创建了一个弱指针,指向与强指针相同的对象。如果此时强指针将指向其他对象,那么它所指向的值将被释放,那么弱指针将更改其值,使其指向
nil
。现在它仍然指向与强指针相同的位置

yourString = nil;
您的强指针指向
nil
。没有任何东西指向旧字符串,因此如果不是因为它是一个文本字符串,它应该被释放。如果您对自己创建的其他对象尝试了完全相同的操作,则弱变量将发生更改,使其指向
nil
。但是,因为字符串文字是文字,不会消失。弱变量仍将指向它

__unsafe_unretained NSString *theirString = myString;
将创建一个新的未恢复指针,指向指向字符串文本的弱指针

NSLog(@"%p %@", yourString, yourString);
NSLog(@"%p %@", myString, myString);
NSLog(@"%p %@", theirString, theirString);
打印所有字符串时,会感到困惑,为什么第一个值是
nil
,而其他两个值不是


相关阅读:
大卫的答案100%正确。我刚刚使用添加了四个显式示例

对象引用的生存期限定符行为。 使用
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;
}
literal
NSString
s(@“something”)的生存期限定符行为。 这与使用NSOBJECTS
测试基本相同,但不是使用
NSObject
,而是使用分配了文字字符串的
NSString
。由于文字字符串不会像其他对象一样被销毁,因此可以观察到
\uu弱
\uu不安全
变量的不同行为

- (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
s的生存期限定符行为。 这基本上与使用NSOBJECTS测试相同,但不是使用
NSObject
,而是使用分配了非文字字符串的
NSString
。由于非文字字符串与其他对象一样被销毁,因此其行为与
test\u 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);
}

只有在自动释放池排空后,弱属性才会设置为nil

尝试:


非常感谢您的解释,因此,如果我这样做:u strong NSString*yourString=[[NSString alloc]initWithString:@“Your String”];,在这个变量设置为nil之后,myString的指针将为nil否?对不起,否。这也将指向字符串文字,这意味着当指向强指针时,弱指针将指向不会消失的字符串文字。哦,我尝试用这个函数initWithUTF8String初始化YourString,结果很好。最后一个问题,出于什么原因,不可能得到与“initWithString”相同的结果?对不起,我无法给出一个很好的答案。事情就是这样。我只能猜测为什么initWithUTF8String的工作方式不同,但这只是我的猜测。这是一个实现细节,但
-initWithString:
基本上是释放调用它的对象,复制参数,然后返回副本。作为另一种优化,不可变值对象通常实现
-copy
以只执行
-retain
,因为无论您是否拥有“真实”副本或只是重新获取相同的对象都无关紧要。因为原始文件是不可变的,副本也是不可变的,所以它们总是一样的。最后,保留字符串文字并没有任何作用,因为它是永久的。所有的组合,
-initWithString:
,给定一个文本,返回相同的文本。
- (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;
}
- (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);
}
@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);