Let me explain my problem. I have 3 UIView: a LoginView, LibraryView, and a StoreView. I have this code to switch from one UIView to another:
- (void)showView:(NSInteger)viewTag
{
if (viewTag == 1)
{
if (self.loginView)
{
self.loginView = nil;
self.loginView.delegate = nil;
}
LoginView *loginPage = [[LoginView alloc]initWithFrame:self.view.bounds];
[loginPage setDelegate:self];
self.loginView = loginPage;
[loginPage release];
[self.view addSubview:self.loginView];
}
else if(viewTag == 2)
{
if (self.libraryView)
{
self.libraryView = nil;
self.libraryView.delegate = nil;
}
LibraryView *libraryPage = [[LibraryView alloc]initWithFrame:self.view.bounds];
[libraryPage setDelegate:self];
self.libraryView = libraryPage;
[libraryPage release];
[self.view addSubview:self.libraryView];
}
else
{
if (self.bookStoreView)
{
self.bookStoreView = nil;
self.bookStoreView.delegate = nil;
}
BookStoreView *bookStore = [[BookStoreView alloc]initWithFrame:self.view.bounds];
[bookStore setDelegate:self];
self.bookStoreView = bookStore;
[bookStore release];
[self.view addSubview:self.bookStoreView];
}
}
Basically, that is how I initialize the UIViews. And here are the buttons used to switch between them:
- (void)loginViewToLibraryView
{
[self.loginView removeFromSuperview];
[self showView:2];
}
- (void)libraryViewToStoreView
{
[self.libraryView removeFromSuperview];
[self showView:3];
}
//so on...
The problem arises when the I call the function libraryViewToLoginView and storeViewToLoginView. The app crashes whenever I call these functions, which is weird because the two functions worked properly before. I checked Profile and it gave me this stack trace:
# Address Category Event RefCt Timestamp Size Responsible Library Responsible Caller
0 0xc4dcac0 CALayer Malloc 1 00:02.233.004 48 UIKit -[UIView _createLayerWithFrame:]
1 0xc4dcac0 CALayer Retain 3 00:02.238.317 0 QuartzCore CA::Layer::insert_sublayer(CA::Transaction*, CALayer*, unsigned long)
2 0xc4dcac0 CALayer Release 2 00:02.238.324 0 UIKit -[UIView(Internal) _addSubview:positioned:relativeTo:]
3 0xc4dcac0 CALayer Retain 3 00:02.238.518 0 QuartzCore -[CALayerArray copyWithZone:]
4 0xc4dcac0 CALayer Release 2 00:02.238.602 0 UIKit -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]
5 0xc4dcac0 CALayer Retain 3 00:02.238.665 0 QuartzCore -[CALayerArray copyWithZone:]
6 0xc4dcac0 CALayer Release 2 00:02.238.796 0 UIKit -[UIView(Internal) _didMoveFromWindow:toWindow:]
7 0xc4dcac0 CALayer Retain 3 00:05.107.397 0 QuartzCore -[CALayerArray copyWithZone:]
8 0xc4dcac0 CALayer Release 2 00:05.107.539 0 UIKit -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]
9 0xc4dcac0 CALayer Retain 3 00:05.107.613 0 QuartzCore -[CALayerArray copyWithZone:]
10 0xc4dcac0 CALayer Release 2 00:05.107.700 0 UIKit -[UIView(Internal) _didMoveFromWindow:toWindow:]
11 0xc4dcac0 CALayer Retain 2 00:06.105.958 0 QuartzCore -[CALayerArray copyWithZone:]
12 0xc4dcac0 CALayer Release 2 00:06.108.134 0 UIKit -[UIView dealloc]
13 0xc4dcac0 CALayer Release 1 00:06.108.492 0 UIKit -[UIView dealloc]
14 0xc4dcac0 CALayer Zombie -1 00:06.115.332 0 QuartzCore CA::release_objects(X::List<void const*>*)
As you can see, it is a bunch of calls on the CALayer which I don't really understand. I want to understand why is this happening. Can anyone explain?
Frankly I have not understood everything that you want to achieve here. But you should consider the sequence of events. See my comments:
if (self.loginView)
{
self.loginView = nil;
//self.loginView is nil now. What so you think doese happen on the next line?
self.loginView.delegate = nil;
// change the sequence of this lines and it will be ok.
}
LoginView *loginPage = [[LoginView alloc]initWithFrame:self.view.bounds];
[loginPage setDelegate:self];
self.loginView = loginPage;
[loginPage release]; //here you release the object. it is gone now. However, there are still references to it.
[self.view addSubview:self.loginView]; // here you add the released object. What do you expect to happen?
// Switch those two statmetns and you should be fine.
Well, it may even work as those statmens are next to each other. And when you remove it from its super view it gets released again. And there your app might crash.
BTW, without having it retained you should not release it at all. Or am I wrong here? However, addSubview should retain it and removeFromSuperview will release it, so no additional release would be required. Onece removed it should be gone. (If not retained somewhere else)
Related
I'm having a weird issue with UIViews and manual memory management.
I have a view (contentView) which is the main view of a view controller.
After a long press on the contentView, another view is supposed to fade in (on top of it).
When the gestures ends, the additional view fades out.
The issue is:
When the contentView receives a long press, I create the auxiliary view, add it to the contentView, and then release it, which is/was the common practice back in the pre-ARC days.
It works okay on the iPhone, but it crashes on the iPad!
The crashy line is:
[ZPNowPlayingItemInfoView dealloc]
...which gets triggered when I remove the auxiliary view from the contentView.
Any clues on why this happens?
If I comment out the release line (see my comment in the code), it works flawlessly on both devices, but it feels bad.
Here's the code:
-(void)longPressDetected:(UILongPressGestureRecognizer*)longPressGR
{
//Content view of the view controller I'm in
UIView *contentView = MSHookIvar<UIView*>(self, "_contentView");
if (longPressGR.state == UIGestureRecognizerStateBegan) {
id item = MSHookIvar<MPAVItem*>(self, "_item");
ZPNowPlayingItemInfoView *infoView =
[[ZPNowPlayingItemInfoView alloc] initWithFrame:
CGRectMake(0,0,contentView.frame.size.width,contentView.frame.size.height)
item:item];
//infoView retain count: 1
[infoView setAlpha:0.f];
[contentView addSubview:infoView];
//infoView retain count: 3 (???)
//iPad goes berserk on this line
//Commented - Works both on iPhone and iPad
//Uncommented - Works only on iPhone
//[infoView release];
//infoView retain count: 2 (if release is uncommented)
[UIView animateWithDuration:0.35f animations:^{
[infoView setAlpha:1.0f];
} completion:^(BOOL finished) {
//infoView retain count: 3
}];
} else if (longPressGR.state == UIGestureRecognizerStateEnded) {
ZPNowPlayingItemInfoView* infoView = nil;
for (UIView *subview in contentView.subviews) {
if ([subview isKindOfClass:[ZPNowPlayingItemInfoView class]]) {
infoView = (ZPNowPlayingItemInfoView*)subview;
break;
}
}
[UIView animateWithDuration:0.35f animations:^{
[infoView setAlpha:0.f];
} completion: ^(BOOL finished){
[infoView removeFromSuperview];
}];
}
P.S. I need to use manual memory management. This is a tweak for jailbroken devices.
Stack trace:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x195287bdc 0x19526c000 + 0x1bbdc // objc_msgSend + 0x1c
1 + Musix.dylib 0x10015b19c 0x100154000 + 0x719c // -[ZPNowPlayingItemInfoView dealloc] + 0x48
2 libsystem_blocks.dylib 0x19590d90c 0x19590c000 + 0x190c // _Block_release + 0xfc
3 UIKit 0x188ef8590 0x188eb0000 + 0x48590 // -[UIViewAnimationBlockDelegate dealloc] + 0x44
4 CoreFoundation 0x1845f1374 0x1845ec000 + 0x5374 // CFRelease + 0x208
5 CoreFoundation 0x184601004 0x1845ec000 + 0x15004 // -[__NSDictionaryI dealloc] + 0x8c
6 libobjc.A.dylib 0x19528d720 0x19526c000 + 0x21720 // (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 0x230
7 CoreFoundation 0x1845f4f90 0x1845ec000 + 0x8f90 // _CFAutoreleasePoolPop + 0x18
8 CoreFoundation 0x1846c774c 0x1845ec000 + 0xdb74c // __CFRunLoopRun + 0x5d8
9 CoreFoundation 0x1845f51f0 0x1845ec000 + 0x91f0 // CFRunLoopRunSpecific + 0x188
10 GraphicsServices 0x18d7575a0 0x18d74c000 + 0xb5a0 // GSEventRunModal + 0xa4
11 UIKit 0x188f26780 0x188eb0000 + 0x76780 // UIApplicationMain + 0x5cc
12 Music (*) 0x10006ee28 0x100064000 + 0xae28 // 0x0000adac + 0x7c
13 libdyld.dylib 0x1958e2a04 0x1958e0000 + 0x2a04 // start + 0x0
ZPNowPlayingItemInfoView:
#interface ZPNowPlayingItemInfoView()
#property (nonatomic, retain) MPAVItem* item;
#property (nonatomic, retain) MPUSlantedTextPlaceholderArtworkView *artworkView;
#property (nonatomic, retain) UILabel *artistLabel;
#property (nonatomic, retain) UILabel *albumLabel;
#property (nonatomic, retain) UILabel *songLabel;
#end
ZPNowPlayingItemInfoView dealloc:
-(void)dealloc
{
[super dealloc];
[self.item release];
[self.artworkView release];
[self.artistLabel release];
[self.songLabel release];
}
You have some problem in ZPNowPlayingItemInfoView class. When this problem happens? Only when the object gets deallocated. When you comment [infoView release] out, your object is never deallocated and the problem doesn't arise - you will have a memory leak though.
Inspect what ZPNowPlayingItemInfoView does, especially its dealloc method. Are you sure you are constructing it correctly? Is item always a valid object?
After seeing the ZPNowPlayingItemInfoView dealloc method, the problem is quite clear - [super dealloc] must always be the last call, not the first one. Once you have deallocated the object, accessing its properties is an undefined operation.
When commenting out the release is a working workaround, that indicates that you have released it once too often. It may well be the very one release that you commented out.
removeFromSuperview does reduce the retain count by 1.
I suggest re-visiting the full life cycle of the view object. This can be tricky though. Each retain needs to have exactly one corresponding release or autorelease. Assigning the view to a property using its getter (self.myView = subview) does retain it and re-assigning another view to the property (self.myView = someOhterview) releases subview.
On the contrary accessing the iVar directly (myView = subview) does not maintain the release/retain-cycle.
There is more than that. Adding the view and removing it from an array, set or dictionary will change the retain count accordingly.
So go and have a deeper look at it. Use instruments to observe the retain count.
I'm having a random UIWebView crash using iOS8.1 and UIWebView, using an iPhone5. In my tests the crash doesn't appear on iOS7.
I've created this github repository to reproduce the crash:
https://github.com/crarau/WebViewCrash
Basically I'm adding a UIWebView and loading www.amazon.com
Randomly the app is crashing on the WebThread with EXC_ARM_BREAKPOINT
After enabling Zombie tracking on the console there is this message:
[UIViewAnimationState release]: message sent to deallocated instance 0x14deff70
Here what happens on UIView load:
[super viewDidLoad];
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.amazon.com"]];
self.webView.layer.cornerRadius = 0;
self.webView.userInteractionEnabled = YES;
self.webView.multipleTouchEnabled = YES;
self.webView.backgroundColor = [UIColor clearColor];
self.webView.scrollView.scrollEnabled = NO;
self.webView.scrollView.bounces = NO;
[self.webView loadRequest:urlRequest];
The exception stacktrace:
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x000000000000defe
Triggered by Thread: 2
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0:
0 libsystem_kernel.dylib 0x2fe054f0 mach_msg_trap + 20
1 libsystem_kernel.dylib 0x2fe052e4 mach_msg + 36
2 CoreFoundation 0x21fed936 __CFRunLoopServiceMachPort + 142
3 CoreFoundation 0x21febedc __CFRunLoopRun + 1012
4 CoreFoundation 0x21f3a20c CFRunLoopRunSpecific + 472
5 CoreFoundation 0x21f3a01e CFRunLoopRunInMode + 102
6 GraphicsServices 0x293330a4 GSEventRunModal + 132
7 UIKit 0x255461cc UIApplicationMain + 1436
8 WebViewCrash 0x0002deec main (main.m:14)
9 libdyld.dylib 0x2fd52aac start + 0
Thread 1 name: Dispatch queue: com.apple.libdispatch-manager
Thread 1:
0 libsystem_kernel.dylib 0x2fe052a0 kevent64 + 24
1 libdispatch.dylib 0x00162674 0x154000 + 58996
2 libdispatch.dylib 0x00157496 0x154000 + 13462
Thread 2 name: WebThread
Thread 2 Crashed:
0 CoreFoundation 0x2202aea2 ___forwarding___ + 534
1 CoreFoundation 0x21f5cdf4 _CF_forwarding_prep_0 + 20
2 CoreFoundation 0x21f2ee58 CFRelease + 596
3 QuartzCore 0x24f0ba60 CA::release_objects(X::List<void const*>*) + 12
4 QuartzCore 0x24f10dc2 -[CAAnimation dealloc] + 50
5 libobjc.A.dylib 0x2f7ecd5a objc_object::sidetable_release(bool) + 162
6 libobjc.A.dylib 0x2f7ed1a4 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 400
7 CoreFoundation 0x21fee2a4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 12
8 CoreFoundation 0x21fede1e __CFRunLoopDoTimer + 646
9 CoreFoundation 0x21fec06e __CFRunLoopRun + 1414
10 CoreFoundation 0x21f3a20c CFRunLoopRunSpecific + 472
11 CoreFoundation 0x21f3a01e CFRunLoopRunInMode + 102
12 WebCore 0x2d362ebe RunWebThread(void*) + 414
13 libsystem_pthread.dylib 0x2fe95e90 _pthread_body + 136
14 libsystem_pthread.dylib 0x2fe95e02 _pthread_start + 114
15 libsystem_pthread.dylib 0x2fe93b8c thread_start + 4
Any advice is appreciated
Thanks
I'm experiencing the same issue on iOS 8 and more specifically an iPad 3 fails much more often than others.
Based on your post and a couple of others that I was able to find, it seems that it's an issue with UIWebView on iOS8 only, as it works on iOS7.
What I've done is to use the new WKWebView on iOS8, which doesn't present the same issue, while continuing to use UIWebView on iOS7.
The best thing to do would be to file a radar with Apple and switch to WKWebView in the meantime.
If you are not using animations you can avoid this bug by disabling Animations. Put this in your controller init method:
[UIView setAnimationsEnabled:NO];
[super viewDidLoad];
[[MPVolumeView alloc] init]; <------ add this line, that will be ok too.
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.amazon.com"]];
self.webView.layer.cornerRadius = 0;
self.webView.userInteractionEnabled = YES;
self.webView.multipleTouchEnabled = YES;
self.webView.backgroundColor = [UIColor clearColor];
self.webView.scrollView.scrollEnabled = NO;
self.webView.scrollView.bounces = NO;
[self.webView loadRequest:urlRequest];
This work around appears to fix it for my app. I'm not comfortable with this, but it's currently the only thing I've been able to figure out to get rid of the crash.
[self.webView performSelector:#selector(loadRequest:)
withObject:urlRequest] afterDelay:0.5];
Or, if you want something safer you can use WKWebView on iOS 8+
Since WKWebView doesn't support local files, start a web server:
[GCDWebServer setLogLevel:4];
webServer = [[[GCDWebServer alloc] init] retain];
[webServer addGETHandlerForBasePath:#"/" directoryPath:rootDir indexFilename:#"index.html" cacheAge:3600 allowRangeRequests:YES];
[webServer startWithPort:0 bonjourName:nil];
Then, depending on the OS version, create your webview:
if (NSClassFromString(#"WKWebView"))
{
NSString* jScript = #"";
WKUserScript* wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController* wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];
WKWebViewConfiguration* wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = wkUController;
webViewNew = [[WKWebView alloc] initWithFrame:CGRectMake(0.f, 0.f, 1.f, 1.f) configuration:wkWebConfig];
webViewNew.navigationDelegate = self;
webViewNew.scrollView.bounces = NO;
webViewNew.scrollView.scrollEnabled = NO;
webViewNew.backgroundColor = [UIColor redColor];
webViewNew.opaque = YES;
}
else
{
webViewOld = [[UIWebView alloc] initWithFrame: CGRectMake (0, 0, 1.0f, 1.0f)];
webViewOld.delegate = self;
[[webViewOld scrollView] setBounces: NO];
webViewOld.scrollView.scrollEnabled = NO;
webViewOld.scalesPageToFit = YES;
[webViewOld setBackgroundColor:[UIColor whiteColor]];
[webViewOld setOpaque:NO];
}
And then browse to: [NSString stringWithFormat: "htztp://127.0.0.1:%d", (int)webServer.port]
(Ignore the z in the http, stackoverflow won't let me post urls to localhost)
I solved the problem. I had the same issue adding a video tag (source was a local mp4 file approx. 18Mb). I could identify the video tag as the crash responsible because html without video doesn't crash. Then i tried to inject two seconds later the video tag via javascript and the app crashed. After a few hours i found this: UIWebView loading html5-Video EXC_BAD_ACCESS crash.
My solution was to extract the video tag injection in a javascript function and call that function on webViewDidFinishLoad.
So, in my controller i have:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[self.webView stringByEvaluatingJavaScriptFromString:#"placeVideoTag();"];
}
While in my index.html i have:
...
<div id="left">
<div id="media-div"></div>
...
and (i added an audio tag too!):
<script>
function placeVideoTag() {
$('#media-div').html('<video id="videoController" width="100%" controls > <source src="video_1.mp4" type="video/mp4"></video>');
$('#media-div').append('<br/><audio id="audioController" controls="" width="100%"><source src="audio_1.mp3" type="audio/mp3"></audio>');
}
</script>
I know it sounds as a strange solution, but it is really working. I had the same identical issue as stated here, also enabling Zombie tracking
Randomly the app is crashing on the WebThread with EXC_ARM_BREAKPOINT
After enabling Zombie tracking on the console there is this message:
[UIViewAnimationState release]: message sent to deallocated instance
0x14deff70
make sure you set the UIWebView delegate to nil in the dealloc method
- (void)dealloc {
[self.webView stopLoading];
self.webView.delegate = nil;
}
Try to update to iOS 8.4.1. It worked for us (however, we didn't find any references to this bug in release notes).
I know am late but this could help someone, make sure your IBOutlet views are weak. This answer helped me to get rid of this issue.
The setContentViewController method in UIPopoverController seems to be causing an app crash in iOS 8. Just wondering if anybody else also faced this issue in iOS 8. This works without any issue in iOS 7.
The error pointed in the exception seems to be misleading as it states that the setContentViewController should be called after presenting the popover
- (void)buttonPressed {
UIViewController *tableViewController = [UIViewController new];
if(_popover == nil){
_popover = [[UIPopoverController alloc] initWithContentViewController:tableViewController];
[_popover presentPopoverFromRect:CGRectMake(self.textField.frame.size.width / 2, self.textField.frame.size.height / 1, 1, 1) inView:self.textField permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}else{
[_popover setContentViewController:tableViewController];
}
}
Here is Stack trace from the crash,
2014-09-11 16:48:39.904 iOS 8 Rotation[3969:67869] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIPopoverController setContentViewController:animated:] can only be called after the popover has been presented.'
*** First throw call stack:
(
0 CoreFoundation 0x01c79df6 __exceptionPreprocess + 182
1 libobjc.A.dylib 0x01903a97 objc_exception_throw + 44
2 CoreFoundation 0x01c79d1d +[NSException raise:format:] + 141
3 UIKit 0x00b1946f -[UIPopoverPresentationController _setContentViewController:animated:] + 89
4 UIKit 0x009bb1b4 -[UIPopoverController setContentViewController:animated:] + 155
5 UIKit 0x009bb114 -[UIPopoverController setContentViewController:] + 48
6 iOS 8 Rotation 0x00046ca5 -[MianViewController buttonPressed] + 933
7 libobjc.A.dylib 0x019197cd -[NSObject performSelector:withObject:withObject:] + 84
8 UIKit 0x002ef79d -[UIApplication sendAction:to:from:forEvent:] + 99
9 UIKit 0x002ef72f -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 64
10 UIKit 0x00422a16 -[UIControl sendAction:to:forEvent:] + 69
11 UIKit 0x00422e33 -[UIControl _sendActionsForEvents:withEvent:] + 598
12 UIKit 0x0042209d -[UIControl touchesEnded:withEvent:] + 660
13 UIKit 0x0033faba -[UIWindow _sendTouchesForEvent:] + 874
14 UIKit 0x00340595 -[UIWindow sendEvent:] + 791
15 UIKit 0x00305aa9 -[UIApplication sendEvent:] + 242
16 UIKit 0x003158de _UIApplicationHandleEventFromQueueEvent + 20690
17 UIKit 0x002ea079 _UIApplicationHandleEventQueue + 2206
18 CoreFoundation 0x01b9d7bf __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
19 CoreFoundation 0x01b932cd __CFRunLoopDoSources0 + 253
20 CoreFoundation 0x01b92828 __CFRunLoopRun + 952
21 CoreFoundation 0x01b921ab CFRunLoopRunSpecific + 443
22 CoreFoundation 0x01b91fdb CFRunLoopRunInMode + 123
23 GraphicsServices 0x040cc24f GSEventRunModal + 192
24 GraphicsServices 0x040cc08c GSEventRun + 104
25 UIKit 0x002ede16 UIApplicationMain + 1526
26 iOS 8 Rotation 0x0004774d main + 141
27 libdyld.dylib 0x0224cac9 start + 1
28 ??? 0x00000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
I just encountered this same error testing our app under iOS 8. In our case, I took the error message at face value and changed a pattern we had in a few places.
The pattern we had to change was:
(1) in our view controller's init, instantiate a popover controller instance.
(2) on some event, set the popover controller's contentViewController property to the desired vc.
(3) call presentPopoverFromRect on the popover controller
and we simply changed step (2) to re-instantiate the popover controller with the desired content vc as an init parameter and ceased setting the contentViewController property (as we're always doing it prior to presenting the popover).
I was experiencing this same issue and finally solved it.
I have two buttons, each one shows its own popover. I was reusing the same UIPopoverController to show both of them. The first click worked fine, but then if you clicked the other one the app crashed.
The way I solved it is create a new UIPopoverController on each click:
importImagePickerControlPopoverController=[[UIPopoverController alloc] initWithContentViewController:pickerController];
[importImagePickerControlPopoverController setDelegate:self];
switch(pickerType)
{
case UIImagePickerControllerSourceTypePhotoLibrary:
case UIImagePickerControllerSourceTypeSavedPhotosAlbum:
[importImagePickerControlPopoverController setPopoverContentSize:CGSizeMake(320, 300) animated:YES];
break;
case UIImagePickerControllerSourceTypeCamera:
[importImagePickerControlPopoverController setPopoverContentSize:CGSizeMake(640, 640) animated:YES];
break;
}
[importImagePickerControlPopoverController setContentViewController:pickerController];
Similar to #user3495742 solution:
-(void)testAndSetPopoverWithVc:(UIViewController*)vc {
if (popover4Menu) {
if ([popover4Menu isPopoverVisible]) {
[popover4Menu setContentViewController:vc animated:YES];
return;
} else {
popover4Menu=nil;
}
};
popover4Menu=[[UIPopoverController alloc] initWithContentViewController:vc];
popover4Menu.delegate=self;
}
Problem rises when reassigning a view controller to an existing popover.
Free and realloc popover before assigning new content view controller: this fixed for me.
I had the same issue and this is what fixed it for me.
Try This
- (void)buttonPressed{
UIViewController *tableViewController = [UIViewController new];
//Check if the popover is nil or not visible
if(_popover == nil || _popover.popoverVisible == false){
_popover = [[UIPopoverController alloc] initWithContentViewController:tableViewController];
[_popover presentPopoverFromRect:CGRectMake(self.textField.frame.size.width / 2, self.textField.frame.size.height / 1, 1, 1) inView:self.textField permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}else{
[_popover setContentViewController:tableViewController];
}
}
You have already have the content view controller set:
_popover = [[UIPopoverController alloc] initWithContentViewController:tableViewController];
↑
Here
So, why not just delete the line?:
[_popover setContentViewController:tableViewController];
That should work.
If popover is not visible when you are calling [_popover setContentViewController:tableViewController];, app will get crash.
Because this method should be called when popover is visible on the screen.
Make sure your popover is visible,
if(_popover != nil && [_popover isPopoverVisible] == YES)
{
[_popover setContentViewController:tableViewController];
}else
{
//create new popover object if _popover is nil or present it
}
[self.popover dismissPopoverAnimated:YES]; //in case it's already showing.
self.popover = nil; // N.B. this is not the same as popover = nil.
self.popover = [[UIPopoverController alloc] initWithContentViewController:tableViewController];
[self.popover presentPopoverFromRect:CGRectMake(self.textField.frame.size.width /
2, self.textField.frame.size.height / 1, 1, 1) inView:self.textField
permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
You do not need to create new instance of UIPopoverController neither set new contentViewController property after you first present UIPopoverViewController. (it depends on how you dismiss you popoverController)
However contentViewController can't be changed before popoverController presentation.
To workaround check popoverController.contentViewController properties. If it is nil, set conntentViewController, otherwise just present popover.
If you want to change contentViewController do it after presentation: use setContentViewController:animated: method.
Check that popoverController.isPopoverVisible before call this method.
Add these two methods into your UIwebview main class.
add <UIPopoverPresentationControllerDelegate> in your webview interface.h class as well.
define .
#property (strong, nonatomic) UIPopoverPresentationController *pop;
then
#synthesize pop;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Assuming you've hooked this all up in a Storyboard with a popover presentation style
if ([segue.identifier isEqualToString:#"showPopover"]) {
UINavigationController *destNav = segue.destinationViewController;
pop = destNav.viewControllers.firstObject;
// This is the important part
UIPopoverPresentationController *popPC = destNav.popoverPresentationController;
popPC.delegate = self;
}
}
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone;
}
This code worked under iOS6.2; installed iOS7 and now crashes:
- (IBAction)bOpenCamera:(UIButton *)sender {
if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
customerCameraFlag = YES; // indicator so text fields are not cleared
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = (id)self;
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePicker.mediaTypes = [NSArray arrayWithObjects: (NSString *) kUTTypeImage, nil];
imagePicker.allowsEditing = NO;
[self presentViewController:imagePicker animated:YES completion:nil];
newMedia = YES;
}
}
I use the same code in two separate classes; neither one work. The camera never opens, just crashes when the last line (newMedia = YES) has executed. I have looked at all the entries in SO and Google, and found nothing that resembles this crash. It apparently worked on iOS 6, but I am now unable to test it because I have gone to iOS7.
Any ideas on how to fix this?
*EXC_BAD_ACCESS is not a thrown exception; it is a hard crash. Spokane-dude should post the backtrace of the actual crash.*
when running the same app on the iPad under iOS7, outside of the debugger it works fine!
I'd bet it isn't working fine. It only appears to be working, but something is still going wildly wrong, potentially corrupting user data. I'd make figuring out what a priority.
Post the crash details.
OK -- crash is here:
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x3b39c350 __pthread_kill + 8
1 libsystem_c.dylib 0x3b31311e pthread_kill + 54
2 libsystem_c.dylib 0x3b34f96e abort + 90
3 REDACTED.APP 0x0006aa72 0x19000 + 334450
4 Foundation 0x338a438c -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 164
5 Foundation 0x338a4088 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 132
That the main thread is dispatching to itself is pretty odd and indicative of a shaky, at best, concurrency implementation.
But wait! The crash is actually further up Thread 0:
11 libsystem_c.dylib 0x3b34f96e abort + 90
12 libsystem_c.dylib 0x3b3304cc __assert_rtn + 176
13 REDACTED.APP 0x0001c3f2 0x19000 + 13298
14 UIKit 0x34e4bd18 -[UIView(CALayerDelegate) drawLayer:inContext:] + 360
15 QuartzCore 0x34bfabe0 -[CALayer drawInContext:] + 108
Whatever is in frame 13 -- some layer delegate trying to draw -- is assert()ing.
Given that it is a failed assert in a drawing method, that should narrow things down considerably.
My app built natively with CocoaTouch and ARC crashes when deallocating a UIView subclass instance.
Here is the crash log.
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
The questions are:
what could be set wrong that makes the internal call to [UIView(UIViewGestures) removeAllGestureRecognizers] crash. One theory is that some gesture in the gestures array is deallocated already somewhere else.
When a UIView contains subviews, how is the sequence of deallocation process?
Some extra background info:
The crash happens, but there is no exact way to reproduce it.
The StandardPanelView instance works as delegate for gestures belongs to its subviews.
We are using flyweight on StandardPanelView instances, i.e., caching and recycling.
Thanks in advance for any hint about how this crash could happen.
My first impression is that you might be trying to access the StandardPanelView, which has just gotten deallocated.
What could be set wrong that makes the internal call to [UIView(UIViewGestures) removeAllGestureRecognizers] crash. One theory is that some gesture in the gestures array is deallocated already somewhere else.
It will not be because a UIGestureRecognizer got deallocated. The UIView strongly holds the UIGestureRecognizers in an NSArray. They will not be deallocated while they are still in the array.
However, the UIGestureRecognizer's delegate may have gotten deallocated. That is only an (assign) property, meaning that it is -not- strongly held, and if the delegate is deallocated, it will be a dangling pointer. So, if in [NSArray makeObjectsPerformSelector:] the delegate is used, this might happen.
When a UIView contains subviews, how is the sequence of deallocation process?
Objects are deallocated from 'parent' to 'child', ie. the superview is deallocated, then the subview, then the gesture recognizers. (Although whether the subviews are dealloced before the gesture recognizers is an implementation detail, so you probably shouldn't depend on it).
We can see this in a simple sample controller:
// The UIView that will be used as the main view
// This is the superview
#interface MyView : UIView
#end
#implementation MyView
- (void)dealloc {
NSLog(#"Dealloc MyView");
}
#end
// This view will be put inside MyView to be used as a subview
#interface MySubview : UIView
#end
#implementation MySubview
- (void)dealloc {
NSLog(#"Dealloc MySubview");
}
#end
// This is the gesture recognizer that we will use
// We will give one to each view, and see when it is deallocated
#interface MyGestureRecognizer : UIGestureRecognizer
#property (nonatomic, copy) NSString *tag;
#end
#implementation MyGestureRecognizer
#synthesize tag;
-(void)dealloc {
NSLog(#"Dealloc MyGestureRecognizer tag: %#", tag);
}
#end
// Just a test view controller that we will push on/pop off the screen to take a look at the deallocations
#interface TestViewController : UIViewController
#end
#implementation TestViewController
- (void)loadView {
self.view = [[MyView alloc] init];
MyGestureRecognizer *recognizer = [[MyGestureRecognizer alloc] initWithTarget:self action:#selector(doStuff)];
recognizer.tag = #"MyViewGestureRecognizer";
recognizer.delegate = self;
[self.view addGestureRecognizer:recognizer];
}
- (void)viewDidLoad {
[super viewDidLoad];
MySubview *subview = [[MySubview alloc] init];
MyGestureRecognizer *recognizer = [[MyGestureRecognizer alloc] initWithTarget:self action:#selector(doStuff)];
recognizer.tag = #"MySubviewGestureRecognizer";
recognizer.delegate = self;
[subview addGestureRecognizer:recognizer];
[self.view addSubview:subview];
}
- (void)doStuff {
// we don't actually care what it does
}
#end
All we are doing is adding MyView to be the main view for TestViewController, and then adding a MySubview inside MyView. We also attach a MyGestureRecognizer to each of the views.
When we push it off the screen, our log output presents:
Dealloc TestViewController
Dealloc MyView
Dealloc MySubview
Dealloc MyGestureRecognizer tag: MySubviewGestureRecognizer
Dealloc MyGestureRecognizer tag: MyViewGestureRecognizer
Sorry for the long answer... It has been about 3 months since you posted it, so maybe you already solved the problem, but in case anyone else stumbles across this answer, I hope it helps out.