“正常” UIButton 导致 obj_stack_overflow 或 EXC_BAD_ACCESS 异常
它看起来确实足够无害。在我的应用程序委托中,我检查 NSUserDefaults 是否有一个标志以在启动时显示提示。如果已设置,则在 applicationDidFinishLaunching:
的末尾,我会执行以下操作:
TipsViewController *vc = [[TipsViewController alloc]
initWithNibName:@“TipsView" bundle:nil];
[window addSubview:vc.view];
[vc release];
想法是暂时显示此视图。 (请注意,它不是模态 VC。此时没有导航控制器,而且此视图中也没有导航栏。)
一旦关闭此视图,我们将添加抢占的 UITabBarController' s 视图到窗口并过渡到它,然后从主窗口中删除提示视图。我还没有到达视图消除点,因为,好吧,继续阅读。
我的 TipsView 的 VC 或多或少是这样连接的:
UIView -> view -> File’s Owner (TipsViewController)
UIImageView -> background image
UIView -> tipView -> File’s Owner
UIImageView -> background image
UIScrollView
UILabel (tip text)
UIButton -> touch-up-inside -> -(IBAction)button1:
UIButton -> touch-up-inside -> -(IBAction)button2:
UIButton -> touch-up-inside -> -(IBAction)button3:
源代码包含所有三个 IBAction 调用的声明和定义。现在他们两个什么也不做。第三个更改提示文本
,调整其大小以适应,并调整滚动视图的contentSize
以匹配。
当我运行应用程序时,TipsViewController 视图显示得很好。我什至可以滚动浏览提示文本。然而,当我在任何 UIButton 上触发内部修饰时,Xcode 开始将我植入源代码中(我在每个 IBAction 处放置了一个断点)...然后使用 EXC_BAD_ACCESS
或 obj_stack_overflow
退出。
我将其与应用程序的其他部分进行了比较,其中有 VC、视图和按钮。它在各方面都是相同的,除了在本例中,我添加了 VC 的视图作为应用程序窗口的子视图,而不是将 VC 推送到导航控制器。此外,iPhone OS 视图控制器编程指南文档说这是公平的游戏:
如果您在中使用单个视图控制器 您的应用程序,您将其视图添加到 一个窗口而不是添加视图 控制器到标签栏或导航 控制器。
确实,我确实有一个UITabBarController在伺机而动,它有带有UINavigationControllers(和其他VC)的选项卡。但是,如果显示提示视图,则标签栏控制器的视图尚未添加到窗口中。目的是在我们展示完提示后将其交换。换句话说,无论出于何种意图和目的,我们暂时表现得好像我们只有一个风险投资家在玩。之后,我们切换到标签栏并拆下尖端VC。
也许我做错了什么?有更好的办法吗? (“一定有更好的方法!”)
示例堆栈跟踪:
#0 0x992b6f52 in objc_exception_throw
#1 0x302d6ffb in -[NSObject doesNotRecognizeSelector:]
#2 0x3026e056 in ___forwarding___
#3 0x3024a0a2 in __forwarding_prep_0___
#4 0x308f79d1 in -[UIApplication sendAction:to:from:forEvent:]
#5 0x309598b1 in -[UIControl sendAction:to:forEvent:]
#6 0x3095bad2 in -[UIControl(Internal) _sendActionsForEvents:withEvent:]
#7 0x3095a81e in -[UIControl touchesEnded:withEvent:]
#8 0x30910fdf in -[UIWindow _sendTouchesForEvent:]
#9 0x308faecb in -[UIApplication sendEvent:]
#10 0x309013e1 in _UIApplicationHandleEvent
#11 0x32046375 in PurpleEventCallback
#12 0x30245560 in CFRunLoopRunSpecific
#13 0x30244628 in CFRunLoopRunInMode
#14 0x32044c31 in GSEventRunModal
#15 0x32044cf6 in GSEventRun
#16 0x309021ee in UIApplicationMain
#17 0x00002888 in main at main.m:14
正如您从跟踪中看到的,我们最终得到了 doesNotRecognizeSelector:
...除了我可以清楚地看到我的提示 VC 源代码中的方法。另外,它们都已连接好。 (IB 中没有多重接线或类似的东西。一切看起来都很好,直到文件所有者关系。)
欢迎/赞赏线索!
It sure looks innocuous enough. In my App Delegate, I check NSUserDefaults for a flag to show tips at startup. If it’s set then, at the end of applicationDidFinishLaunching:
, I do this:
TipsViewController *vc = [[TipsViewController alloc]
initWithNibName:@“TipsView" bundle:nil];
[window addSubview:vc.view];
[vc release];
The idea is to show this view temporarily. (Note that it’s not a modal VC. No Navigation Controller at this point, plus there’s no navigation bar in this view anyway.)
Once this view is dismissed, we’ll add our pre-empted UITabBarController’s view to the window and transition over to it, then remove the tip view from the main window. I haven’t made it to the view dismissal point yet because, well, keep reading.
My TipsView’s VC is wired up more or less like so:
UIView -> view -> File’s Owner (TipsViewController)
UIImageView -> background image
UIView -> tipView -> File’s Owner
UIImageView -> background image
UIScrollView
UILabel (tip text)
UIButton -> touch-up-inside -> -(IBAction)button1:
UIButton -> touch-up-inside -> -(IBAction)button2:
UIButton -> touch-up-inside -> -(IBAction)button3:
The source contains declarations and definitions for all three IBAction calls. Right now two of them do nothing. The third one changes the tip text
, resizes it to fit, and adjusts the scroll view’s contentSize
to match.
When I run the app, the TipsViewController view shows up just fine. I can even scroll through the tip text. However, when I trigger a touch-up-inside on any UIButton, Xcode starts to plant me in the source (where I’ve placed a breakpoint at each IBAction) … and then bails out with either a EXC_BAD_ACCESS
or obj_stack_overflow
.
I have compared this with other parts of the app where I have a VC, a view, and buttons. It is identical in every way except that, in this case, I’ve added a VC’s view as a subview of the app’s window instead of pushing the VC onto a navigation controller. Moreover, the View Controller Programming Guide for iPhone OS docs say this is fair game:
If you use a single view controller in
your application, you add its view to
a window instead of adding the view
controller to a tab bar or navigation
controller.
True, I do have a UITabBarController waiting in the wings, and it has tabs with UINavigationControllers (and other VCs) in it. However, if the tip view is being shown, the tab bar controller’s view has not yet been added to the window. The intent is to swap it in after we’re done showing tips. In other words, for all intents and purposes, we’re temporarily acting like we have a single VC in play. Afterward, we switch to the tab bar and tear down the tip VC.
Perhaps I’m doing something subtly wrong? Is there a better way? (“There’s got to be a better way!”)
Sample stack trace:
#0 0x992b6f52 in objc_exception_throw
#1 0x302d6ffb in -[NSObject doesNotRecognizeSelector:]
#2 0x3026e056 in ___forwarding___
#3 0x3024a0a2 in __forwarding_prep_0___
#4 0x308f79d1 in -[UIApplication sendAction:to:from:forEvent:]
#5 0x309598b1 in -[UIControl sendAction:to:forEvent:]
#6 0x3095bad2 in -[UIControl(Internal) _sendActionsForEvents:withEvent:]
#7 0x3095a81e in -[UIControl touchesEnded:withEvent:]
#8 0x30910fdf in -[UIWindow _sendTouchesForEvent:]
#9 0x308faecb in -[UIApplication sendEvent:]
#10 0x309013e1 in _UIApplicationHandleEvent
#11 0x32046375 in PurpleEventCallback
#12 0x30245560 in CFRunLoopRunSpecific
#13 0x30244628 in CFRunLoopRunInMode
#14 0x32044c31 in GSEventRunModal
#15 0x32044cf6 in GSEventRun
#16 0x309021ee in UIApplicationMain
#17 0x00002888 in main at main.m:14
As you can see from the trace, we end up at doesNotRecognizeSelector:
… except I can clearly see the methods in my tip VC source. Plus, they’re all wired up. (No multiple wirings or anything like that in IB. Everything looks good there, down to the File’s Owner relationships.)
Clues welcome/appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题就在你的第一个代码片段中:你正在创建一个 TipsViewController 的实例,保留它的视图,然后释放视图控制器——这将导致它被释放。所以现在按钮的目标是指向已释放视图控制器的指针。
视图不保留其视图控制器,也不保留其委托或目标。
只要您希望显示视图,您就必须保留视图控制器实例(可能在
retain
属性中)。使用完视图后,可以将其从其父视图中删除,并释放视图控制器。The problem is right there in your first code snippet: you’re creating an instance of TipsViewController, retaining its view, then releasing the view controller--which will cause it to be deallocated. So now the
target
of the buttons is a pointer to the deallocated view controller.A view does not retain its view controller, nor do they retain their delegates or targets.
You'll have to keep the view controller instance retained—perhaps in a
retain
property—for as long as you want the view to be displayed. When you're done with the view, you can remove it from its parent view, and release the view controller.