BKObject是一个自定义对象,我想将多个BKObject放入数组中。
BKViewController:
#import <UIKit/UIKit.h>
#import "BKObject.h"
@interface BKViewController : UIViewController
@property (strong, nonatomic) NSArray *data;
@property (weak, nonatomic) BKObject *tmpObject;
@end
BKViewController.m:
#import "BKViewController.h"
@implementation BKViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *arr = [[NSMutableArray alloc] init];
for(NSInteger i = 0; i < 100000; i++){
[arr addObject:[[BKObject alloc] initWithName:@""]];
}
self.data = [NSArray arrayWithArray:arr];
__weak BKObject *weakMutableObject = arr[0];
[arr removeAllObjects];
NSLog(@"%@", weakMutableObject); // print out the object, why?
__weak BKObject *weakObject = self.data[0];
self.data = nil;
NSLog(@"%@", weakObject); // print out the object again, but why?
self.tmpObject = [[BKObject alloc] initWithName:@""];
NSLog(@"%@", self.tmpObject); // print null, very clear
}
@end
我很好奇为什么前两个NSLog消息显示一个对象而不是null(如在上一个NSLog中)。 我正在使用最新的Xcode 5.0.1和iOS 7 SDK。
答案 0 :(得分:4)
NSMutableArray *arr = [[NSMutableArray alloc] init];
for(NSInteger i = 0; i < 100000; i++){
[arr addObject:[[BKObject alloc] initWithName:@""]];
}
好的,所以在这一点上,我们有一堆由数组保留的对象。
self.data = [NSArray arrayWithArray:arr];
现在,在这一点上,我们有两堆不同数组保留的对象。
__weak BKObject *weakMutableObject = arr[0];
[arr removeAllObjects];
NSLog(@"%@", weakMutableObject); // print out the object, why?
因为arr[0]
指向的对象也由self.data
保留。
__weak BKObject *weakObject = self.data[0];
self.data = nil;
NSLog(@"%@", weakObject); // print out the object again, but why?
这个有点有趣。 “问题”是arrayWithArray:
正在添加额外的保留/自动释放,因为它们是平衡的,所以可以自由地执行。你可以通过在不同的点消耗自动释放池来证明这一点。
这显示了一个实时对象:
__weak NSObject *weakObject;
self.data = [NSArray arrayWithArray:arr]; // Note outside nested autorelease pool
@autoreleasepool {
...
weakObject= self.data[0];
self.data = nil;
}
NSLog(@"%@", weakObject); // print out the object
这显示nil:
__weak NSObject *weakObject;
@autoreleasepool {
self.data = [NSArray arrayWithArray:arr]; // Note inside nested autorelease pool
...
weakObject= self.data[0];
self.data = nil;
}
NSLog(@"%@", weakObject); // print nil
这里的教训是,您不应该假设对象将在autorelease块中的任何给定点解除分配。这不是ARC给出的承诺。它只承诺对象有效的最小时间。系统的其他部分可以随意添加平衡的保留/自动释放对,这将延迟释放,直到池耗尽。
答案 1 :(得分:2)
这一行:
self.data = [NSArray arrayWithArray:arr];
最终会有两个数组和两个对象的强引用。然后从第一个数组中删除对象,但不从第二个数组中删除。因此,对象仍然有一个强大的参考,并仍然活着。
请记住,当对该对象的所有强引用都被删除时,__weak
将被清零。对于第二个数组,您仍然可以对第一个NSLog
进行强引用。
使用第二个NSLog
,可能存在一个自动释放涉及访问属性,阻止阵列立即释放。 编辑:有关详细信息,请参阅Rob Napier的答案。
使用第三个日志,您正在设置:
self.tmpObject = [[BKObject alloc] initWithName:@""];
self.tmpObject
是弱参考。由于您只对该对象有弱引用,因此该属性会立即归零。
答案 2 :(得分:0)
这可能是因为你仍然在同一个自动发布池中。哪个范围适用于您的功能。尝试在函数范围之外设置弱引用(例如作为属性)并调用函数在另一个函数内创建和释放,然后您应该看到对象的释放。
如果你想在循环中创建和释放很多对象,比如在你的例子中考虑在自定义发布池中这样做。
答案 3 :(得分:0)
这是对象的工作方式。你已经创建了一个被分配了一个内存位置的对象,然后它被放入一个本地的NSArray中,然后跟踪它,然后在最终放入实例变量(self.data)之前将它放入另一个本地数组中。因此,此时您的对象在技术上具有3作为保留计数,因此在您的代码中,您已经释放了两次,这是它在NSLog语句中打印的原因。
尝试使用以下代码:
NSString *a = @"1";
NSMutableArray *arr = [[NSMutableArray alloc] init];
for (int i = 0; i < 10000; i++) {
[arr addObject:a]; // Retain count 1
}
self.myArr = arr; // Retain count 2
NSString *test = arr[0];
[arr removeAllObjects];
NSLog(@"%@", test); // Prints ... Good! Retain count is 1
NSString *test1 = self.myArr[0];
self.myArr = nil;
NSLog(@"%@", test1); // Crash as object gone
答案 4 :(得分:0)
问题是您是将数组值分配给某个变量然后删除了数组,但是在nslog中,您正在打印已分配数组的变量。如此defintely它将不会打印null它将打印对象
self.data = [NSArray arrayWithArray:arr];
__weak BKObject *weakMutableObject = arr[0];
[arr removeAllObjects];
NSLog(@"%@", weakMutableObject); // print
out the object, why?
像这样的便利构造函数的返回值必须是自动释放的对象*。这意味着当前的autoreleasepool已保留该对象,并且在池耗尽之前不会释放它。因此,您几乎可以保证此对象至少在您的方法持续时间内存在 - 尽管您可能不应该依赖此行为。