首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用自动释放池泄漏测试NSString?

如何用自动释放池泄漏测试NSString?
EN

Stack Overflow用户
提问于 2021-10-22 13:08:34
回答 1查看 131关注 0票数 4

试图修复300MB内存泄漏,并在发现泄漏原因后;

(它是从C++线程调用NSStringstringFromUTF8String: (没有@autoreleasepool-block包装))

我编辑了代码,以执行引用计数(而不是自动发布),如下所示:

代码语言:javascript
复制
public func withNSString(
    _ chars: UnsafePointer<Int8>,
    _ callback: (NSString) -> Void
) {
    let result: NSString = NSString(utf8String: chars)!;
    callback(result);
}

作为个人政策,带有单元测试,例如:

代码语言:javascript
复制
import Foundation
import XCTest
@testable import MyApp

class AppTest: XCTestCase {
    func testWithNSString_hasNoMemoryLeak() {
        weak var weakRef: NSString? = nil
        autoreleasepool {
            let chars = ("some data" as NSString).utf8String!;
            withNSString(chars, { strongRef in
                weakRef = strongRef;
                XCTAssertNotNil(weakRef);
            })
            // Checks if reference-counting is used.
            XCTAssertNil(weakRef); // Fails, so no reference-counting. 
        }
        // Checks if autoreleased.
        XCTAssertNil(weakRef); // Fails, OMG! what is this?
    }
}

但是现在,即使是自动发布似乎也不再起作用了,(-_- )

为什么最后一次XCTAssertNil 调用失败?

(换句话说,我如何修复内存泄漏?)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-22 13:37:47

问题是你用的是一个很短的字符串。它被内联到堆栈上,所以直到整个堆栈帧超出范围后才会释放。如果您使字符串稍微长一点(2个字符长),这将按照您预期的方式进行。当然,这是一个实现细节,并且可能由于编译器的不同版本、操作系统的不同版本、不同的优化设置或不同的体系结构而发生变化。

请记住,使用任何类型的静态字符串来测试这类事情是很棘手的,因为静态字符串被放置到二进制文件中。因此,如果编译器注意到您间接地创建了一个指向静态字符串的指针,那么它可能会优化间接方向,而不是释放它。

但是,在所有这些情况下,都不存在内存泄漏。您的内存泄漏更有可能出现在withNSString的调用代码中。我主要怀疑您没有正确地处理作为chars传递的字节。我们需要更多地了解为什么你认为有一个漏洞来评估它。(Foundation也有一些小泄漏,而仪器在泄漏方面有假阳性,所以如果您在追逐小于50个字节的分配,并且没有在每个操作中重复,那么您可能是在追逐鬼魂。)

请注意,这有点危险:

代码语言:javascript
复制
let chars = ("some data" as NSString).utf8String!
withNSString(chars, { strongRef in

utf8String内部指针不会比NSString更长,而且Swift可以在对象上次引用之后(可能是在它们超出作用域之前)自由地销毁它们。正如医生们所指出的:

此C字符串是指向string对象内部结构的指针,该结构的生存期可能比string对象短,并且肯定不会有更长的生存期。因此,如果C字符串需要存储在使用此属性的内存上下文之外,则应复制它。

在这种情况下,对象是一个常量字符串,它在二进制文件中,不能被销毁。但在更普遍的情况下,这是典型的撞车原因。我强烈建议远离NSString接口,使用字符串。它提供了utf8CString,它返回一个正确的ContinguousArray,这要安全得多。

代码语言:javascript
复制
let chars = "some data".utf8CString
chars.withUnsafeBufferPointer { buffer in
    withNSString(buffer.baseAddress!, { strongRef in
        weakRef = strongRef;
        XCTAssertNotNil(weakRef);
    })
}

withUnsafeBufferPointer确保在块完成之前不能销毁chars

如果需要,还可以确保字符串的生存期(这对于修复您不希望以更安全的方式重写的旧代码非常有用):

代码语言:javascript
复制
let string = "some data"

withExtendedLifetime(string) {
    let chars = string.utf8CString
    chars.withUnsafeBufferPointer { buffer in
        withNSString(buffer.baseAddress!, { strongRef in
            weakRef = strongRef;
            XCTAssertNotNil(weakRef);
        })
    }
}
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69677467

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档