3

I'm trying to understand how ARC and NSHashTable weakObjectsHashTable works. The objects I add to the hash table should be removed/zeroed or whatever they become once the object has been deallocated. The code example below in its NSLog shows that the object still exists in the hash table. What am I doing wrong?

#import <Foundation/Foundation.h>

int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        NSHashTable *hashTable = [NSHashTable weakObjectsHashTable];

        @autoreleasepool
        {
            NSString *str = @"Hello World!";
            [hashTable addObject:str];
            str = nil;
        }

        NSLog(@"hashTable:%@", [hashTable allObjects]);
        // prints: hashTable:("Hello World!") – but should be empty?
    }
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
StackUnderflow
  • 2,403
  • 2
  • 21
  • 28
  • 1
    `string` is a special case, see here http://stackoverflow.com/questions/10922888/weak-nsstring-variable-is-not-nil-after-setting-the-only-strong-reference-to-nil and try the version with NSObject instead. – JustAMartin Jan 24 '17 at 17:09

3 Answers3

1

To assign nil object it does not affect the result. NSString *str = @"Hello World!"; object reference is not weak! Apple's documents says: If there are no strong references to objects within such a hash table, those objects are deallocated.

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Collections/Articles/Sets.html#//apple_ref/doc/uid/20000136-CJBDHAJD

anarchist
  • 23
  • 4
  • But `str` is out of the scope where NSLog is called! Doesn't this mean that the strong reference (which is `str`) has been destroyed right at the `}` before `NSLog` ? – JustAMartin Jan 24 '17 at 17:05
1

Try this piece of code for OS X console application:

//
//  main.m
//  

#import <Foundation/Foundation.h>

uint objectsAlive = 0;
uint totalObjects = 0;

@interface TestObject : NSObject
@end

@implementation TestObject
{
    uint myIx;
}

- (id)init
{
    // NSAssert([NSThread currentThread] == [NSThread mainThread], @"Not on the main thread");

    self = [super init];
    if (self)
    {
        objectsAlive++;
        totalObjects++;

        myIx = totalObjects;

        NSLog(@"Object with id=%u created; alive objects %u", myIx, objectsAlive);
    }

    return self;
}

- (void)dealloc
{
    objectsAlive--;
    NSLog(@"Object  with id=%u is being destroyed by ARC; alive objects will become %u", myIx,objectsAlive);
}

@end

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

    // default global autorelease pool
    @autoreleasepool {

        NSHashTable * testHashMap = [NSHashTable weakObjectsHashTable];
        // weakObjectsHashTable - according to Apple docs, entries are not necessarily purged right away when the weak object is reclaimed, and we can observe this behavior here - some entries stay alive until last autorelease

        // comment out the line above and uncomment the line below to observe different behavior with strong references in NSHashTable
        // NSHashTable * testHashMap = [[NSHashTable alloc] init];

        // nested autoreleasepool to ensure that the objects added to the testHashMap are released by ARC
        @autoreleasepool {

            for(int i = 0; i < 10;i++) {
                TestObject * obj = [[TestObject alloc] init];
                [testHashMap addObject: obj];
                NSLog(@"Count in hash table inside additions scope is %lu",
                      (unsigned long)testHashMap.count);
            }

            NSLog(@"Count in hash table inside @autoreleasepool is %lu",
                  (unsigned long)testHashMap.count);
            NSLog(@"Objects in hash table inside of @autoreleasepool are %@",
                  testHashMap.allObjects);
            NSLog(@"Exiting inner autorelease pool...");

        }

        // objects in NSHashTable also were released, according to dealloc logs in TestObject, but count is still lagging behind (shows 2)
        NSLog(@"Count in hash table outside of @autoreleasepool is %lu",
              (unsigned long)testHashMap.count);

        // this should indeed show that NSHashTable with weakObjectsHashTable is empty, despite count=2
        NSLog(@"Objects in hash table outside of @autoreleasepool are %@",
              testHashMap.allObjects);

        NSLog(@"Exiting outer autorelease pool, ecpect all objects in strong ref NSHashTable to die...");
    }

    return 0;
}

You should see that the hashmap indeed is empty and objects get correctly destroyed right inside the for loop where their strong references are abandoned. But the problem is with .count property - it lies, telling that there are entries although allObjects return none.

JustAMartin
  • 13,165
  • 18
  • 99
  • 183
0

As the document said:

The major option is to provide for "weak" references that are removed automatically, but at some indefinite point in the future.

So, the point of automatic removal is unknown, and it's not guaranteed that it will be removed in the next run loop, so you can't rely on AutoReleasePool.

tomisacat
  • 506
  • 4
  • 12