【问题标题】:Class method: is that the right way to avoid memory leakage?类方法:这是避免内存泄漏的正确方法吗?
【发布时间】:2011-01-13 20:27:33
【问题描述】:

首先,我是这个网站的新手,在此先感谢您的帮助。

我的 iPhone 应用程序有一个类,它的主要作用是封装我处理来自 Web 服务器的一堆原始字节上的数据的方式。每次我需要显示来自该数据的定义类型的信息(例如,包含在这些原始字节中的一条建议)时,我都会调用 getAdviceFromGame 方法。该方法通过在最后调用 NSString 类方法构建一个可显示的 NSString(由 stringWithUTF8String 类方法返回的对象是自动释放的,根据方法命名规则 - 没有 init,名称中没有 alloc)。请注意,我没有在方法名称中添加“New”,因为调用者不拥有该方法返回的对象。

方法如下:

-(NSString*) getAdviceFromGame:(NSInteger)number
                    ofLine:(NSInteger)line {
// Returns the advice at line specified as argument.
NSInteger currentOffset;
NSRange currentRange;
NSInteger offsetAdvice;
NSInteger length;
char currentCString[100];

if (line == 1)
    offsetAdvice = OFF_ADVICE1;
else
    offsetAdvice = OFF_ADVICE2;

// Length is the same whateve is the line.
length = LEN_ADVICE1;

// Point to the begnning of the requested game.
currentOffset = OFF_G1_SET + (number - 1) * LEN_SET;
// Point to the selected advice.
currentOffset = currentOffset + offsetAdvice;
// Skip TL
currentOffset = currentOffset + 2;

currentRange.location = currentOffset;
currentRange.length = length;

NSLog(@"Avant getBytes");

// Get raw bytes from pGame.
// Contains a C termination byte.
[pGame getBytes:currentCString range:currentRange];

// Turn these raw bytes into an NSString.
// We return an autoreleased string.
return [NSString stringWithUTF8String:currentCString];

}

如果从我的角度来看,这个方法并不重要,从内存管理的角度来看,因为我只发回一个“自动释放”对象。注意:pGame 是一个 NSData 类型的内部类变量。

在我的应用程序中,为了了解自动释放对象的行为方式,我在 - (void)applicationDidFinishLaunching:(UIApplication *) 应用程序函数中循环了 10000 次此方法,并在 - (void)tabBarController 中循环了 10000 次相同的方法:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController。这样,我可以调用大分配。

在代码执行期间,当应用程序启动时,我可以使用 alloc 测量工具看到分配的对象大小正在增长(从 400K 到 800K)。然后,当 applicationDidFinishLaunching 方法结束时,数量会下降到 400K。因此,我猜想该池已被操作系统“耗尽”(一种垃圾管理)。

当我单击标签栏时,由于循环再次出现大分配。此外,我可以看到大小在增长(因为数以千计的 NSString 被分配并发回)。完成后,大小将降至 400K。

因此,我的第一个问题是: Q1:我们能否准确地知道自动释放池何时会“耗尽”或“清除”? Q2:这是否发生在诸如 didSelectViewController 之类的 OS/GUI 方法的末尾? Q3:调用getAdviceFromGame的人在使用之前必须保留我的方法返回的对象吗?

现在我有另一个(更复杂的)方法,我在内部分配一个可变字符串,然后再发回 NSString:

-(NSString*) getBiddingArrayFromGame:(NSInteger)number
                           ofRow:(NSInteger)row
                          ofLine:(NSInteger)line {
NSInteger offset;
char readByte;
NSMutableString *cardSymbol = [[NSMutableString alloc] initWithString:@""];
NSRange range;

// Point to the begnning of the requested game.
offset = OFF_G1_SET + (number - 1) * LEN_SET;

// Returns the array value from cell (row, line)
// We must compute the offset of the line.
// We suppose that the offset cannot be computed, but
// only deduced from line number through a table.
switch (line) {
    case 1:
        offset = offset + OFF_LINE1;
        break;
    case 2:
        offset = offset + OFF_LINE2;
        break;
    case 3:
        offset = offset + OFF_LINE3;
        break;
    case 4:
        offset = offset + OFF_LINE4;
        break;
    default:
        // This case should not happen but for robustness
        // we associate any extra value with a valid offset.
        offset = OFF_LINE4;
        break;
}

// Skip TL bytes
offset = offset + 2;

// From the offset and from the row value, we can deduce
// the offset in the selected line.
offset = offset + (row - 1);

// Now, we must read the byte and build a string from
// the byte value.
range.location = offset;
range.length = 1;
[pGame getBytes:&readByte range:range];

// We must extract the family type.
// If the family if of type "Special" then we must build by
// hand the value to display. Else, we must build a string
// with the colour symbol and associated character by reading
// in the card character table.
switch (readByte & CARD_FAMILY_MASK) {
    case COLOUR_CLUBS:
        // "Trèfles" in French.
        [cardSymbol appendString:CLUBS_UTF16];
        break;
    case COLOUR_DIAMONDS:
        [cardSymbol appendString:DIAMONDS_UTF16];
        break;
    case COLOUR_HEARTS:
        [cardSymbol appendString:HEARTS_UTF16];
        break;
    case COLOUR_SPADES:
        [cardSymbol appendString:SPADES_UTF16];
        break;
    case COLOUR_SPECIAL:
        break;
    case COLOUR_ASSET:
    default:
        break;
}

[cardSymbol autorelease];

// Return the string.
return [NSString stringWithString:cardSymbol];

}

如您所见,这并不是很复杂,但从内存管理的角度来看更为关键,因为我“内部”分配并初始化了一个 NSString。由于我在方法结束时使用它,我只能在调用 stringWithString:cardSymbol 之前自动释放它(实际上我想释放它以便立即释放它)否则它可以在 stringWithString:cardSymbol 方法之前释放它。好吧,我对这样做的方式不满意,但也许这是正确的方式。

因此我的最后一个问题是:这是正确的做法吗?

恐怕在到达 stringWithString:cardSymbol 之前自动释放池被清除。

最好的问候, 弗朗茨

【问题讨论】:

    标签: objective-c


    【解决方案1】:

    首先,您在该代码中的任何地方都没有类方法,但您的问题标题表明您确实有一个类方法。因此,我建议再次阅读Objective-C guide(说真的——在我编写Objective-C 的前5 年里,我大约每六个月读一次那篇文章,并且每次都学到一些东西)。

    因此,我的第一个问题是: Q1:可以 我们准确地知道什么时候自动释放 池将被“排干”或“清除”? Q2:这是否发生在结束时 OS/GUI 方法,例如 选择视图控制器? Q3:必须 打电话给 getAdviceFromGame 的人 保留我发回的对象 使用前的方法?

    自动释放池没有魔法。 documentation is quite clear 了解它们的工作原理。

    对于基于 UIKit 的应用程序,有一个由运行循环管理的自动释放池。每次通过 runloop 都会导致释放池被耗尽。如果您在一次运行循环中分配大量临时对象,那么您可能需要创建和排出您自己的自动释放池(您的代码没有表明这是您需要做的)。

    如您所见,这不是很 从一个复杂但更关键的 从内存管理的角度来看 我“内部”分配并初始化一个 NSString。因为我在最后使用它 该方法,我只能自动释放它 打电话之前 stringWithString:cardSymbol (其实我 想释放它,以便它是 立即解除分配)否则它可以 之前被释放 stringWithString:cardSymbol 方法。 好吧,我对这种方式不满意 这样做,但也许这是正确的 方法。

    除非您明确地创建和排空自动释放池,否则您的字符串不会从您身下消失。水池的排水不是自动的。

    没有理由创建可变字符串的不可变副本。只需返回可变字符串。如果你真的真的很想创建一个不可变的副本,那就按照 Yuji 的建议去做吧。

    【讨论】:

    • @bbum:感谢您的解释和建议。抱歉,我的问题标题不准确。事实上,标题只是问题的一部分。确实,我的方法使用了类方法(最后,返回时): return [NSString stringWithUTF8String:currentCString];我专注于“类方法”,因为这个方法是自动释放它发回的对象的类方法。我会尽量在我的下一个问题标题中更准确。
    • 别担心——我现在明白你为什么如此强调了。
    【解决方案2】:

    Q1:我们能否准确知道自动释放池何时会“耗尽”或“清除”?

    Q2:这是否发生在诸如 didSelectViewController 之类的 OS/GUI 方法的末尾?

    是的,每个事件循环都会耗尽自动释放池一次。

    Q3:调用getAdviceFromGame的人在使用之前必须保留我的方法发回的对象吗?

    如果有人想在特定事件周期之后保留该对象,可以。如果没有,您不必这样做,因为在您当前的事件处理方法返回之前,autoreleased 对象保证是活动的。

    请注意,我没有在我的方法名称中添加“New”,因为调用者不拥有该方法返回的对象。

    非常好!但我也建议您将方法名称从getAdviceFromGame:ofLine 更改为adviceFromGame:ofLine。通常在 Cocoa 中,get... 用于作为方法参数传递的指针返回某些内容时,就像您使用 [pGame getBytes:&readByte range:range]; 时一样。

    至于第二部分,你可以使用而不是你的行

    [cardSymbol autorelease];
    
    // Return the string.
    return [NSString stringWithString:cardSymbol];
    

    顺序

    NSString*s=[SString stringWithString:cardSymbol];
    [cardSymbol release];
    return s;
    

    甚至只是

    return [cardSymbol autorelease];
    

    因为NSMutableStringNSString 的子类。但是自动释放池中只有一个对象并不重要,即使在 iPhone 上也是如此。

    【讨论】:

    • @Yuji:感谢您对方法命名的建议(“get”或不“get”,我不知道有什么区别)。发布问题后,我得到了相同的想法,以便在方法内部释放 cardSymbol,方法是使用备用 NSString 指针,其指向的数据可以在“返回”之前释放。 NSString*s=[SString stringWithString:cardSymbol]; [卡片符号发布];返回 s;返回卡符号; :确实,我不知道我可以返回要返回的类型的子类。
    • 感谢您的回复。我注意到我在最后一个示例中忘记了autorelease,现在我更正了。愚蠢的我。
    猜你喜欢
    • 2017-05-16
    • 2011-02-28
    • 2010-10-08
    • 2016-03-19
    • 1970-01-01
    • 2013-01-23
    • 2015-05-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多