我一直在努力理解iOS中强引用和弱引用之间的区别。我所理解的是:
//.h File
@property(nonatomic,strong) NSString* myStrongString;
@property(nonatomic,weak) NSString* myWeakString;
//.m File
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignTempString];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)assignTempString{
self.myStrongString = [[NSString alloc] initWithString:@"Varun Mehta"];
}
- (IBAction)printAssignedString:(id)sender {
NSLog(@"Object will have strong reference so it will print my name==%@",self.myStrongString);
}
根据我的理解,当我使用myWeakString重复上述步骤时,它应该打印null。但它仍然印着我的名字。任何知道为什么会发生这种事的人。
但当我用[NSString stringWithFormat:@"Varun Mehta"]
或[[NSString alloc] initWithFormat:@"Varun Mehta"]
替换[[NSString alloc] initWithString:@"Varun Mehta"]
时,结果正如我所期望的那样。
这里有几件事需要考虑。
-
一个静态声明的字符串被构建到你的应用程序中,所以它不会被真正保留或释放,因此对
@"my string"
的弱引用总是有效的。编译器只是将[[NSString alloc] initWithString:@"Varun Mehta"]
识别为一个静态字符串,并删除您的alloc/init。然而,根据定义,任何处理格式化的东西都是创建一个新字符串,因此新字符串遵守弱引用规则,并立即被释放,从而消除引用。 -
如果您访问了一个最终在自动释放池中的弱保留对象,那么在所有方法返回并且运行循环返回到另一个循环(从而耗尽自动释放池)之前,它实际上不会被释放,因此即使对象"死了",您也可以继续处理它。这通常仅在与非ARC代码交互时发生。
如果您需要练习,请尝试以下代码:
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignTempString];
}
-(void)assignTempString{
@autoreleasepool
{
self.myStrongString = [NSString stringWithFormat:@"%@", @"Strong string"];
self.myWeakString = [NSString stringWithFormat:@"%@", @"Weak string"];
}
}
- (IBAction)printAssignedString:(id)sender {
NSLog(@"Strong ptr content: %@",self.myStrongString);
NSLog(@"Weak ptr content: %@",self.myWeakString);
}
[NString alloc]将分配一个ARC托管对象,并将其保留计数设置为1。只要视图控制器处于活动状态,此保留计数将为1,因此不会解除分配[NSString字符串WithFormat:]返回一个自动释放的字符串,该字符串在执行[sel-assignTempString]后被释放。
两个方法initWithString
和stringWithFormat
准确地表明了预期结果。所以initWithString
希望您创建分配内存,然后初始化它。而stringWithFormat
希望您只指向字符串。
当您使用强/弱变量执行init
时,它将一直存在到程序结束。当你指向;强文字将保留引用,因此将不允许ARC清除字符串文字,弱文字不会保留引用,因此ARC可以在函数调用后立即清除它。
希望它能澄清对你的工作。
您所经历的一切都是因为NSString的实现方式。
由于NSString对象是不可变的,所以当您使用带有字符串文字作为参数的stringWithString:
时,编译器会使用快捷方式。如果this和其他相关方法的参数是字符串文字,则返回的值将仅指向字符串文字。整个对象实例化都被优化掉了。
字符串文字不会被释放。但是弱变量在dealloc期间仅为nil,因此如果从未调用dealloc,则弱变量永远不会设置为nil。
如果使用stringWithFormat:
,则不会发生这种情况。即使只使用字符串文字作为参数,也会创建新的字符串实例
为什么?很可能是因为苹果认为不值得检查stringWithFormat:
是否与没有任何格式说明符的字符串一起使用。
这是一个实施细节,不要想太久这个决定。它不应该影响您编写的代码。我建议您将每个不是纯文本的字符串(即没有任何NSString方法的@"Foo")视为动态创建的NSString(即使用isEqualToString:
进行所有字符串比较)
此日志记录代码将显示这种重用行为。它将为所有NSString实例显示相同的地址,因为编译器已经优化了对简单@"Foo"
的所有调用。
NSLog(@"%p", @"Foo");
NSLog(@"%p", [[NSString alloc] initWithString:@"Foo"]);
NSLog(@"%p", [NSString stringWithString:@"Foo"]);
NSLog(@"%p", [[NSString stringWithString:@"Foo"] copy]);
NSLog(@"%p", [@"Foo" copy]);
在Xcode的较新版本中,您甚至会收到此代码的良好警告:
将initWithString:与文字一起使用是多余的
使用字符串WithString:带文字是多余的