7

我是 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 上运行新的分配,但感觉就像我在黑暗中拍摄并且有点 hack。可能有正确的方法来做到这一点?

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,但找不到任何有用的信息。我尝试了分配模板中的仪器,但在那里也找不到任何有用的东西(可能是因为我不知道在哪里看)。

这就是我的分配跟踪的样子,你可以看到内存都被释放有点太晚了! 有效的 XHTML.

4

3 回答 3

12

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

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

正如 Gabriele 所建议的那样,在将对象设置为其他内容之前立即将对象设置为可能会导致编译器在第二个/nil之前插入适当的版本,但它可能已经足够聪明地这样做了——如果这不起作用,它很可能是自动释放池的东西。-alloc-initWithData:

于 2013-08-31T18:40:32.193 回答
4

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

如果你这样做:

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

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

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

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

于 2013-08-31T20:02:06.077 回答
3

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

#import <objc/message.h>

// ---

while ([[object valueForKey:@"retainCount"] integerValue] > 1) {
    objc_msgSend(object, NSSelectorFromString(@"release"));
}
objc_msgSend(object, NSSelectorFromString(@"release"));

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

于 2015-11-16T11:09:45.897 回答