12

我对这段代码有疑问:

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

4

3 回答 3

31

tl; 博士:问题是字符串文字永远不会被释放,所以你的弱指针仍然指向它。


理论

变量将保留它们指向的值。

变量不会保留它们的值,当值被释放时,它们会将指针设置为 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,而其他两个不是。


相关阅读:

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

于 2012-06-06T22:03:39.287 回答
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文字s (@"something")的生命周期限定符行为。

这与 基本相同test_usingNSObjects,但不是使用 a NSObject,而是使用NSString分配了文字字符串的 a 。由于文字字符串不会像其他对象一样被破坏,因此可以观察到变量__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.");
}

非文字 s的生命周期限定符行为NSString

这与 基本相同test_usingNSObjects,但不是使用 a NSObject,而是使用NSString分配了非文字字符串的 a 。由于非文字字符串像其他对象一样被销毁,因此行为与在 中观察到的相同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);
}
于 2013-04-04T18:02:28.120 回答
-2

弱属性只会在自动释放池耗尽后设置为零。

尝试:

@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);
于 2013-05-08T14:41:30.630 回答