如何在iOS上强制发布

时间:2013-08-31 18:25:26

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

我是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'中的数据被释放,并且我的内存不足。我真正希望能做的是:

object = nil;

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

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

但我不知道该怎么做。我可以在延迟后在一个执行选择器上运行新的分配,但感觉就像我在黑暗中拍摄并且有点黑客攻击。可能有正确的方法吗?

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

更新
谢谢你的答案,我已经尝试了

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

并没有奏效。我不确定是否应该这样做。现在我明白 应该可以工作了,但是我必须在一小段时间内保留其他东西。我在所有的init / dealloc方法中都有NSLogs,我首先可以看到被调用的新类实例(MyObject的ivars)的所有内容,然后几乎立即(在几毫秒内),MyObject的dealloc其次是伊利亚斯的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,但找不到任何有用的信息。我在分配模板中尝试了仪器,但在那里找不到任何有用的东西(可能因为我不知道在哪里看)。

这是我的分配跟踪的样子,你可以看到内存全部解除分配有点太晚了! Valid XHTML

3 个答案:

答案 0 :(得分:11)

这可能不是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,这可能会导致编译器在第二个-alloc / -initWithData:之前插入相应的版本,但它可能是已经足够聪明 - 如果这不起作用,它很可能是autorelease-pool的事情。

答案 1 :(得分:4)

排空@autoreleasepool {...}时没有延迟;池中的对象立即调用release。如果一个对象幸存下来,那是因为在其他地方有一个strong引用,或者因为该对象是autorelease d到下一个池中。

如果你这样做:

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

Foo的第一个实例将在分配第二个

之前发布

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

请注意,编译器有时会在非优化版本中发出retain/autorelease个序列,这些序列在优化版本中被删除。

答案 2 :(得分:3)

更一般的答案,我发现你如何强制释放一个物体:

#import <objc/message.h>

// ---

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

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