惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
D
Docker
云风的 BLOG
云风的 BLOG
F
Fortinet All Blogs
F
Full Disclosure
H
Hackread – Cybersecurity News, Data Breaches, AI and More
P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
L
LangChain Blog
H
Help Net Security
B
Blog
T
Tailwind CSS Blog
V
V2EX
博客园_首页
阮一峰的网络日志
阮一峰的网络日志
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
Recent Announcements
Recent Announcements
aimingoo的专栏
aimingoo的专栏
美团技术团队
A
About on SuperTechFans
C
Cybersecurity and Infrastructure Security Agency CISA
K
Kaspersky official blog
I
InfoQ
Project Zero
Project Zero
I
Intezer
Google DeepMind News
Google DeepMind News
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Threat Research - Cisco Blogs
Last Week in AI
Last Week in AI
C
Cyber Attacks, Cyber Crime and Cyber Security
G
GRAHAM CLULEY
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AWS News Blog
AWS News Blog
Spread Privacy
Spread Privacy
S
Securelist
Recorded Future
Recorded Future
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 叶小钗
S
Security Affairs
Blog — PlanetScale
Blog — PlanetScale
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
The Hacker News
The Hacker News

Objective-C

oc Swift 调用不同 macos 版本的 同一 header 的接口. - V2EX collectionview 插入数据崩溃 - V2EX 算应急, objective-c 有什么书推荐吗? - V2EX
iOS/Objective-C - NSMutableDictionary 撑爆了内存? - V2EX
xingheng · 2019-11-07 · via Objective-C

这是一个创建于 2411 天前的主题,其中的信息可能已经有所发展或是发生改变。

在一个面试被问到一个问题:在一个核心入口函数处统计每一个调用方的调用次数,入口函数知道每一个调用方的函数名(NSString *),问题是要怎么统计。

我回答构建一个 NSMutableDictionary<NSString *, NSNumber *>的 static 对象应该就可以解决问题了,用 dispatch_once 保证初始化的线程安全。但是被追问这样的话 NSMutableDictionary 的内存会撑爆,调用量会非常大,怎么优化?

我没答上来,事后也没想明白。内存增大应该是 NSString 的原因,NSNumber 毕竟只是值的变化,能占多少内存。NSMutableDictionary 确实会对 key 进行 copy,我在想什么量级的 NSString 会撑爆内存。

有想法?

w99wen

1

w99wen      2019 年 11 月 7 日

你这个有两个问题,
第一:多线程你这个方法不能保证多线程安全,加锁影响效率。
第二:内存占用吃不消。

你的错误想法:
内存释放的理解有根本错误,再看看书吧。

推荐:
切到串型队列,存储一部分数据后写盘,或者持续写盘。

现在还有问这个的。有意思。

w99wen

2

w99wen      2019 年 11 月 7 日

如果问你的人就只能反问成这样,他水平也一般啊。

ai277014717

3

ai277014717      2019 年 11 月 7 日

感觉 NSNumber 拆装箱应该也考虑进去

kera0a

4

kera0a      2019 年 11 月 7 日 via iPhone

是我没看明白吗?
一个调用方调用 1 次和 100w 次,占用的内存不是一样的嘛。
一个程序能用几个调用方啊,这种场景怎么着也和内存无关吧

wutiantong

5

wutiantong      2019 年 11 月 7 日

不懂为啥会撑爆内存,怀疑面试官的水平。

xingheng

6

xingheng      2019 年 11 月 7 日

@w99wen 确实想过串行的方法,把初始化和 dict 的操作都挪到串行队列里面去。主要问题还是内存,以我的理解,NSMutableDictionary 在 setValue:forKey:的时候确实会对 NSString \*key 进行 copy,但是这个 copy 不会产生新的内存分配(假定是 inmutable string ),只是把原有的 imutable string's retain count 加 1。上面说 NSMutableDictionary 的撑爆内存其实是指 NSMutableDictionary 持有的 NSString 把内存撑爆了。

这个理解有错误吗?请指正。

写 io 的话也想过,一旦 NSMutableDictionary 的数量级到了某个设定量就写文件,这样的话就还需要再次汇总结果了,可能不符合对方的预期。

这个问题来自蚂蚁金服的面试官。

xingheng

7

xingheng      2019 年 11 月 7 日

@kera0a 对方明确说明了调用方数量确实会有很大,我猜测还是 NSString 作为 key 的内存占用量会很大。

也不排除因为他们的 app 在正常运行的时候其他需求上已经占用大量内存了,只是针对或者设计了这样一个问题来优化这个统计结果。

xingheng

8

xingheng      2019 年 11 月 7 日

@ai277014717 NSNumber 拆装箱过程中可能会产生局部变量,内存会在每次退出函数的时候就被释放了,我觉得不至于影响 NSMutableDictionary 所持有的内存。

kera0a

9

kera0a      2019 年 11 月 7 日 via iPhone

@xingheng 我觉得出题者没考虑实际情况啊
一个方法能有 100 个调用位置就算它业务复杂了,算下来这个字典顶多 100 个 String + 100 个 number
决定 这个 Dict 大小的只有 key 有多少个,很显然 Key 不可能很多

w99wen

10

w99wen      2019 年 11 月 7 日

api 的数量会很大的,内存占用就不能小看了。
比如手淘 /支付宝之类的超大 app,底层做数据统计,整个 app 的 api 绝对是很恐怖的存在。
加上有的 api 是很长的,这个内存要求没问题的。

举个例子,你见过 6000 个会话的用户吗?
我在统计后台看到过。

w99wen

11

w99wen      2019 年 11 月 7 日

比如说,你 hook 的 objc_msgsend,统计整个 app 的 api 调用,包括系统底层的调用。那你这个 api 的数量,肯定不能在存内存了。也不能在当前线程操作。

luopengfei14

14

luopengfei14      2019 年 11 月 7 日 via iPhone

大佬好多,曾经的菜鸡 iOSer 觉得 iOS 开发已经配不上这么深入的研究了。不喜勿喷…

hoyixi

15

hoyixi      2019 年 11 月 7 日

这玩意的统计,难道不该实时传给服务器 or 缓存到本地到了一定条件同步给服务器吗,难道要常驻在内存里?

xingheng

16

xingheng      2019 年 11 月 7 日

简单写一下目前我能想的代码结构再讨论吧

```

void core_func(NSString *caller)
{
static NSMutableDictionary<NSString *, NSNumber *> *dict;
static dispatch_queue_t serialQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dict = [NSMutableDictionary new];
serialQueue = dispatch_queue_create("initializer.serial.queue", DISPATCH_QUEUE_SERIAL);
});

dispatch_async(serialQueue, ^{
if (dict[caller]) {
dict[caller] = @([dict[caller] unsignedIntegerValue] + 1);
} else {
dict[caller] = @1;
}

if (dict.count > 1000) {
NSString *url = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filename = [NSString stringWithFormat:@"stastics-data-%.3f", NSDate.date.timeIntervalSince1970];

url = [url stringByAppendingPathComponent:filename];

if ([dict writeToFile:url atomically:YES]) {
[dict removeAllObjects];
}
}
});
}

```

xingheng

17

xingheng      2019 年 11 月 7 日

@ai277014717 以我的理解,只有给对象发送了 autorelease 消息的对象才会在 autoreleasepool 闭合的时候 release,其他对象还是一直存在的。ARC 下,上面的 url, filename 可以算是 autorelease 对象。
我印象中以前有看过关于 dispatch queue 在执行的时候外围其实已经包了一个 @autoreleasepool{ },这样的话我觉得 autoreleased 对象并不能对内存构成威胁。

请指正。

xingheng

18

xingheng      2019 年 11 月 7 日

@hoyixi 那种存服务器的统计以前我还真写过,就是先写内存然后批量发到服务器,发送失败就临时写文件。但是我觉得面试官在这里应该不是问的一个设计上的问题,还是语言级的内存管理问题。

samlee123

19

samlee123      2019 年 11 月 8 日

确实会造成内存暴增,他问的就是 hook msgsend 然后做统计吧 ,调用方法需要开辟方法栈,评论里居然还有人质疑面试官水平。。。。。。🐶

xingheng

20

xingheng      2019 年 11 月 8 日

@samlee123 抱歉,是我没有描述清楚。确定不是 hook msgsend,面试官明确说了被调用方知道是谁调用了自己,参见#16 的示例代码。

ai277014717

21

ai277014717      2019 年 11 月 8 日

@xingheng 除了个别 init copy new 等方法外一般默认都是 autorelease 对象 例如 numberWithInt:
dispatch_queue 中确实出现了 autoreleasePool