【问题标题】:Objective-C ARC Strong and Weak references "object will be released after assignment" warning - but it doesn't get releasedObjective-C ARC强和弱引用“对象将在分配后释放”警告-但它没有被释放
【发布时间】:2016-10-11 15:00:32
【问题描述】:

我正在做这个简单的 Objective-C 练习来更好地理解 ARC,它是一个程序,当引用设置为强时应该显示 R2D2,而当引用设置为弱时会失败。但是,代码仍然可以编译,即使有警告说对象将被释放。

main.m

#import <Foundation/Foundation.h>
#import "Robot.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {

        Robot *robot = [[Robot alloc]init];

        robot.firstString =[[NSString alloc]initWithFormat:@"R%d",2];
        robot.secondString = [[NSString alloc]initWithFormat:@"D%d",2];

        NSLog(@"%@%@",robot.firstString,robot.secondString);

    }
    return 0;
}

Robot.m

#import "Robot.h"

@implementation Robot

@end

Robot.h

#import <Foundation/Foundation.h>

@interface Robot : NSObject

@property (strong) NSString *firstString;
@property (weak) NSString *secondString;

@end

谢谢

【问题讨论】:

  • 你对“失败”的定义是什么?更糟糕的情况是你会看到(null) 而不是D2。结果也可能受到编译时优化级别的影响。
  • @rmaddy 这是另一个使用系统类来尝试探索运行时行为的案例。在这种情况下,绊倒标记的指针。一般来说,在探索运行时时,最好从您自己的NSObject 子类开始,这样您就不会对NS* 类的实现细节感到惊讶。

标签: objective-c automatic-ref-counting objective-c-runtime


【解决方案1】:

哦,这很有趣。你遇到了一个标记的指针。

@interface Robot : NSObject

@property (strong) NSString *firstString;
@property (weak) NSString *secondString;

@end

@implementation Robot
@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {

        Robot *robot = [[Robot alloc]init];

        robot.firstString =[[NSString alloc]initWithFormat:@"R%d",2];
        robot.secondString = [[NSString alloc]initWithFormat:@"D%d",2];

        NSLog(@"%@ %@",robot.firstString,robot.secondString);
        NSLog(@"%p %p",robot.firstString,robot.secondString);

        robot.secondString = [[NSString alloc]initWithFormat:@"argy bargy foo foo %d",2];

        NSLog(@"%@ %@",robot.firstString,robot.secondString);
        NSLog(@"%p %p",robot.firstString,robot.secondString);

    }
    return 0;
}

输出:

 R2 D2
 0x325225 0x324425
 R2 (null)
 0x325225 0x0

请注意,您的弱字符串有一个奇数地址。但是分配不能落在奇数地址上!事实上,堆上的分配通常按 16 个字节对齐(尽管静态分配的特殊情况字符串可能只是偶数地址)。

那个奇怪的地址意味着bit 0中有一个1。这表明 标记的指针 正在使用中。也就是说,字符串被编码在对象的地址中,运行时检测到低位被设置,因此,特殊情况下它是标记指针。

实际上,如果你在ASCII表中查找0x320x44,前者是字符2,后者是字符D0x25 是字符串的长度 (2) 和类索引(+ 低位)。

所以,你永远不会看到弱引用归零,因为不涉及分配。如果没有分配,就永远不会有释放,因此字符串引用永远不会无效。

第二个字符串——@"argy bargy foo foo 2——不适合标记指针,因此是堆分配。

【讨论】:

    【解决方案2】:

    编译器很聪明。编译器可以替换

    [[NSString alloc]initWithFormat:@"D%d",2]
    

    @"D2"
    

    这是一个 NSString 常量,永远不会被释放。另一种可能性:优化编译器替换

        Robot *robot = [[Robot alloc]init];
    
        robot.firstString =[[NSString alloc]initWithFormat:@"R%d",2];
        robot.secondString = [[NSString alloc]initWithFormat:@"D%d",2];
    
        NSLog(@"%@%@",robot.firstString,robot.secondString);
    

        Robot *robot = [[Robot alloc]init];
        NSString* tmp1 =[[NSString alloc]initWithFormat:@"R%d",2];
        NSString* tmp2 = [[NSString alloc]initWithFormat:@"D%d",2];
    
        robot.firstString = tmp1;
        robot.secondString = tmp2;
    
        NSLog(@"%@%@",tmp1,tmp2);
    

    然后用

        NSString* tmp1 =[[NSString alloc]initWithFormat:@"R%d",2];
        NSString* tmp2 = [[NSString alloc]initWithFormat:@"D%d",2];
    
        NSLog(@"%@%@",tmp1,tmp2);
    

    然后用

        NSString* tmp1 =@"R2";
        NSString* tmp2 = @"D2";
    
        NSLog(@"%@%@",tmp1,tmp2);
    

    然后用

        NSLog(@"R2D2");
    

    【讨论】:

    • 一般来说,我相信您知道,编译器不能内联或替换 ObjC 方法调用。你确定 Clang 实际上对initWithFormat: 有这样的特殊情况吗?我更怀疑删除Robot 的分配。您认为编译器可以知道这些属性访问器没有被覆盖?它当然不知道我没有在运行时调出它们。
    • 编译器永远无法优化掉一个方法调用。因为每个方法调用都是动态调度的,所以调用该方法的结果无法直到运行时才能确定。
    猜你喜欢
    • 2015-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-01
    • 2012-08-27
    相关资源
    最近更新 更多