4

我的应用程序使用 CocoaTouch 原生构建,并且在释放 UIView 子类实例时,ARC 崩溃。

这是崩溃日志。

OS Version:      iOS 6.1.3 (10B329)

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x00000008
Crashed Thread:  0

0: 0x39de65b0 libobjc.A.dylib objc_msgSend + 16
1: 0x31edb694 CoreFoundation -[NSArray makeObjectsPerformSelector:] + 300
2: 0x33d8c57a UIKit -[UIView(UIViewGestures) removeAllGestureRecognizers] + 146
3: 0x33d8c144 UIKit -[UIView dealloc] + 440
4: 0x00240b36 MyApp -[StandardPanelView .cxx_destruct](self=0x20acba30, _cmd=0x00240985) + 434 at StandardPanelView.m:139
5: 0x39deaf3c libobjc.A.dylib object_cxxDestructFromClass(objc_object*, objc_class*) + 56
6: 0x39de80d2 libobjc.A.dylib objc_destructInstance + 34
7: 0x39de83a6 libobjc.A.dylib object_dispose + 14
8: 0x33d8c26a UIKit -[UIView dealloc] + 734
9: 0x0022aa14 MyApp -[StandardPanelView dealloc](self=0x20acba30, _cmd=0x379f1a66) + 156 at StandardPanelView.m:205
10: 0x39de8488 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 168
11: 0x31e98440 CoreFoundation _CFAutoreleasePoolPop + 16
12: 0x31f28f40 CoreFoundation __CFRunLoopRun + 1296
13: 0x31e9bebc CoreFoundation CFRunLoopRunSpecific + 356
14: 0x31e9bd48 CoreFoundation CFRunLoopRunInMode + 104
15: 0x35a502ea GraphicsServices GSEventRunModal + 74
16: 0x33db1300 UIKit UIApplicationMain + 1120
17: 0x00113c50 MyApp main(argc=1, argv=0x2fd3ed30) + 140 at main.m:23

问题是:

  1. 什么可能设置错误导致内部调用 [UIView(UIViewGestures) removeAllGestureRecognizers] 崩溃。一种理论是,手势数组中的某些手势已经在其他地方释放。
  2. 当一个 UIView 包含子视图时,释放过程的顺序是怎样的?

一些额外的背景信息:

  1. 崩溃发生了,但没有确切的方法来重现它。
  2. StandardPanelView 实例作为手势代表属于其子视图。
  3. 我们在 StandardPanelView 实例上使用享元,即缓存和回收。

提前感谢您提供有关此崩溃如何发生的任何提示。

4

1 回答 1

1

我的第一印象是您可能正在尝试访问刚刚被释放的 StandardPanelView。

  • 可能设置错误导致对 [UIView(UIViewGestures) removeAllGestureRecognizers] 的内部调用崩溃。一种理论是,手势数组中的某些手势已经在其他地方释放。

这不会是因为 UIGestureRecognizer 被释放了。UIView 强烈地将 UIGestureRecognizers 保存在 NSArray 中。当它们仍在数组中时,它们不会被释放。

但是,UIGestureRecognizer 的委托可能已被释放。这只是一个(分配)属性,这意味着它不是强持有的,如果委托被解除分配,它将是一个悬空指针。因此,如果在[NSArray makeObjectsPerformSelector:]委托中使用,这可能会发生。

  • 当一个 UIView 包含子视图时,释放过程的顺序是怎样的?

对象从“父”释放到“子”,即。超级视图被释放,然后是子视图,然后是手势识别器。(尽管在手势识别器之前是否释放子视图是一个实现细节,所以你可能不应该依赖它)。

我们可以在一个简单的示例控制器中看到这一点:

// 将用作主视图的 UIView
// 这是超级视图
@interface 我的视图:UIView
@结尾

@implementation 我的视图
- (无效)dealloc {
    NSLog(@"Dealloc MyView");
}
@结尾


// 这个视图将被放在 MyView 中用作子视图
@interface MySubview : UIView
@结尾

@implementation MySubview
- (无效)dealloc {
    NSLog(@"Dealloc MySubview");
}
@结尾


// 这是我们将使用的手势识别器
// 我们将为每个视图分配一个,并查看它何时被释放
@interface MyGestureRecognizer : UIGestureRecognizer
@property (nonatomic, copy) NSString *tag;
@结尾

@implementation MyGestureRecognizer
@synthesize 标签;
-(无效)dealloc {
    NSLog(@"Dealloc MyGestureRecognizer 标签:%@", tag);
}
@结尾


// 只是一个测试视图控制器,我们将按下/弹出屏幕以查看释放
@interface TestViewController : UIViewController
@结尾

@implementation TestViewController
-(无效)加载视图{
    self.view = [[MyView alloc] init];
    MyGestureRecognizer *recognizer = [[MyGestureRecognizer alloc] initWithTarget:self action:@selector(doStuff)];
    识别器.tag = @"MyViewGestureRecognizer";
    识别器.delegate = self;
    [self.view addGestureRecognizer:recognizer];

}

- (void)viewDidLoad {
    [超级视图DidLoad];

    MySubview *subview = [[MySubview alloc] init];
    MyGestureRecognizer *recognizer = [[MyGestureRecognizer alloc] initWithTarget:self action:@selector(doStuff)];
    识别器.tag = @"MySubviewGestureRecognizer";
    识别器.delegate = self;
    [子视图 addGestureRecognizer:recognizer];
    [self.view addSubview:subview];
}

-(无效)doStuff {
  // 我们实际上并不关心它做了什么
}
@结尾

我们所做的只是添加 MyView 作为 TestViewController 的主视图,然后在 MyView 中添加一个 MySubview。我们还将 MyGestureRecognizer 附加到每个视图。

当我们将其推离屏幕时,我们的日志输出显示:

Dealloc TestViewController
Dealloc MyView
Dealloc MySubview
Dealloc MyGestureRecognizer 标签:MySubviewGestureRecognizer
Dealloc MyGestureRecognizer 标签:MyViewGestureRecognizer

很抱歉,答案很长......自您发布以来已经过去了大约 3 个月,所以也许您已经解决了这个问题,但是如果其他人偶然发现这个答案,我希望它会有所帮助。

于 2013-11-25T08:07:12.147 回答