我是否遗漏了 ARC 的某些内容?
我正在 xcode 中使用 ARC 为 iOS 5 开发一个大型应用程序。除了当我尝试释放我的接口之一时,该系统似乎运行良好。我正在使用名为 WhirlyGlobe 的框架在第一个视图控制器中创建 3D 交互式地球仪。
当我切换视图控制器(在我拥有的 4 个视图控制器之间)时,我注意到用于地球仪视图控制器的内存没有被释放。所有其他视图控制器(仅使用简单的视图和图像)都很好地释放了它们的内存 - 但地球仍然驻留,或者看起来是这样。当导航回地球时,由于“glsmLoadTextureLevelBuffer”中分配了 1mb,内存几乎增加了 10mb。
继续回答我的问题 - 在 ARC 处于活动状态的情况下,我还能做些什么来帮助释放我的对象吗?我注意到我的 viewDidUnload 和 dealloc 方法根本没有被调用,并且我可以触发任何东西的唯一方法是使用 viewDidDisappear (这显然并不理想) - 见下文:
- (void)clear
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (self.layerThread)
{
[self.layerThread cancel];
while (!self.layerThread.isFinished)
[NSThread sleepForTimeInterval:0.001];
}
self.glView = nil;
self.sceneRenderer = nil;
if (theScene)
{
delete theScene;
theScene = NULL;
}
self.theView = nil;
self.texGroup = nil;
self.layerThread = nil;
self.earthLayer = nil;
self.vectorLayer = nil;
self.labelLayer = nil;
self.interactLayer = nil;
self.pinchDelegate = nil;
self.panDelegate = nil;
self.tapDelegate = nil;
self.longPressDelegate = nil;
self.rotateDelegate = nil;
}
- (void)viewDidDisappear:(BOOL)animated {
NSLog(@"dealloc - viewDidDisappear");
[self clear];
}
我正在设置我不再需要的所有内容为零。这是最佳实践吗?
地球仪设置代码: [超级viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
// Set up an OpenGL ES view and renderer
EAGLView *ev = [[EAGLView alloc] initWithFrame:CGRectMake(0, 0, 824, self.view.frame.size.height)];
self.glView = ev;
self.sceneRenderer = [[SceneRendererES1 alloc] init];
UIColor *whiteC = [UIColor whiteColor];
[sceneRenderer setClearColor:whiteC];
glView.renderer = sceneRenderer;
glView.frameInterval = 2; // 60 fps (2)
[self.view addSubview:glView];
self.view.backgroundColor = [UIColor blackColor];
self.view.opaque = YES;
self.view.autoresizesSubviews = YES;
//glView.frame = self.view.bounds;
glView.frame = CGRectMake(275, GLOBE_HEIGHT_FIX, 768, SCREEN_HEIGHT+STATUS_BAR_HEIGHT); // was 260 x
glView.backgroundColor = [UIColor whiteColor];
self.view.backgroundColor = [UIColor whiteColor]; // red for debug
// Create the textures and geometry, but in the right GL context
[sceneRenderer useContext];
self.texGroup = [[TextureGroup alloc] initWithInfo:[[NSBundle mainBundle] pathForResource:@"bdGlobe_info" ofType:@"plist"]];
// Need an empty scene and view
theScene = new WhirlyGlobe::GlobeScene(4*texGroup.numX,4*texGroup.numY);
self.theView = [[WhirlyGlobeView alloc] init];
[theView setFarPlane:5.0];
[theView setHeightAboveGlobe:GLOBE_HEIGHT_VIEW];
if (globeShouldAnimate) glView.alpha = 1.0;
// Need a layer thread to manage the layers
self.layerThread = [[WhirlyGlobeLayerThread alloc] initWithScene:theScene];
// Earth layer on the bottom
self.earthLayer = [[SphericalEarthLayer alloc] initWithTexGroup:texGroup];
[self.layerThread addLayer:earthLayer];
// Set up the vector layer where all our outlines will go
self.vectorLayer = [[VectorLayer alloc] init];
[self.layerThread addLayer:vectorLayer];
// General purpose label layer.
self.labelLayer = [[LabelLayer alloc] init];
[self.layerThread addLayer:labelLayer];
self.interactLayer = [[InteractionLayer alloc] initWithVectorLayer:self.vectorLayer labelLayer:labelLayer globeView:self.theView
countryShape:[[NSBundle mainBundle] pathForResource:@"10m_admin_0_map_subunits" ofType:@"shp"]
oceanShape:[[NSBundle mainBundle] pathForResource:@"10m_geography_marine_polys" ofType:@"shp"]
regionShape:[[NSBundle mainBundle] pathForResource:@"10m_admin_1_states_provinces_shp" ofType:@"shp"]];
self.interactLayer.maxEdgeLen = [self.earthLayer smallestTesselation]/10.0;
[self.layerThread addLayer:interactLayer];
// Give the renderer what it needs
sceneRenderer.scene = theScene;
sceneRenderer.view = theView;
// Wire up the gesture recognizers
self.panDelegate = [PanDelegateFixed panDelegateForView:glView globeView:theView];
self.tapDelegate = [WhirlyGlobeTapDelegate tapDelegateForView:glView globeView:theView];
self.longPressDelegate = [WhirlyGlobeLongPressDelegate longPressDelegateForView:glView globeView:theView];
// Kick off the layer thread
// This will start loading things
[self.layerThread start];
I'm developing a large scale application for iOS 5 using ARC in xcode. The system seems to work well except for when I'm trying to deallocate one of my interfaces. I'm using a framework called WhirlyGlobe to create a 3D interactive globe in the first view controller.
When I switch view controllers (between the 4 I have), I notice that the memory being used for the view controller with the globe isn't being released. All the other view controllers (only using simple views and images) release their memory fine - But the globe stays resident, or so it seems. When navigating back to the globe, I get almost a 10mb jump in memory due to 1mb allocations in "glsmLoadTextureLevelBuffer".
To get on with my question - Is there anything more I can do, with ARC active, to help release my objects? I've noticed my viewDidUnload and dealloc methods are not being called at all, and that the only way I can get anything to fire is using viewDidDisappear (which is not ideal obviously) - See below:
- (void)clear
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (self.layerThread)
{
[self.layerThread cancel];
while (!self.layerThread.isFinished)
[NSThread sleepForTimeInterval:0.001];
}
self.glView = nil;
self.sceneRenderer = nil;
if (theScene)
{
delete theScene;
theScene = NULL;
}
self.theView = nil;
self.texGroup = nil;
self.layerThread = nil;
self.earthLayer = nil;
self.vectorLayer = nil;
self.labelLayer = nil;
self.interactLayer = nil;
self.pinchDelegate = nil;
self.panDelegate = nil;
self.tapDelegate = nil;
self.longPressDelegate = nil;
self.rotateDelegate = nil;
}
- (void)viewDidDisappear:(BOOL)animated {
NSLog(@"dealloc - viewDidDisappear");
[self clear];
}
I'm setting everything I no longer need to nil. Is this the best practise?
The globe setup code:
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
// Set up an OpenGL ES view and renderer
EAGLView *ev = [[EAGLView alloc] initWithFrame:CGRectMake(0, 0, 824, self.view.frame.size.height)];
self.glView = ev;
self.sceneRenderer = [[SceneRendererES1 alloc] init];
UIColor *whiteC = [UIColor whiteColor];
[sceneRenderer setClearColor:whiteC];
glView.renderer = sceneRenderer;
glView.frameInterval = 2; // 60 fps (2)
[self.view addSubview:glView];
self.view.backgroundColor = [UIColor blackColor];
self.view.opaque = YES;
self.view.autoresizesSubviews = YES;
//glView.frame = self.view.bounds;
glView.frame = CGRectMake(275, GLOBE_HEIGHT_FIX, 768, SCREEN_HEIGHT+STATUS_BAR_HEIGHT); // was 260 x
glView.backgroundColor = [UIColor whiteColor];
self.view.backgroundColor = [UIColor whiteColor]; // red for debug
// Create the textures and geometry, but in the right GL context
[sceneRenderer useContext];
self.texGroup = [[TextureGroup alloc] initWithInfo:[[NSBundle mainBundle] pathForResource:@"bdGlobe_info" ofType:@"plist"]];
// Need an empty scene and view
theScene = new WhirlyGlobe::GlobeScene(4*texGroup.numX,4*texGroup.numY);
self.theView = [[WhirlyGlobeView alloc] init];
[theView setFarPlane:5.0];
[theView setHeightAboveGlobe:GLOBE_HEIGHT_VIEW];
if (globeShouldAnimate) glView.alpha = 1.0;
// Need a layer thread to manage the layers
self.layerThread = [[WhirlyGlobeLayerThread alloc] initWithScene:theScene];
// Earth layer on the bottom
self.earthLayer = [[SphericalEarthLayer alloc] initWithTexGroup:texGroup];
[self.layerThread addLayer:earthLayer];
// Set up the vector layer where all our outlines will go
self.vectorLayer = [[VectorLayer alloc] init];
[self.layerThread addLayer:vectorLayer];
// General purpose label layer.
self.labelLayer = [[LabelLayer alloc] init];
[self.layerThread addLayer:labelLayer];
self.interactLayer = [[InteractionLayer alloc] initWithVectorLayer:self.vectorLayer labelLayer:labelLayer globeView:self.theView
countryShape:[[NSBundle mainBundle] pathForResource:@"10m_admin_0_map_subunits" ofType:@"shp"]
oceanShape:[[NSBundle mainBundle] pathForResource:@"10m_geography_marine_polys" ofType:@"shp"]
regionShape:[[NSBundle mainBundle] pathForResource:@"10m_admin_1_states_provinces_shp" ofType:@"shp"]];
self.interactLayer.maxEdgeLen = [self.earthLayer smallestTesselation]/10.0;
[self.layerThread addLayer:interactLayer];
// Give the renderer what it needs
sceneRenderer.scene = theScene;
sceneRenderer.view = theView;
// Wire up the gesture recognizers
self.panDelegate = [PanDelegateFixed panDelegateForView:glView globeView:theView];
self.tapDelegate = [WhirlyGlobeTapDelegate tapDelegateForView:glView globeView:theView];
self.longPressDelegate = [WhirlyGlobeLongPressDelegate longPressDelegateForView:glView globeView:theView];
// Kick off the layer thread
// This will start loading things
[self.layerThread start];
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
为此,您可以使用分配工具。使用堆快照,您可以在应用程序生命周期的各个点标记堆,并比较构成每个快照点内存中当前分配的对象图。这应该可以帮助您缩小保留内容和保留对象的范围。
You can use the allocations instrument for this. Using heap shot, you can mark the heap at various points in the lifetime of your application and compare the object graph that constitutes the current allocations in memory at the point of each snapshot. That should help you narrow down what's being retained and by whom.
这是轶事,但可能是类似的情况。
我刚刚遇到过这样一种情况,在 ARC 中,即使以静态方式访问它们(例如,
[SingletonThing instance].thing
),我的对象也从未被释放,因为自动释放池没有排水。其原因是一个奇怪的无限循环在其自己的线程上运行。为封闭代码放置一个单独的@autorelease
块。另一方面,即使您的一个(或库)对象将
UIView
作为子视图,我认为您的 UIViewController 永远不会viewDidUnload
因此永远不会释放。我必须凭经验检查这一点。This is anecdotal, but COULD be a similar situation.
I just had a situation where my objects were not getting released ever, in ARC, even though they were accessed in a static way (e.g.,
[SingletonThing instance].thing
) because the autorelease pool wasn't draining. The reason for this was a bizarre endless loop that runs on its own thread. Putting a separate@autorelease
block for the enclosing code.On the other hand, even if one of your (or the libs) object has the
UIView
as a subview, I think your UIViewController will neverviewDidUnload
and therefore never dealloc. I have to check this empirically.确保您的
-(void)viewDidDisappear:(BOOL)animated
被调用。如果您使用
[self.view addSubview:yourViewController.view];
和
[yourViewController.view removeFromSuperview];
那么
viewDidDisappear:
和viewDidAppear:
不会被调用,这些回调只会被调用
当您在 IOS 5 中使用
presentViewController:
时或
presentModalViewController:
和 iOS 5 中的
dismissViewControllerAnimated:
或
dismissModalViewControllerAnimated:
或使用 UINavigationController 来呈现和关闭您的 viewController
make sure your
-(void)viewDidDisappear:(BOOL)animated
is invoked.if you use
[self.view addSubview:yourViewController.view];
and
[yourViewController.view removeFromSuperview];
then
viewDidDisappear:
andviewDidAppear:
will not be invokedthese callback will only be invoked
when you use
presentViewController:
in IOS 5or
presentModalViewController:
and
dismissViewControllerAnimated:
in IOS 5or
dismissModalViewControllerAnimated:
or use
UINavigationController
to present and dismiss your viewController我在使用堆快照后发现了问题(感谢 Mark Adams) - 事实证明我没有使几个委托成为弱实体,因此在更改视图控制器时它们没有被释放。默认强代表留驻。
感谢所有的建议,它们都帮助我指明了正确的方向:)
I found the problem after using heap shots (thanks to Mark Adams) - Turns out I wasn't making a couple of delegates weak entities, so they weren't being released when changing the view controller. Default strong delegates stay resident.
Thanks to all the suggestions, they all helped point me in the right direction :)