Я пытаюсь узнать больше о ручном подсчете ссылок. Я создал класс с переменной экземпляра и ее геттером/сеттером, который использует ручной выпуск:
@interface Foo: NSObject {
id bar;
}
- (id)bar;
- (void)setBar:(id)value;
@end
@implementation Foo
- (id)bar {
return bar;
}
- (void)setBar:(NSNumber*)value {
[bar release];
bar = value;
}
@end
Затем я использую его следующим образом:
int main() {
Foo *f = [[Foo alloc] init];
NSNumber *n = [[NSNumber alloc] initWithInteger: 3];
[f setBar: n];
[n release];
NSLog(@"%@\n", [[f bar] description]);
return 0;
}
Я скомпилировал его с помощью Address Sanitizer и ожидаю, что он будет ловить, поскольку [[f bar] description]
должен вызывать метод для освобожденного объекта:
clang -fno-objc-arc -fsanitize=address -g -framework Foundation main.m
ASAN_OPTIONS=detect_leaks=1
Но к моему удивлению работает нормально!
2018-04-08 18:18:38.470300-0400 a.out[3457:291626] 3
Затем я попробовал это с NSDate
вместо этого, чтобы увидеть, имеет ли это значение:
NSDate *n = [[NSDate alloc] init];
[c setBar: n];
[n release];
И это делает:
ASAN:DEADLYSIGNAL
=================================================================
==3379==ERROR: AddressSanitizer: SEGV on unknown address 0x7fff0b800018 (pc 0x7fff577f3e9d bp 0x7ffee4491670 sp 0x7ffee44915f8 T0)
==3379==The signal is caused by a READ memory access.
#0 0x7fff577f3e9c in objc_msgSend (libobjc.A.dylib:x86_64h+0x6e9c)
#1 0x7fff5841b014 in start (libdyld.dylib:x86_64+0x1014)
...
Я просмотрел метод dealloc
NSNumber
и обнаружил, что он не вызывается:
@interface Dealloc: NSObject
@end
@implementation Dealloc
- (void)dealloc {
NSLog(@"dealloc");
}
@end
...
method_exchangeImplementations(class_getInstanceMethod([Dealloc class], @selector(dealloc)),
class_getInstanceMethod([NSNumber class], @selector(dealloc)));
2018-04-08 18:18:38.470300-0400 a.out[3457:291626] 3
По сравнению с NSDate
:
2018-04-08 18:21:03.899490-0400 a.out[3483:293512] dealloc
2018-04-08 18:21:03.900538-0400 a.out[3483:293512] dealloc
2018-04-08 18:21:03.900598-0400 a.out[3483:293512] dealloc
Затем я попробовал это с кучей других классов:
NSString
не освобождаетсяNSMeasurement
выделяетсяNSArray
не освобождаетсяNSDictionary
не освобождаетсяNSData
не освобождаетсяNSURL
освобождается
Что тут происходит? Почему только некоторые из них подхватываются Address Sanitizer
Обновлять:
Как указали Роб и rmaddy, с небольшими целочисленными значениями для NSNumber
он хранится как помеченный указатель, который фактически не выделяет никакой дополнительной памяти:
(lldb) p/t n (__NSCFNumber *) $1 = 0b0000000000000000000000000000000000000000000000000010101000100111 (int)42
Последний бит установлен в 1, указывая на то, что он помечен, и все, кроме первых 4 битов, является целым числом 42.
Для постоянной строки (__NSCFConstantString
) с [[NSString alloc] initWithString:@"blah"]
это не похоже на помеченный указатель, поскольку последний бит не установлен, но это все же странный адрес:
(lldb) p n
(__NSCFConstantString *) $1 = 0x0000000100002068 @"blah"
(lldb) p/t n
(__NSCFConstantString *) $2 = 0b0000000000000000000000000000000100000000000000000010000001101000 @"blah"
NSDate
и NSMeasurement
все отображаются в «нормальном» диапазоне адресов> 0x600000000000.
Пустой NSArray
имеет адрес в этом диапазоне, но не освобождает его, но заполняет его объектами:
[[NSArray alloc] initWithObjects:@2, @3, @5, nil]
NSNumber
с большим произвольным значением, например65465.23424
. Маленькие целые числа специально оптимизированы. То же самое со статическими значениямиNSString
. - person rmaddy   schedule 09.04.2018[[NSNumber alloc] initWithFloat:65465.23424]
освобождается. Существуют ли другие специальные оптимизации, такие как какой-то пул дляNSString
и других типов коллекций? - person Luke   schedule 09.04.2018@"..."
является статической строкой, которая не будет освобождена. Не уверен насчет типов коллекций. - person rmaddy   schedule 09.04.2018release
. Вы можете только предположить, что они будут управлять памятью так, как если бы они это делали (т. е. они не будут утекать, но никогда не обещают, что вы выйдете из строя). Небольшие целые числа, пустые коллекции и статические строки имеют особый регистр. Я думаю, что на 64-битных платформах почти все целые числа являются помеченными указателями. Я считаю, что короткие строки также могут стать помеченными указателями. Просто нет никаких обещаний, чтоrelease
илиdealloc
действительно будут вызываться, только то, что это сработает. - person Rob Napier   schedule 09.04.2018[[NSNumber alloc] initWithInt: 42]
я получаю(lldb) p/t n (__NSCFNumber *) $1 = 0b0000000000000000000000000000000000000000000000000010101000100111 (int)42
Теперь я буду изучать другие типы. - person Luke   schedule 09.04.2018