iPhone - 需要一些关于 EXC_BAD_ACCESS 的帮助,这让我发疯(包括完整的源代码)

Posted

技术标签:

【中文标题】iPhone - 需要一些关于 EXC_BAD_ACCESS 的帮助,这让我发疯(包括完整的源代码)【英文标题】:iPhone - Need some help with EXC_BAD_ACCESS that drives me crazy (full source code included) 【发布时间】:2011-09-13 22:39:24 【问题描述】:

我有一个使用 GoogleMap 视图的应用。 在该应用程序中,我想显示一些自定义注释和 userLocation 的自定义视图。当位置管理器给出一个标题时,我想为 userLocation 显示该自定义视图,如果它无法提供一个标题,我希望返回默认的蓝色动画点,等等......

项目和源代码可用HERE

嗯,这里有两个问题:

第一个问题,真的很糟糕。你可以用这个小应用玩几个小时。但是...启动它,等待它加载,将其发送到后台,将您的 iPhone 置于睡眠模式,等待一分钟,唤醒您的 iPhone,启动应用程序(将其唤醒):崩溃,EXC_BAD_ACCESS。做同样的事情,不要让 iPhone 进入睡眠模式:没有崩溃。不是等待一分钟而是几秒钟,没有崩溃。 10 秒,没有崩溃,20 秒,没有崩溃,接近 30 秒,也许是崩溃,接近一分钟,崩溃!

第二个问题(奖励:-)), 可以与第一个问题联系起来,但这并不是这个问题的真正核心:我用来实现 userPinLocation 更新的解决方案/to the blue pin 似乎有一个非常糟糕的副作用:当您在街上移动时,userLocation 不会在地图上移动。对于第二个问题的任何答案,如果它与崩溃没有直接联系,请使用 cmets not answers。这不是问题的核心……我认为……

为了找到错误代码,我已将我的应用程序缩减到最低限度。这给出了以下代码(源代码中的一些提示为 cmets):

EXC_BAD_ACCESS_TestViewController.h

#import <MapKit/MapKit.h>

@interface EXC_BAD_ACCESS_TestViewController : UIViewController<CLLocationManagerDelegate> 

    CLLocationManager* locationMgr;
    NSMutableArray* customAnnotations;
    CLLocationDirection userHeading;
    MKMapView* myMapView;


@property(nonatomic, retain) CLLocationManager* locationMgr;
@property(nonatomic, retain) NSMutableArray* customAnnotations;
@property(nonatomic, assign) CLLocationDirection userHeading;
@property(nonatomic, retain) IBOutlet MKMapView* myMapView;

- (void) loadAnnotations;

@end

EXC_BAD_ACCESS_TestViewController.m

// On first launch, due to code simplification, the app may crash when asked authorization to use geo hardware. Just kill/relaunch after accepting.
// Breakpoints on each method entry : EXC_BAD_ACCESS crash without any break-stop

#import "EXC_BAD_ACCESS_TestViewController.h"
#import "CustomAnnotation.h"

@implementation EXC_BAD_ACCESS_TestViewController

@synthesize locationMgr, customAnnotations, myMapView, userHeading;

// ===========================================================================================================
-(id) initWithCoder:(NSCoder *)aDecoder

    self = [super initWithCoder:aDecoder];
    if (!self) return nil;

    self.userHeading = -1;

    return self;


// ===========================================================================================================
- (void) viewDidLoad

    [self loadAnnotations];

    self.myMapView.mapType = MKMapTypeStandard;
    self.myMapView.showsUserLocation = YES;

    // -----------------------------------------------
    // Just comment this sole line : no more crash
    // -----------------------------------------------
    for (CustomAnnotation* pin in self.customAnnotations) [self.myMapView addAnnotation:pin];

    // locationManager
    self.locationMgr = [[CLLocationManager alloc] init];
    self.locationMgr.desiredAccuracy = kCLLocationAccuracyBest;
    self.locationMgr.distanceFilter = 1.0;
    self.locationMgr.headingFilter = 1.0;
    self.locationMgr.purpose = @"Some purpose.";
    self.locationMgr.delegate = self;
    [self.locationMgr startUpdatingHeading];

    [super viewDidLoad];


// ===========================================================================================================
- (void) loadAnnotations
   
    // Code for testing, real code gets the datas using another way of doing, as simple as this one
    self.customAnnotations = [NSMutableArray array];
    double latitude = 45.0;
    double longitude = -45.0;

    for (int i=1; i<=400; i++) 
        CLLocationCoordinate2D pinLocation = CLLocationCoordinate2DMake(latitude, longitude);
        CustomAnnotation* pin = [[[CustomAnnotation alloc] initWithCoordinate:pinLocation] autorelease];            
        [self.customAnnotations addObject:pin];

        latitude += 0.01;
        longitude += 0.01;
    


// ===========================================================================================================
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation

    MKAnnotationView* pinView = nil;
    NSString* annotationIdentifier;
    UIImage* pinImage;
    BOOL canShowCallout;

    // User location
    if ([annotation isKindOfClass:[MKUserLocation class]] && self.userHeading >= 0) 
        annotationIdentifier = @"UserLocationAnnotation";
        pinImage = [UIImage imageNamed:@"custom_userlocation.png"];
        canShowCallout = NO;
    
    // Custom Pin
    else if ([annotation isKindOfClass:[CustomAnnotation class]]) 
        annotationIdentifier = @"CustomAnnotation";
        pinImage = [UIImage imageNamed:@"custom_pin.png"];
        canShowCallout = YES;
    
    // Others
    else 
        return nil;
    

    pinView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];

    if (pinView) 
        pinView.annotation = annotation;
    
    else 
        pinView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier] autorelease];
        if (pinView) 
            pinView.image = pinImage;
            pinView.canShowCallout = canShowCallout;

            if (canShowCallout) 
                pinView.calloutOffset = CGPointMake(-5, 5);
                pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
            
        
    

    return pinView;


// -----------------------------------------------
// Just comment this method : no more crash
// -----------------------------------------------
// ===========================================================================================================
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading

    if (!self.myMapView) return;
    if (!self.myMapView.userLocation) return;

    CLLocationDirection compassHeading_True = newHeading.trueHeading;
    CLLocationDirection compassHeading_Magnetic = newHeading.magneticHeading;

    CLLocationDirection heading;
    if (compassHeading_True == -1)              heading = compassHeading_Magnetic;
    else if (newHeading.headingAccuracy >= 0)   heading = compassHeading_True;
    else                                        heading = -1;

    // If we get/loose the heading, update pin image
    // I didn't found any other solution to force the user pin to update its display.
    if ((self.userHeading == -1 || heading == -1) && self.userHeading != heading) 
        [self.myMapView removeAnnotation:self.myMapView.userLocation];
        self.userHeading = heading;
        [self.myMapView addAnnotation:self.myMapView.userLocation];
    

    self.userHeading = heading;

    // ------------------------------------
    // Some non bugued code was there
    // ------------------------------------


// ===========================================================================================================
- (void) dealloc
   
    self.myMapView = nil;
    self.customAnnotations = nil;
    self.locationMgr = nil;

    [super dealloc];


@end

CustomAnnotation.h

#import <MapKit/MapKit.h>

@interface CustomAnnotation : NSObject<MKAnnotation> 
    CLLocationCoordinate2D coordinate;


@property(nonatomic, assign) CLLocationCoordinate2D coordinate;

- (id)initWithCoordinate:(CLLocationCoordinate2D) c;
@end

CustomAnnotation.m

#import "CustomAnnotation.h"

@implementation CustomAnnotation

@synthesize coordinate;

// ===========================================================================================================
- (id)initWithCoordinate:(CLLocationCoordinate2D) c

    self = [super init];
    if (!self) return nil;

    self.coordinate = c;

    return self;


// ===========================================================================================================
- (NSString*)subtitle 

    return @"";


// ===========================================================================================================
- (NSString*)title 

    return @"";


@end

我在每种方法中都尝试了断点,尝试使用环境变量启用 Zombies(但由于这需要在设备上运行,我不确定它们是否真的被激活),没有任何解决方案的开始......我仍然不知道出了什么问题。

您知道如何解决 EXC_BAD_ACCESS 问题吗? 对于第二个问题的任何答案,如果它不能解决崩溃,请使用 cmets 而不是答案。就我所见,这个问题并不是问题的核心。

提示:我在上面的代码中添加了 2 个 cmets,在那里我找到了一些解决方案的开始。有两个地方似乎没有联系,可以从代码中删除一个或另一个来解决问题。让我抓狂的是 OR。

在 iPhone 4 ios 4.2.1 上运行

【问题讨论】:

您能否为我们创建 Tiny App 并将其上传到 mediafire.com 或其他网站,在此处发布 URL,以便我们获取。懒得创建项目并复制粘贴代码进行调试。 @Nikita :好的,完成,看看我的编辑 [self.myMapView removeAnnotation:self.myMapView.userLocation]; // 这行导致堆栈跟踪崩溃这是我所看到的.. @Nikita:你是怎么看到的?我在堆栈跟踪中看不到任何内容...如果这是问题所在,如果不进入调试模式并直接崩溃,这怎么可能?有什么可以在不丢失功能的情况下解决这个问题?为什么只删除添加注释的第一行,与此无关,可以解决这个问题? 我认为 Nikita 的原因是正确的。即使这不是主要原因,手动删除和添加地图视图自己的 userLocation 注释(尤其是在 showsUserLocation 为 YES 时)可能是一个非常糟糕的主意。地图视图可能会在您删除注释并再次添加注释之间尝试访问用户位置视图。如果地图视图试图对它有一个引用的前一个视图做某事,那可能会导致 EXC_BAD_ACCESS。代替显式 removeAnn:userLoc,将 showUserLocation 设置为 NO,而不是显式 addAnn:userLoc,将 showUserLocation 设置为 YES。 【参考方案1】:
[self.myMapView removeAnnotation:self.myMapView.userLocation];

这一行导致堆栈跟踪崩溃,这是我所看到的。

当我看到 exc_bad_access 时,我在 gdb 中做了一个 bt。我添加了 Guard Malloc 断点。查看它所说的关于删除注释的堆栈。

【讨论】:

能否详细说明“gdb 中的 bt。我添加了 Guard Malloc 断点。” ?什么是BT?你是如何设置 Guard malloc bp 的? BT 回来了 Trace。 XCode -> Goto Run -> Show -> Breakpoints (Alt-Command-B),滚动到列表底部并添加符号 malloc_error_break 我这样做了,但如果没有定义断点,它的作用完全相同。您能否将一些细节(屏幕截图)放入您的答案中以供更多读者阅读?您定义的 BP,以及显示错误的堆栈跟踪。请问?【参考方案2】:

您可以尝试在视图控制器 .m 文件中覆盖名为 didReceiveMemoryWarning 的方法。

有时会发生设备上的内存因运行应用程序而变低的情况,并且 IOS 会向应用程序发送消息。现在的问题是当你不覆盖内存警告方法时,它的默认操作是从内存中删除任何不可见的subviews。当您的应用程序运行时,这可能会导致僵尸变量。

查看apple doc

【讨论】:

不是我认为的问题,它在真正的应用程序中被覆盖。你测试了吗?

以上是关于iPhone - 需要一些关于 EXC_BAD_ACCESS 的帮助,这让我发疯(包括完整的源代码)的主要内容,如果未能解决你的问题,请参考以下文章

需要一些 iphone SDK 代码(将应用内图片保存到照片中)

关于在 iPhone 应用程序在后台时确定关闭位置的建议

iPhone 使用 xcoding、私有框架阻止 SMS

需要关于在没有 iphone 但我有 ipod touch 的情况下提交 Iphone 应用程序的帮助

iPhone应用程序开发[关闭]

我们是不是需要为所有 iPhone/iPad 设备提供单独的故事板?