iOS:在操作大型 NSString 实例时如何避免自动释放副本?

发布于 2024-12-04 12:04:44 字数 789 浏览 0 评论 0原文

我在 iOS 应用程序中有一个场景,其中操作一个非常大的 NSString 实例(HTTP 响应,大于 11MB)会导致内存中同时存在多个大型中介,因为我调用的 SDK 方法返回新的自动释放实例。这里采取的最佳方法是什么?

例如,假设 largeString 是一个自动释放的 NSString 实例:

NSArray *partsOfLargeString = [largeString componentsSeparatedByString:separator];

for (NSString *part in partsOfLargeString) {
    NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

    NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
}

如果有与 componentsSeparatedByString等价的非自动释放的实例,那就太好了stringByTrimmingCharactersInSet,但我不打算自己实现这些。

据我所知,没有办法“强制”释放已添加到自动释放池中的对象。我知道我可以在这里创建和使用自己的自动释放池,但我希望非常细化,并且围绕各个语句设置自动释放池绝对不是一种可扩展的方法。

非常感谢任何建议。

I have a scenario in an iOS application where manipulating a very large NSString instance (an HTTP response, upwards of 11MB) results in multiple large intermediaries being in memory at once, since the SDK methods I am calling return new autoreleased instances. What is the best approach to take here?

For example, assuming that largeString is an autoreleased NSString instance:

NSArray *partsOfLargeString = [largeString componentsSeparatedByString:separator];

for (NSString *part in partsOfLargeString) {
    NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

    NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
}

It would be great if there were non-autoreleased equivalents to componentsSeparatedByString or stringByTrimmingCharactersInSet, but I'm not looking to implement these myself.

To my knowledge, there isn't a way to "force" release an object that has already been added to an autorelease pool. I know that I can create and use my own autorelease pool here, but I'd like to be extremely granular and having autorelease pools around individual statements definitely isn't a very scalable approach.

Any suggestions are much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

墨落画卷 2024-12-11 12:04:44

正如比尔所说,我首先尝试为每个循环迭代建立一个自动释放池,例如:

for (NSString *part in partsOfLargeString) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
    …

    [pool drain];
}

或者,如果您使用的是足够新的编译器:

for (NSString *part in partsOfLargeString) {
    @autoreleasepool {
        NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
        …
    }
}

如果这仍然不可接受,并且您确实需要以更细粒度的方式释放对象,您可以使用类似的东西:

static inline __attribute__((ns_returns_retained))
id BICreateDrainedPoolObject(id (^expression)(void)) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];
    id object = expression();
    [object retain];
    [pool drain];
    return object;
}

#define BIOBJ(expression) BICreateDrainedPoolObject(^{return (expression);})

它计算表达式,保留其结果,释放任何辅助自动释放对象并返回结果;然后:

for (NSString *part in partsOfLargeString) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSString *trimmedPart = BIOBJ([part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]);
    NSData *data = BIOBJ([trimmedPart dataUsingEncoding:NSUTF8StringEncoding]);
    [trimmedPart release];

    // do something with data
    [data release];

    …

    [pool drain];
}

请注意,由于该函数返回一个保留的对象,因此您有责任释放它。您将可以控制何时执行此操作。

请随意为函数和宏选择更好的名称。可能有一些应该处理的极端情况,但它应该适用于您的特定示例。欢迎提出建议!

As Bill said, I’d first try to have an autorelease pool for each loop iteration, e.g.:

for (NSString *part in partsOfLargeString) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
    …

    [pool drain];
}

or, if you’re using a recent enough compiler:

for (NSString *part in partsOfLargeString) {
    @autoreleasepool {
        NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        NSData *data = [trimmedPart dataUsingEncoding:NSUTF8StringEncoding];
        …
    }
}

If that’s still not acceptable and you do need to release objects in a more granular fashion, you could use something like:

static inline __attribute__((ns_returns_retained))
id BICreateDrainedPoolObject(id (^expression)(void)) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];
    id object = expression();
    [object retain];
    [pool drain];
    return object;
}

#define BIOBJ(expression) BICreateDrainedPoolObject(^{return (expression);})

which evaluates the expression, retains its result, releases any ancillary autoreleased objects and returns the result; and then:

for (NSString *part in partsOfLargeString) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSString *trimmedPart = BIOBJ([part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]);
    NSData *data = BIOBJ([trimmedPart dataUsingEncoding:NSUTF8StringEncoding]);
    [trimmedPart release];

    // do something with data
    [data release];

    …

    [pool drain];
}

Note that, since the function returns a retained object, you’re responsible for releasing it. You’ll have control over when to do that.

Feel free to choose better names for the function and macro. There might be some corner cases that should be handled but it should work for your particular example. Suggestions are welcome!

时间你老了 2024-12-11 12:04:44

首先,您不需要以这种方式解析来自 HTTP 服务器的响应。解析 HTTP 响应(包括解析 HTML)是一个已解决的问题,尝试使用原始字符串操作来解析它会导致脆弱的代码,这些代码很容易因看似无害的服务器端更改而崩溃。

自动释放池非常便宜。您可以使用 @autoreleasepool {... that code ...} 包围 for 的主体[内部],它可能既可以解决您的高水位问题,又可以忽略不计的性能影响[相比原始字符串操作]。

除此之外,你的总结是正确的——如果套件中没有非自动释放变体,那么你就必须重新发明轮子。话虽如此,缺乏非自动释放变体并不是设计师的疏忽,这是相当典型的。相反,这可能是因为有更好的工具可用于实现那种还需要更细粒度的内存管理的大容量解决方案。

First, you shouldn't need to parse responses from an HTTP server in this fashion. Parsing HTTP responses (including parsing HTML) is a solved problem and attempting to parse it using raw string manipulation will lead to fragile code that can easily be crashed with seemingly innocuous server side changes.

Autorelease pools are pretty cheap. You could surround the body [inside] of the for with @autoreleasepool {... that code ...} and it'll probably both fix your high-water issue and have negligible performance impact [compared to raw string manipulation].

Beyond that, your summary is correct -- if there isn't a non-autoreleasing variant in the 'kit, then you'd have to re-invent the wheel. With that said, it is fairly typical that the lack of a non-autoreleasing variant is not an oversight on the part of the designer. Instead, it is likely because there are better tools available for achieving the sort of high-volume solution that would also require finer grained memory management.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文