1

我的应用程序中出现了 exc_bad_access,但我不知道如何修复它。我刚刚升级到 Xcode 4.5,我的目标是 IOS 5.0。这也是我第一次使用 UIPageViewController。我更喜欢尽可能多地使用故事板。

我正在尝试做的是重新创建一个高尔夫球场翻书,其中包含可以滚动和缩放的图像。我现在基本上已经有了几个主要工作的教程的混搭;

1)我设置了一个 UIPageviewController,它加载了一个 UIScrollview,它向它添加了一个 imageView。2)通过手势或点击翻转正在工作,滚动正在工作,我有捏缩放工作和自定义单指和两指点击以放大/缩小。3) 当您开始用滑动手势翻页,然后松开手指时,会出现崩溃。这基本上取消了翻转,但随后一条消息被发送到一个僵尸对象。

这是我的“GuideViewController.h”,它充当数据源和根。

#import <UIKit/UIKit.h>
#import "YardageHoleViewController.h"

@interface GuideViewController : UIViewController <UIPageViewControllerDataSource>

@property (strong, nonatomic) UIPageViewController *pageController;
@property (strong, nonatomic) NSArray *pageContent;

- (YardageHoleViewController *)viewControllerAtIndex:(NSUInteger)index storyboard (UIStoryboard *)storyboard;
- (NSUInteger)indexOfViewController:(YardageHoleViewController *)viewController;

@end

这是实施

#import "GuideViewController.h"
#import "GolfCourseAppDelegate.h"
#import "Hole.h"
@interface GuideViewController ()
@end

@implementation GuideViewController
@synthesize pageContent = _pageContent;
@synthesize pageController = _pageController;

- (void)viewWillDisappear:(BOOL)animated
{
[[[GolfCourseAppDelegate sharedDelegate] locationManager] stopUpdatingLocation];
}

- (void)viewDidLoad
{
[super viewDidLoad];
[[[GolfCourseAppDelegate sharedDelegate] locationManager] startUpdatingLocation];
[self createContentPages];

NSDictionary *options =
[NSDictionary dictionaryWithObject:
 [NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin]
                            forKey: UIPageViewControllerOptionSpineLocationKey];

self.pageController = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationVertical options: options];

//self.pageController.delegate = self;

self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];

YardageHoleViewController *initialViewController = [self viewControllerAtIndex:0 storyboard:self.storyboard];

NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];

[self.pageController setViewControllers:viewControllers
                         direction:UIPageViewControllerNavigationDirectionForward
                          animated:NO
                        completion:NULL];

[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];

}

- (YardageHoleViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{
NSLog(@"getting data view controller at index: %d", index);
// Return the data view controller for the given index.
if (([self.pageContent count] == 0) || (index >= [self.pageContent count])) {
    return nil;
}

// Create a new view controller and pass suitable data.
YardageHoleViewController *yardageHoleViewController = [storyboard instantiateViewControllerWithIdentifier:@"YardageHoleViewController"];
yardageHoleViewController.dataObject = [self.pageContent objectAtIndex:index];
return yardageHoleViewController;
}

- (NSUInteger)indexOfViewController:(YardageHoleViewController *)viewController
{
// Return the index of the given data view controller.
// For simplicity, this implementation uses a static array of model objects and the view controller stores the model object; you can therefore use the model object to identify the index.
NSLog(@"returning indexOfViewController : %d", [self.pageContent indexOfObject:viewController.dataObject]);
return [self.pageContent indexOfObject:viewController.dataObject];
}

#pragma mark - Page View Controller Data Source

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSLog(@"getting view controller before view controller");
NSUInteger index = [self indexOfViewController:(YardageHoleViewController *)viewController];
if ((index == 0) || (index == NSNotFound)) {
    return nil;
}

index--;
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSLog(@"getting view controller After view controller");
NSUInteger index = [self indexOfViewController:(YardageHoleViewController *)viewController];
if (index == NSNotFound) {
    return nil;
}

index++;
if (index == [self.pageContent count]) {
    return nil;
}
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}

- (void) createContentPages
{
NSLog(@"creating content Pages");
int totalHoles = [[[GolfCourseAppDelegate appData] objectForKey:@"holes"] count];

NSMutableArray *holeData = [[NSMutableArray alloc] init];
for (int i = 1; i < totalHoles+1; i++)
{
    Hole *newHole = [[Hole alloc] initWithHoleNumber:i imageUrl:[NSString stringWithFormat:@"hole%@%d.jpg", (i < 10) ? @"0" : @"", i]];
    NSLog(@"Hole image url:%@",newHole.imageUrl);
    //int holeNumber = i;
    //NSString *imageUrl = [NSString stringWithFormat:@"hole%@%d.jpg", (i < 10) ? @"0" : @"", i];
   [holeData addObject:newHole];
}

self.pageContent = [[NSArray alloc] initWithArray:holeData];
NSLog(@"count of holeData %d", self.pageContent.count);
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

现在对于视图,我们正在浏览“YardageHoleViewController.h”

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#import <CoreLocation/CoreLocation.h>

@interface YardageHoleViewController : UIViewController <UIScrollViewDelegate, CLLocationManagerDelegate>

@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
@property (assign, nonatomic) int hole;
@property (assign, nonatomic) int totalHoles;
@property (strong, nonatomic) id dataObject;
@property (strong, nonatomic) IBOutlet UILabel *frontLabel;
@property (strong, nonatomic) IBOutlet UILabel *middleLabel;
@property (strong, nonatomic) IBOutlet UILabel *backLabel;
- (IBAction)nextPage:(id)sender;
- (IBAction)previousPage:(id)sender;
- (IBAction)infoPage:(id)sender;
- (IBAction)homePage:(id)sender;

- (void)updateDistanceDisplay;
- (NSString *)formatDistance:(NSNumber *)distance;

@end

在这里你可以看到我有一些事情正在发生。有一些子视图可以根据位置等显示到杯子的距离。您还可以看到一些插座,除了手势之外,我还想在顶部有按钮进行导航,现在这不起作用,因为手势覆盖了按钮点击(稍后的另一个问题)。

所以这里是肉和土豆'YardageHoleViewController.m'

#import "YardageHoleViewController.h"
#import "GolfCourseAppDelegate.h"
#import "Hole.h"

@interface YardageHoleViewController ()
@property (nonatomic, strong) UIImageView *imageView;
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
@end

@implementation YardageHoleViewController
@synthesize scrollView = _scrollView;
@synthesize hole = _hole;
@synthesize totalHoles = _totalHoles;
@synthesize imageView = _imageView;
@synthesize frontLabel = _frontLabel;
@synthesize middleLabel = _middleLabel;
@synthesize backLabel = _backLabel;
@synthesize dataObject = _dataObject;

/* The point of this method is to get around a slight annoyance with UIScrollView, which is: if the scroll view content size is smaller than its bounds, then it sits at the top-left rather than in the center. This method  positions the image view such that it is always in the center of the scroll view’s bounds.
 */
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;

if (contentsFrame.size.width < boundsSize.width) {
    contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
    contentsFrame.origin.x = 0.0f;
}

if (contentsFrame.size.height < boundsSize.height) {
    contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
    contentsFrame.origin.y = 0.0f;
}

self.imageView.frame = contentsFrame;
}

- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
CGPoint pointInView = [recognizer locationInView:self.imageView];
CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
}

- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
// Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
}

- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
// Return the view that you want to zoom
return self.imageView;
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so you need to re-center the contents
[self centerScrollViewContents];
}

- (void)viewDidLoad {
[super viewDidLoad];
Hole *hole = (Hole*)self.dataObject;
self.hole = hole.holeNumber;
UIImage *image = [UIImage imageNamed:hole.imageUrl];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = image.size;
//Here you’re setting up two gesture recognizers: one for the double-tap to zoom in, and one for the two-finger-tap to zoom out.
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];

UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewTwoFingerTapped:)];
twoFingerTapRecognizer.numberOfTapsRequired = 1;
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
[[[GolfCourseAppDelegate sharedDelegate] locationManager] setDelegate:self];
[self updateDistanceDisplay];

self.totalHoles = [[[GolfCourseAppDelegate appData] objectForKey:@"holes"] count];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:@"UIDeviceOrientationDidChangeNotification" object:nil];
}

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
self.scrollView.minimumZoomScale = scaleWidth;
self.scrollView.maximumZoomScale = 1.5f;
self.scrollView.zoomScale = scaleWidth;
[self centerScrollViewContents];
}

- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight) {
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];
    NSString *moviePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"hole%@%d", (self.hole < 10) ? @"0" : @"", self.hole] ofType:@"mp4"];
    MPMoviePlayerViewController *viewController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:moviePath]];
    viewController.moviePlayer.controlStyle = MPMovieControlStyleNone;
    viewController.view.backgroundColor = [UIColor blackColor];
    [self presentMoviePlayerViewControllerAnimated:viewController];
} else {
    [self dismissMoviePlayerViewControllerAnimated];
}
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
[self updateDistanceDisplay];
}

- (void) updateDistanceDisplay {

CLLocation *userLocation = [[GolfCourseAppDelegate sharedDelegate] userLocation];

if (userLocation != nil) {
    NSMutableDictionary *holeLocations = [[[GolfCourseAppDelegate appData] objectForKey:@"holes"] objectForKey:[NSString stringWithFormat:@"hole%d", self.hole]];

    if (round([[[holeLocations objectForKey:@"front"] objectForKey:@"lat"] floatValue]) == 0) {
        self.frontLabel.text = @"---";
    } else {
        CLLocation *frontLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:@"front"] objectForKey:@"lat"] floatValue] longitude:[[[holeLocations objectForKey:@"front"] objectForKey:@"lng"] floatValue]];
        if (([frontLocation distanceFromLocation:userLocation]/1000)>1000){
            self.frontLabel.text = @"Out of Range";
        }else{
        self.frontLabel.text = [self formatDistance:[NSNumber numberWithFloat:([frontLocation distanceFromLocation:userLocation]/1000)]];
        }
    }

    if (round([[[holeLocations objectForKey:@"middle"] objectForKey:@"lat"] floatValue]) == 0) {
        self.middleLabel.text = @"---";
    } else {
        CLLocation *middleLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:@"middle"] objectForKey:@"lat"] floatValue] longitude:[[[holeLocations objectForKey:@"middle"] objectForKey:@"lng"] floatValue]];
        self.middleLabel.text = [self formatDistance:[NSNumber numberWithFloat:([middleLocation distanceFromLocation:userLocation]/1000)]];

    }

    if (round([[[holeLocations objectForKey:@"back"] objectForKey:@"lat"] floatValue]) == 0) {
        self.backLabel.text = @"---";
    } else {
        CLLocation *backLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:@"back"] objectForKey:@"lat"] floatValue] longitude:[[[holeLocations objectForKey:@"back"] objectForKey:@"lng"] floatValue]];
        self.backLabel.text = [self formatDistance:[NSNumber numberWithFloat:([backLocation distanceFromLocation:userLocation]/1000)]];

    }
}
}

- (NSString *) formatDistance:(NSNumber *)distance {

NSNumber *displayDistance;
NSString *unitSuffix = @"";

// Convert km to yards if prefs say so.
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];

if ([[preferences stringForKey:@"measurementUnit"] isEqualToString:@"meters"]) {

    distance = [NSNumber numberWithFloat:([distance floatValue]*1000.0)];
    if ([distance floatValue] < 1000.0) {
        displayDistance = distance;
        unitSuffix = @"";
    } else {
        displayDistance = [NSNumber numberWithFloat:([distance floatValue]/1000.0)];
        unitSuffix = @"km";
    }

} else {

    distance = [NSNumber numberWithFloat:([distance floatValue]*1.0936133*1000.0)];
    if ([distance floatValue] < 1760.0) {
        displayDistance = distance;
        unitSuffix = @"";
    } else {
        displayDistance = [NSNumber numberWithFloat:([distance floatValue]/1760.0)];
        unitSuffix = @"mi";
    }

}

NSNumberFormatter *decimalStyle = [[NSNumberFormatter alloc] init];
[decimalStyle setFormatterBehavior:NSNumberFormatterBehavior10_4];
[decimalStyle setNumberStyle:NSNumberFormatterDecimalStyle];
[decimalStyle setRoundingMode:NSNumberFormatterRoundFloor];
[decimalStyle setRoundingIncrement:[NSNumber numberWithFloat:1.0]];

NSString *finalDistance = [decimalStyle stringFromNumber:displayDistance];

return [NSString stringWithFormat:@"%@%@", finalDistance, unitSuffix];
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
[self setImageView:nil];
[self setScrollView:nil];
[self setFrontLabel:nil];
[self setBackLabel:nil];
[self setBackLabel:nil];
[self setFrontLabel:nil];
[self setMiddleLabel:nil];
[super viewDidUnload];
}
- (IBAction)nextPage:(id)sender {
//TODO
// [((UIPageViewController*)self.parentViewController) setViewControllers:
// target direction:UIPageViewControllerNavigationForward completion:nil];
}

- (IBAction)previousPage:(id)sender {
//TODO
//  [((UIPageViewController*)self.parentViewController) setViewControllers:<#(NSArray *)#> direction:UIPageViewControllerNavigationDirectionReverse animated:true completion:nil];
}

- (IBAction)infoPage:(id)sender {
//TODO
}

- (IBAction)homePage:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
@end

哇!大量阅读。所以我首先尝试的是设置异常断点。没运气。然后我添加了很多 NSlog 语句来查看我们崩溃的位置,最后在仪器中寻找僵尸。在这里,我们在 YardageHoleViewController 中的 malloc 上看到“一条 Objective-C 消息已发送到地址为 0x1386e0e0 的已释放对象(僵尸)”。

从我的 NSLog 语句中,我可以看到成功的翻页看起来像这样;2012-12-16 13:33:52.280 BAP 模板 [1365:13a03] 获取索引处的数据视图控制器:0 //从此处开始翻转 > 2012-12-16 13:34:06.289 BAP 模板 [1365:13a03] 获取视图控制器在视图控制器 2012-12-16 13:34:06.290 BAP 模板 [1365:13a03] 返回 indexOfViewController 之后:0 2012-12-16 13:34:06.292 BAP 模板 [1365:13a03] 在索引处获取数据视图控制器: 1

这是当您开始翻转然后发布 2012-12-16 13:36:18.613 BAP Template[1365:13a03] 在索引处获取数据视图控制器时发生的情况:0 //开始翻转然后发布 2012-12-16 13: 36:21.828 BAP 模板 [1365:13a03] 获取视图控制器后视图控制器 2012-12-16 13:36:21.829 BAP 模板 [1365:13a03] 返回 indexOfViewController:0 2012-12-16 13:36:21.831 BAP 模板 [ 1365:13a03] 在索引处获取数据视图控制器:1

所以从某种意义上说,它试图表现得好像它完成了翻转,但我们没有,然后就是我们遇到了糟糕的时刻 =(

我已将所有内容设置为强,我真的不知道接下来要尝试什么?

一般来说,对我的代码的任何建议将不胜感激。提前致谢! 僵尸简介

更新我查看了组织者线程 0 名称中的崩溃日志:调度队列:com.apple.main-thread 线程 0 崩溃:0 libobjc.A.dylib 0x3737bf78 objc_msgSend + 16 1 CoreLocation 0x3405ddc0 -[CLLocationManager onClientEventLocation:] + 1136 2 CoreLocation 0x3405d77e -[CLLocationManager onClientEvent:supportInfo:] + 194 3 CoreLocation 0x34057e38 __CLClientInvokeCallback_block_invoke_0 + 48 我已经注释掉了在 viewDidLoad / willDisappear 中开始/停止更新位置的两行。

不再崩溃,但为什么呢?

4

1 回答 1

1

由于在视图加载时开始位置更新,因此该方法

- (void)locationManager:didUpdateToLocation:fromLocation:

在模拟器中被连续调用,就像每秒一样,但是在设备上它只有在检测到运动时才会被调用。该方法内部是调用 [self updateDistanceDisplay],这就是它崩溃的原因。我实施的修复现在在设备上运行良好,但它不是防弹的。

首先,在 appDelegate 中,我们要修复 locationManager.distanceFilter 以便不是每个小动作都会触发委托方法。

self.locationManager.distanceFilter = 1.0f;

接下来,修改 didUpdateToLocation 方法以仅在纬度或经度发生变化时更新显示。

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
   if ((newLocation.coordinate.latitude!=oldLocation.coordinate.latitude)||(newLocation.coordinate.longitude!=oldLocation.coordinate.longitude))
       [self updateDistanceDisplay];
}

再次,不是防弹的。有一种情况,假设用户在高尔夫球车中行驶得足够快并试图翻页,我们可能会进入崩溃状态。

于 2012-12-20T18:27:55.767 回答