苹果文档里说:
The NSCache class incorporates various auto-eviction policies, which ensure that a cache
doesn’t use too much of the system’s memory. If memory is needed by other applications,
these policies remove some items from the cache, minimizing its memory footprint.
NSCache本来很简单的类,就这里内存吃紧时的淘汰策略有些疑问。网上有很多文章说内存不够时,自动淘汰,貌似会收到系统内存warning通知,然后自动怎么怎么。。。感觉很智能。。。
但是分析后发现:
1 淘汰策略只是NSCache自身逻辑,和内存warning之类无关;
2 淘汰策略依赖totalCostLimit和countLimit成员变量的设置,默认没有开启;
https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSCache.swift
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation库逆向后,NSCache类只是很薄的封装,里面都是调用的C实现,也没有处理内存warning什么的。
void * -[NSCache init](void * self, void * _cmd) {
rbx = self;
var_60 = 0x2;
intrinsic_movdqu(var_40, intrinsic_punpcklqdq(zero_extend_64(___NSCacheKeyRelease), zero_extend_64(___NSCacheValueRelease)));
var_10 = 0x0;
if (cache_create("", &var_60, &var_10) != 0x0) {
[rbx release];
rbx = 0x0;
}
else {
*(rbx + *ivar_offset(_private) + 0x8) = var_10;
rbx->_private = 0x0;
*(rbx + *ivar_offset(_private) + 0x10) = 0x1;
}
rax = rbx;
return rax;
}
void -[NSCache setObject:forKey:cost:](void * self, void * _cmd, void * arg2, void * arg3, unsigned long long arg4) {
r12 = arg4;
r14 = arg3;
r15 = arg2;
rbx = _cmd;
r13 = self;
___NSCheckReentrancy(self, _cmd);
if (r14 != 0x0) {
if (r15 != 0x0) {
rbx = *ivar_offset(_private);
COND = (*(int8_t *)(r13 + rbx + 0x10) & 0x1) != 0x0;
rax = *(r13 + rbx + 0x20);
if (!COND) {
if (rax == 0xffffffffffffffff) {
*(r13 + rbx + 0x20) = 0x2;
}
}
else {
if (rax != 0xffffffffffffffff) {
if (rax != 0x1) {
if (rax == 0x0) {
rax = [r15 conformsToProtocol:@protocol(NSDiscardableContent)];
CMP(rax, 0x1);
*(r13 + rbx + 0x20) = !(rax - rax + CARRY(RFLAGS(cf))) | 0x1;
}
}
else {
if ([r15 conformsToProtocol:@protocol(NSDiscardableContent)] != 0x0) {
*(r13 + rbx + 0x20) = 0x2;
}
}
}
else {
if ([r15 conformsToProtocol:@protocol(NSDiscardableContent)] == 0x0) {
*(r13 + rbx + 0x20) = 0x2;
}
}
}
if (cache_set_and_retain(*(r13 + rbx + 0x8), r14, r15, r12) == 0x0) {
cache_release_value(*(r13 + rbx + 0x8), r15);
}
}
else {
___CFExceptionProem(r13, rbx);
_CFStringCreateWithFormat(*_kCFAllocatorSystemDefault, 0x0, @"%@: attempt to insert nil value (key: %@)");
objc_exception_throw([NSException exceptionWithName:*_NSInvalidArgumentException reason:__CFAutoreleasePoolAddObject() userInfo:0x0]);
}
}
return;
}
void -[NSCache removeObjectForKey:](void * self, void * _cmd, void * arg2) {
r14 = arg2;
rbx = self;
___NSCheckReentrancy(self, _cmd);
if (r14 != 0x0) {
cache_remove(*(rbx + *ivar_offset(_private) + 0x8), r14);
}
return;
}
void -[NSCache setCountLimit:](void * self, void * _cmd, unsigned long long arg2) {
___NSCheckReentrancy(self, _cmd);
cache_set_count_hint(*(self + *ivar_offset(_private) + 0x8), arg2);
return;
}
void -[NSCache setTotalCostLimit:](void * self, void * _cmd, unsigned long long arg2) {
___NSCheckReentrancy(self, _cmd);
cache_set_cost_hint(*(self + *ivar_offset(_private) + 0x8), arg2);
return;
}