NSCache内存淘汰策略分析

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

猜你喜欢

转载自blog.csdn.net/demondev/article/details/85015754