NSHashTable weakObjectsHashTable — добавленный объект не обнуляется

Я пытаюсь понять, как работает ARC и NSHashTable weakObjectsHashTable. Объекты, которые я добавляю в хеш-таблицу, должны быть удалены/обнулены или чем-то еще, чем они становятся после освобождения объекта. Пример кода ниже в его NSLog показывает, что объект все еще существует в хэш-таблице. Что я делаю неправильно?

#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?
    }
}

person StackUnderflow    schedule 18.01.2015    source источник
comment
string — это особый случай, см. здесь stackoverflow.com/questions/10922888/ и вместо этого попробуйте версию с NSObject.   -  person JustAMartin    schedule 24.01.2017


Ответы (3)


Присвоение нулевого объекта не влияет на результат. NSString *str = @"Hello World!"; ссылка на объект не слабая! В документах Apple говорится: если в такой хеш-таблице нет сильных ссылок на объекты, эти объекты освобождаются.

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

person anarchist    schedule 11.05.2015
comment
Но str выходит за рамки, где вызывается NSLog! Разве это не означает, что сильная ссылка (то есть str) была уничтожена прямо в } перед NSLog? - person JustAMartin; 24.01.2017

Попробуйте этот фрагмент кода для консольного приложения OS X:

//
//  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;
}

Вы должны увидеть, что хэш-карта действительно пуста, а объекты корректно уничтожаются прямо внутри цикла for, где их сильные ссылки игнорируются. Но проблема со свойством .count - оно врет, говоря, что записи есть, хотя allObjects ничего не возвращает.

person JustAMartin    schedule 25.01.2017

Как сказано в документе:

Основной вариант — предусмотреть «слабые» ссылки, которые удаляются автоматически, но в какой-то неопределенный момент в будущем.

Итак, точка автоматического удаления неизвестна, и нет гарантии, что она будет удалена в следующем цикле выполнения, так что на AutoReleasePool полагаться нельзя.

person tomisacat    schedule 14.09.2017