NSCache記憶體淘汰政策分析
-
-
- 1 NSCache功能
- 2 分析前
- 3 分析後
- 4 參考
-
- (1) NSCache的swift版源碼
- (2) NSCache的OC版逆向
-
1 NSCache功能
- 儲存key-value對,可增删查;
- 記憶體吃緊時,可自動移除部分key-value對;
2 分析前
蘋果文檔裡說:
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通知,然後自動怎麼怎麼。。。感覺很智能。。。
3 分析後
但是分析後發現:
1 淘汰政策隻是NSCache自身邏輯,和記憶體warning之類無關;
2 淘汰政策依賴totalCostLimit和countLimit成員變量的設定,預設沒有開啟;
4 參考
(1) NSCache的swift版源碼
https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSCache.swift
(2) NSCache的OC版逆向
/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;
}