BKObject is a custom object and I want to put mutiple BKObject in to an array.
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
I'm curious about why the first 2 NSLog messages show an object instead of null(as in the last NSLog). I'm using the latest Xcode 5.0.1 with iOS 7 SDK.
NSMutableArray *arr = [[NSMutableArray alloc] init];
for(NSInteger i = 0; i < 100000; i++){
    [arr addObject:[[BKObject alloc] initWithName:@""]];
}
OK, so at this point, we have a bunch of objects retained by an array.
self.data = [NSArray arrayWithArray:arr];
And now at this point, we have a bunch of objects retained by two different arrays.
__weak BKObject *weakMutableObject = arr[0];
[arr removeAllObjects];
NSLog(@"%@", weakMutableObject); // print out the object, why?
Because the object pointed to by arr[0] is also retained by self.data.
__weak BKObject *weakObject = self.data[0];
self.data = nil;
NSLog(@"%@", weakObject); // print out the object again, but why?
This one is a bit interesting. The "problem" is that arrayWithArray: is adding an extra retain/autorelease, which it's free to do since they're balanced. You can demonstrate that pretty simply by draining the autorelease pool at different points.
This shows a live object:
  __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
This shows 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
The lesson here is that you should not assume that an object will deallocate at any given point within an autorelease block. That is not a promise ARC gives. It only promises a minimum amount of time that the object will be valid. Other parts of the system are free to attach balanced retain/autorelease pairs as much as they like, which will delay deallocation until the pool drains.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With