stringWithFormat 和 initWithFormat 在 ARC 中产生不同的值
Posted
技术标签:
【中文标题】stringWithFormat 和 initWithFormat 在 ARC 中产生不同的值【英文标题】:stringWithFormat and initWithFormat result different values in ARC 【发布时间】:2016-06-27 09:00:15 【问题描述】:我是 Objective C 和 ios 的新手。当使用 NSString stringWithFormat 和 initWithFormat 时,我对 2 个结果值感到困惑,如下所示:
NSString* str0 = @"Hello";
__weak NSString* str1 = [[NSString alloc] initWithFormat:@"%@", str0 ]; // warning: Assigning retained object to weak variable; object will be released after assignment
__weak NSString* str2 = [NSString stringWithFormat:@"%@",str0];
NSLog(@"Result: str0 %@, str1 %@, str2 %@", str0, str1, str2);
输出:结果:str0 Hello, str1 (null), str2 Hello
环顾网络,这些调用者的答案在 ARC 示例下是相同的:stringWithFormat vs initWithFormat under ARC
但是使用上面的代码,str2 在这里似乎没有弱或强的含义,即我可以删除 str2 声明的 __weak 并得到相同的结果。
我担心的是,如果由 stringWithFormat 创建的字符串归框架所有(或其他方式超出用户应用程序的范围)?
【问题讨论】:
我尝试了您的代码并为所有字符串获取了"Hello"
。
当你使用一个弱变量并且没有对对象的其他引用时,你期望什么?
@gnasher729 如果 str2 声明为 __weak,我预计它为 null,如果 strong 则为“Hello”。但似乎 stringWithFormat 对这些声明没有影响(如 Rob 的回答)。
【参考方案1】:
str1
为空,因为你只有对字符串的弱引用,所以 ARC 可能会立即释放它,因为你没有对它的强引用。事实上,编译器甚至可能会警告你:
警告:将保留对象分配给弱变量;赋值后对象会被释放。
str2
是 Hello
,因为 stringWithFormat
创建和“自动释放”对象(没有强引用的对象,但在池耗尽之前不会释放,即在您返回操作系统之后)。它不是“由框架拥有的”,只是还没有被释放。
请注意,使用NSString
查看内存管理有时可能会出现问题(尤其是在处理字符串文字时),因为存在有时会影响其内存管理的内部优化,但您向我们展示的这些结果正是您所期望的.
鉴于非典型的NSString
内存管理,您可能希望使用自己的自定义对象来观察这种模式:
@interface CustomObject: NSObject
+ (instancetype)customObject;
@end
@implementation CustomObject
+ (instancetype)customObject
return [[self alloc] init];
@end
和
__weak CustomObject *obj1 = [[CustomObject alloc] init];
__weak CustomObject *obj2 = [CustomObject customObject];
NSLog(@"obj1=%@; obj2=%@", obj1, obj2);
您会看到在这种情况下obj1
将始终为nil
,但obj2
不是。 basic rule of memory management 是您拥有任何名称以“alloc”、“new”、“copy”或“mutableCopy”开头的对象。因此,这些对象的所有权已转移给您,ARC 将为您释放它们(即您只有一个 weak
引用,因此它会立即释放)。名称以除这些前缀以外的任何内容开头的对象,没有将所有权传递给您(因此,在 Objective-C 中,已作为自动释放对象传递给您的应用程序,如果在池耗尽时没有强引用,这些对象将被释放。
对于你问题的根源,对象是否“由框架拥有”,答案一般是否定的,但也有例外。例如NSString
有一些优化可以保留字符串引用。同样,还有UIImage
方法,特别是缓存图像的imageNamed
。等等。
但是,一般来说,您不必担心操作系统在做什么。只要确保你解决了你自己的强引用并让操作系统做它会做的事情。通常,如果它进行缓存,无论如何都会在内存压力下清除这些缓存。
【讨论】:
感谢您的回答。我不知道 stringWithFormat 返回一个 'autorelease' 对象。我从 Apple 指南中检查的内容如下: 返回值 通过使用格式作为模板创建的字符串,剩余的参数值在没有任何本地化的情况下被替换。 任何不以alloc
、new
、copy
或mutableCopy
开头的方法都会在Objective-C 中返回自动释放对象。我听说有人争辩说这种行为可能会根据编译器优化设置而改变,但这不是我的经验。【参考方案2】:
我对你的问题的回答
如果您想对某人应该拥有的属性具有弱属性 已经保留了
.h
@property (nonatomic, weak) NSString *str1;
@property (nonatomic, weak) NSString *str2;
.m
@synthesize str1,str2;
-(void)viewDidLoad
NSString* str0 = @"Hello";
NSString* str1 = [[NSString alloc] initWithFormat:@"%@", str0 ];
NSString* str2 = [NSString stringWithFormat:@"%@",str0];
NSLog(@"Result: str0 %@, str1 %@, str2 %@", str0, str1, str2);
输出
Result: str0 Hello, str1 Hello, str2 Hello
Graver answer here
来自Apple Forum Document的详细解释
不同之处在于返回值是如何进行内存管理的。 alloc/initWithFormat:返回一个保留的字符串,你必须 释放或自动释放自己。 stringWithFormat:返回一个字符串 已经自动发布。 (一个自动释放的对象将是 当前自动释放池消失时释放;这通常 在处理下一个事件之前发生。)
您应该使用哪个取决于您对字符串所做的操作。如果 它是一个临时字符串,如果它要存储在另一个字符串中 对象,或者如果它将使用 "self.property" 语法,使用 stringWithFormat:;如果会 分配给全局、静态或实例变量(不使用 "self.property"),使用 alloc/initWithFormat:.
在你的情况下,你可以选择任何一种方式。 stringWithFormat:将允许 你删除显式的释放调用,但自动释放也是 比发布慢一点。由您决定是否 您希望您的代码更小更简单或更长更快。
【讨论】:
感谢您对详细讨论的引用。 欢迎兄弟-:)【参考方案3】:弱变量不包含对对象的引用。当对象被释放时,weak 变量将被设置为 nil。由于您在其他地方没有参考,这可能随时发生。
另一方面,iOS 可以随意保留其他引用。所以不能保证弱变量会被设置为 nil。因此,根据您的代码,每个变量可能是 nil,或者可能不是 nil 并且具有值“Hello”。
这就是你得到的。每个变量都可以设置为 nil 或包含“Hello”,这就是发生的事情。
【讨论】:
【参考方案4】:@gnasher729 :我希望知道在哪种情况下指向 NSString stringWithFormat 的弱/强指针使用正确与否,而与 NSString alloc、init 有很大不同。 我认为清楚地理解它将有助于确定我何时以及如何正确使用它。 顺便说一句,我同意@Rob 和@user3182143 的观点,即 NSString stringWithFormat 会产生一个自动释放的对象。 为了证明这一点,我确实更改为强制释放该对象,现在很清楚:
NSString* str0 = @"Hello";
__weak NSString* str1 = [[NSString alloc] initWithFormat:@"%@", str0 ];
__weak NSString* str2_weak = nil;
NSString* str2 = nil;
@autoreleasepool
str2_weak = [NSString stringWithFormat:@"%@",str0];
str2 = [NSString stringWithFormat:@"%@",str0];
NSLog(@"Result: str0 %@, str1 %@, str2_weak %@ str2 %@", str0, str1, str2_weak, str2);
结果:str0 Hello, str1 (null), str2_weak (null) str2 Hello
使用上面的修改代码,一个自动释放池块来覆盖stringWithFormat的结果,str2_weak保持对自动释放对象的弱引用,因此在块之后它应该为空; str2 实际上保留了自己的字符串,因此不应释放。 输出现在对我来说“有意义”:
【讨论】:
以上是关于stringWithFormat 和 initWithFormat 在 ARC 中产生不同的值的主要内容,如果未能解决你的问题,请参考以下文章
何时使用 NSString stringWithFormat;
NSString - stringWithFormat 自动释放
NSString stringWithFormat 无法添加反斜杠