Memory management 具有无限随机注释的MKMapView

Memory management 具有无限随机注释的MKMapView,memory-management,mkmapview,mkannotation,mapkit,Memory Management,Mkmapview,Mkannotation,Mapkit,我试图用无限量的注释填充MKMapView,当用户在地图上滚动时可以看到这些注释。显然,用户必须被放大到足以显示视图的程度,因为如果不放大,应用程序将无法处理,一次只能显示大约20个视图 我有一个大约100个对象的数组,我需要在地图上的任意位置随机重复。这些是在运行时使用MKMapView的visibleMapRect属性创建的,以仅创建必要的。我还实现了一个缓存字典,以防止重新创建以前创建的对象。以下是我当前的简化代码: @property (nonatomic, strong) NSArra

我试图用无限量的注释填充MKMapView,当用户在地图上滚动时可以看到这些注释。显然,用户必须被放大到足以显示视图的程度,因为如果不放大,应用程序将无法处理,一次只能显示大约20个视图

我有一个大约100个对象的数组,我需要在地图上的任意位置随机重复。这些是在运行时使用MKMapView的visibleMapRect属性创建的,以仅创建必要的。我还实现了一个缓存字典,以防止重新创建以前创建的对象。以下是我当前的简化代码:

@property (nonatomic, strong) NSArray *mapObjects; //Contains 100 objects to be repeated
@property (nonatomic, strong) NSMutableDictionary *cache; //Cache already created mapObjects

//Constants used
int const MapDensity  = 5000.0; //Density of annotations
int const MapZoomMax  = 30000.0; //Max zoom level to show annotations

- (void)loadLocations {
    MKMapRect rect = [mapView visibleMapRect];

    if (rect.size.width > MapZoomMax) {
    [self performSelectorOnMainThread:@selector(removeAllAnnotations) withObject:nil waitUntilDone:NO];
        return;
    }

    rect.origin.x = MapDensity*floor(rect.origin.x/MapDensity);
    rect.origin.y = MapDensity*floor(rect.origin.y/MapDensity);

    MKMapPoint pointLocation = rect.origin;
    NSMutableArray *locationsArray = [NSMutableArray array];

    while (pointLocation.y < rect.origin.y+rect.size.height) {
        while (pointLocation.x < rect.origin.x+rect.size.width) {
            int cacheKey = pointLocation.x*pointLocation.y;
            if (![self.cache objectForKey:[NSNumber numberWithInt:cacheKey]]) {

                //Adjust for randomness
                MKMapPoint pointLocationAdjusted = pointLocation;
                pointLocationAdjusted.x += arc4random()%MapDensity;
                pointLocationAdjusted.y += arc4random()%MapDensity;

                //Create annotation
                GLAnnotation *annotation = [[GLAnnotation alloc] init];
                [annotation setCoordinate:MKCoordinateForMapPoint(pointLocationAdjusted)];
                [annotation setMapObject:[self.mapObjects objectAtIndex:(arc4random()%[mapObjects count])]];
                [locationsArray addObject:annotation];

                [self.cache setObject:annotation forKey:[NSNumber numberWithInt:cacheKey]];
            } else {
                [locationsArray addObject:[self.cache objectForKey:[NSNumber numberWithInt:cacheKey]]];
            }
            pointLocation.x += MapDensity; //Go to next X
        }
        pointLocation.x = rect.origin.x; //Restart X
        pointLocation.y += MapDensity; //Go to next Y
    }

    [self performSelectorOnMainThread:@selector(addAnnotations:) withObject:locationsArray waitUntilDone:NO];
}

- (void)addAnnotations:(NSArray *)annotations {
    NSMutableArray *newAnnotations = [NSMutableArray array];
    for (id annotation in annotations) {
        if (![mapView.annotations containsObject:annotation]) {
            [mapView addObject:annotation];
        }
    }
    [mapView addAnnotations:newAnnotations];
}

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    [self performSelectorInBackground:@selector(loadLocations) withObject:nil];
}

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
    if([annotation isKindOfClass:[GLAnnotation class]]){
        static NSString *annotationIdentifier = @"AnnotationIdentifier";

        MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
        if(!annotationView){
            annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
            annotationView.canShowCallout = YES;
        }

        annotationView.image = [(GLAnnotation *)annotation picture];
        return annotationView;
    }
    return nil;
}
@property(非原子,强)NSArray*mapObjects//包含100个要重复的对象
@属性(非原子,强)NSMutableDictionary*缓存//缓存已创建的映射对象
//使用的常数
int const MapDensity=5000.0//注释密度
int const MapZoomMax=30000.0//显示注释的最大缩放级别
-(无效)装载位置{
MKMapRect rect=[mapView visibleMapRect];
如果(rect.size.width>MapZoomMax){
[self-performSelectorOnMainThread:@selector(RemoveAllNotations),对象:nil waitUntilDone:NO];
返回;
}
rect.origin.x=MapDensity*floor(rect.origin.x/MapDensity);
rect.origin.y=MapDensity*floor(rect.origin.y/MapDensity);
MKMapPoint pointLocation=rect.origin;
NSMutableArray*位置数组=[NSMutableArray];
while(pointLocation.y

代码似乎大部分时间都在工作,但有时会导致地图滚动延迟(我尝试在后台线程中运行它),并且通常会导致应用程序崩溃,这似乎是由内存问题(我尝试使用缓存修复)引起的。不输出任何错误消息,除非:EXC\u BAD\u ACCESS。如果有人能告诉我如何正确管理这么多的注释,我将不胜感激。

将它们缓存(到磁盘),并按地图块地址键入。请参阅:

我认为您可能会生成超过需要的25000000个项目。但是请检查我的数学。您将从rect.origin开始为每个坐标对创建一个注释,并按MapDensity递增。因此,每个5000x5000块对应一个对象。正在缓存的第一个(0,0)。但是,如果地图视图只更改了一个贴图点,则(0,1)将有一个全新的对象和以下5000x5000块。也许你应该把你的起点移到下一个最低点,然后从那里开始递增。这样,您就可以一直重用同一对象,直到您的起点离开5000x5000块


此外,它们对缓存进行哈希运算会导致冲突。(1,12),(2,6),(3,4),(4,3),(6,2),(12,1),(-1,-12),(-2,-6)等,所有哈希值都相同。由于密钥可以是字符串,请尝试[NSString stringWithFormat:@“%d_%d”,x,y]替换。

是的,关于缓存错误,您是对的。这部分似乎工作正常,所以我没有注意到,但如果用户在一些敏感的位置,它很容易被注意到。非常感谢。在第一部分中,我添加了以下代码来改变起点,但仍然会出现很多内存错误:
rect.origin.x-=MapDensity*floor(rect.origin.x/MapDensity)
rect.origin.y-=MapDensity*floor(rect.origin.y/MapDensity)看起来你正在穿越河流。regionDidChangeAnimated调用发生在ui线程上,然后在后台加载位置