iPhone - нужна помощь с EXC_BAD_ACCESS, которая сводит меня с ума (полный исходный код включен) - PullRequest
1 голос
/ 14 сентября 2011

У меня есть приложение, которое использует представление GoogleMap.
В этом приложении я хочу отобразить некоторые пользовательские аннотации и пользовательское представление для userLocation.Когда диспетчер местоположений дает заголовок, я хочу отобразить это пользовательское представление для userLocation, если оно не доставляет его, я хочу, чтобы синяя анимированная точка по умолчанию вернулась и т. Д. *

Проекти Исходный код доступен ЗДЕСЬ

Ну, здесь есть 2 проблемы:

1-я проблема, ДЕЙСТВИТЕЛЬНО ПЛОХО ОДИН. Вы можетеиграть часами с этим крошечным приложением.Но ... Запустите его, подождите, пока он загрузится, отправьте его в фоновый режим, переведите iPhone в спящий режим, подождите минуту, разбудите ваш iPhone, запустите приложение (которое будит его): крах, EXC_BAD_ACCESS.Сделайте то же самое, не переводя iPhone в спящий режим: без сбоев.Не ждать ни минуты, а нескольких секунд, без сбоев.10 секунд, без сбоев, 20 секунд, без сбоев, около 30 секунд, возможно, сбои, около минуты, сбой!

2-я проблема (бонус :-)), , которая может бытьсвязан с первым, но это не совсем суть этого вопроса: решение, которое я использую для достижения userPinLocation для обновления с / на синий значок, похоже, имеет очень плохой побочный эффект: userLocation не перемещается на картекак вы двигаетесь на улице.Для любого ответа на эту вторую проблему, если она не связана напрямую с сбоем, используйте комментарии, а не ответы.Это не суть вопроса ... Я думаю ...

Чтобы найти неисправный код, я сократил свое приложение до минимума.Это дает следующий код (некоторые подсказки в исходном коде в виде комментариев):

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

Я пробовал точки останова в каждом методе, пытаясь включить зомби с переменными среды (но так как это нужно запускать на устройстве, я неуверен, что они действительно активированы), без какого-либо начала решения ... Я до сих пор не знаю, что происходит не так.

Вы видите, как решить эту проблему EXC_BAD_ACCESS?
Длялюбой ответ для 2-й проблемы, пожалуйста, используйте комментарии, а не ответы, если это не решит проблему.Насколько я знаю, эта проблема не является основным вопросом.

Подсказка: я поместил 2 комментария в приведенном выше коде, где нашел какое-то начало решения.Есть 2 места, которые, кажется, не связаны между собой, которые могут быть исключены из кода ИЛИ ИЛИ для решения проблемы.Что меня сводит с ума, так это ИЛИ.

Работает на iPhone 4 ios 4.2.1

Ответы [ 2 ]

1 голос
/ 14 сентября 2011
[self.myMapView removeAnnotation:self.myMapView.userLocation];

Эта строка вызывает сбой от трассировки стека, это то, что я видел.

Когда я увидел exc_bad_access, я сделал bt в gdb. Я добавил контрольную точку охраны Malloc. Глядя на стек, он сказал об удалении аннотации.

0 голосов
/ 14 сентября 2011

Вы можете попробовать переопределить метод, называемый didReceiveMemoryWarning, в файле контроллера .m.

Иногда случается, что память устройства заканчивается из-за запуска приложений, и IOS отправляет сообщениеприложение.Теперь проблема в том, что когда вы не отменяете метод предупреждения о памяти, его действие по умолчанию - удалить из памяти все subview s, которые не видны.Это может привести к появлению зомби-переменных при запуске приложения.

Проверьте apple doc

...