【问题标题】:How to force release on iOS如何在 iOS 上强制释放
【发布时间】:2013-09-04 06:40:03
【问题描述】:

我是 ARC 的新手,但了解它的工作原理,我正在尝试。我在 iOS 上,所以内存是一个严重的问题。

我有一个包含大量大数据的 MyObject 类。我想发布它,并加载一组新数据。

MyObject *object;
object = [[MyObject alloc] initWithData:folder1]; // load data from folder1

// later...
object = [[MyObject alloc] initWithData:folder2]; // load data from folder2

这工作正常,没有泄漏,我猜 ARC 在新分配之前插入了一个 [object release]。我的问题是“对象”内的数据在分配新集之后被释放,而我的内存不足。我真正想做的是:

object = nil;

<function to pop the pool, wait till everything is deallocated>

object = [MyObject alloc] initWithData:folder2]; // load data from folder2

但我不知道该怎么做。我可以在延迟后在 performselector 上运行新分配,但感觉就像我在黑暗中拍摄并且有点黑客。可能有一种正确的方法可以做到这一点?

PS 我已经尝试寻找答案,但所有结果都是关于内存泄漏以及如何确保变量超出范围并将变量设置为 nil 等。我的问题与此无关,更多的是时间问题事物。

更新
谢谢解答,已经试过了

object = nil;
object = [MyObject alloc] initWithData:folder2];

它没有奏效。我不确定它是否应该这样做。现在我明白它应该工作的,但我必须有其他东西在这几分之一秒内保持它。我在我的所有 init/dealloc 方法中都有 NSLogs,我可以首先看到被调用的类的新实例(MyObject 的 ivars)的所有初始化,然后几乎立即(在几毫秒内)MyObject 的 dealloc ,然后是其 ivars 的 deallocs。 我也尝试了@autorelease,但同样的事情发生了。

我搜索了整个项目并粘贴了我认为可能与此相关的所有代码。

@interface AppDelegate : UIResponder <UIApplicationDelegate>;
    @property PBSoundSession *soundSession;
@end


//--------------------------------------------------------------
@implementation AppDelegate

// onTimer fired at 60Hz
-(void)onTimer:(NSTimer *) theTimer {
   [oscReceiver readIncoming]; // check incoming OSC messages
   // then do a bunch of stuff with _soundSession;
}
@end


//--------------------------------------------------------------
@implementation OscReceiver

-(void)readIncoming {
   AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];

   // parse all incoming messages

   if(bLoadNewSoundBank) {
      NSString *newFolder = parseNewFolder();
      appDelegate.soundSession = nil;
      appDelegate.soundSession = [MyObject alloc] initWithData:newFolder];
   }
}

@end


//--------------------------------------------------------------
@implementation GuiController

// onTimer fired at 10Hz
-(void)onTimer:(NSTimer *) theTimer {
   PBSoundSession *soundSession = appDelegate.soundSession;
   // update gui with received values
}

@end

我认为这可能是 GuiController::onTimer 中的“soundSession”局部变量在该方法的持续时间内持有旧的 appDelegate.soundSession,但令我惊讶的是注释掉了所有 GUI 代码(实际上禁用了计时器),没有区别。

有没有办法在那个时候找出谁仍然持有我的 appDelegate.soundSession?我放置了一个断点,将其设置为 nil,但找不到任何有用的信息。我在分配模板中尝试了仪器,但在那里也找不到任何有用的东西(可能是因为我不知道在哪里看)。

这就是我的分配轨迹的样子,你可以看到内存全部释放有点太晚了! .

【问题讨论】:

  • 好吧,在分配/初始化新对象之前,只需执行object = nil;,您就会得到很多想要的东西。

标签: ios objective-c memory-management automatic-ref-counting


【解决方案1】:

这可能不是 ARC 问题。您可能会看到您的 autorelease pool 没有足够快地耗尽 - 您的 MyObject 正在被释放,但由于一些内部 -retain/-autorelease 对,它加载的数据被池保留。尝试将您的 -initWithData: 调用包装在 @autoreleasepool 块中,如下所示:

@autoreleasepool {
    object = [[MyObject alloc] initWithData:folder1];
    // do things
}
// later…
@autoreleasepool {
    object = [[MyObject alloc] initWitData:folder2];
    // do other things
}

将对象设置为 nil,然后按照 Gabriele 的建议将其设置为其他值可能会导致编译器在第二个 -alloc/-initWithData: 之前插入适当的版本,但这样做可能已经足够聪明了——如果这不起作用,很可能是 autorelease-pool 的问题。

【讨论】:

  • 我修改了我的答案。它肯定会在指针重新分配之前释放内存,否则会发生内存泄漏,因为不会引用内存的这一部分。
  • 对——我是说这可能发生在重新分配之前立即,即在第二个 -alloc/-initWithData: 之后但在 object 设置为之前结果,在这种情况下,内存中会暂时有两个 MyObjects。
  • 查看我对 Gabriele 的回答的评论:旧对象在分配新对象之后 被释放(如果我正确理解文档的话)。所以这两个答案都有道理。
  • 效果很好!否则我的代码会炸毁 iPhone Simulator 上 400MB 以上的 RAM。现在它低于 90 就像它什么都不做一样。非常感谢。
【解决方案2】:

耗尽@autoreleasepool {...} 时没有延迟;池中的对象立即调用了release。如果一个对象幸存下来,那是因为在别处有一个 strong 引用,或者是因为该对象被 autoreleased 放入了下一个池中。

如果你这样做:

 a = [[Foo alloc] initBigThing];
 a = nil;
 a = [[Foo alloc] initBigThing];

Foo 的第一个实例将在第二个分配之前释放

有一个重要的警告;如果调用a 的任何代码路径发生在retain/autorelease 上,那么它将一直存在直到池耗尽。将它包围在@autoreleasepool{ ... }; 中应该可以解决问题。

请注意,编译器有时会在非优化构建中发出 retain/autorelease 序列,而这些序列在优化构建中会被消除。

【讨论】:

    【解决方案3】:

    更一般的答案,我发现了如何强制释放对象:

    #import <objc/message.h>
    
    // ---
    
    while ([[object valueForKey:@"retainCount"] integerValue] > 1) {
        objc_msgSend(object, NSSelectorFromString(@"release"));
    }
    objc_msgSend(object, NSSelectorFromString(@"release"));
    

    但您不应该这样做,因为 ARC 可能会在稍后释放对象,这会导致崩溃。该方法只能在调试中使用!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      • 2016-12-24
      • 2015-09-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多