マップビューに表示されるアノテーション(カスタムPostLocationAnnotationクラス)を取り込んでクラスターがそれらを閉じ、PostLocationAnnotationsとLocationGroupAnnotationsのMKAnnotationの配列(それぞれにPostLocationAnnotationsが含まれるクラスター)を出力するメソッドがあります。これが私が関数を呼び出す方法です(マップのビューポートが変更されたときに呼び出される'updateAnnotations'メソッド内から):
[annotationsToAdd addObjectsFromArray:[ffMapView annotations]];
[ffMapView addAnnotations:[self clusterAnnotations:annotationsToAdd WithEpsilon:20.0f andMinPts:4]];
annotationsToAddには、サーバーから取得された、まだマップに追加されていない注釈が最初に入力されます。したがって、マップに配置する必要のあるアノテーションの完全なリストをclusterAnnotationsメソッドに渡します。メソッドの本体は次のとおりです。
- (NSArray *)clusterAnnotations:(NSArray *)annotations WithEpsilon:(float)eps andMinPts:(int)minPts
{
NSMutableSet *D = [[NSMutableSet alloc] initWithCapacity:[annotations count]];
NSMutableArray *C = [[NSMutableArray alloc] init];
for (id <MKAnnotation> annotation in annotations)
{
if ([annotation isKindOfClass:[PostLocationAnnotation class]])
{
NSMutableDictionary *dictEntry = [NSMutableDictionary dictionaryWithObjectsAndKeys:
annotation, @"point",
[NSNumber numberWithBool:NO], @"visited",
[NSNumber numberWithBool:NO], @"noise",
[NSNumber numberWithBool:NO], @"clustered", nil];
[D addObject:dictEntry];
[dictEntry release];
} else if ([annotation isKindOfClass:[LocationGroupAnnotation class]])
{
for (PostLocationAnnotation *location in [(LocationGroupAnnotation *)annotation locations])
{
NSMutableDictionary *dictEntry = [NSMutableDictionary dictionaryWithObjectsAndKeys:
location, @"point",
[NSNumber numberWithBool:NO], @"visited",
[NSNumber numberWithBool:NO], @"noise",
[NSNumber numberWithBool:NO], @"clustered", nil];
[D addObject:dictEntry];
[dictEntry release];
}
}
}
for (NSMutableDictionary *P in D)
{
if ([P objectForKey:@"visited"] == [NSNumber numberWithBool:NO])
{
[P setValue:[NSNumber numberWithBool:YES] forKey:@"visited"];
NSMutableSet *N = [[NSMutableSet alloc] initWithSet:[self regionQueryForPoint:P andEpsilon:eps fromList:D]];
if ([N count] < minPts)
{
[P setValue:[NSNumber numberWithBool:YES] forKey:@"noise"];
} else {
LocationGroupAnnotation *newCluster = [[LocationGroupAnnotation alloc] initWithLocations:nil];
[C addObject:newCluster];
[self expandDbscanClusterWithPoint:P andRegion:N andCluster:newCluster andEpsilon:eps andMinPts:minPts fromList:D];
[newCluster release];
}
[N release];
}
}
NSMutableArray *annotationsToAdd = [[[NSMutableArray alloc] initWithCapacity:[annotations count]] autorelease];
for (NSMutableDictionary *P in D)
{
if ([P objectForKey:@"clustered"] == [NSNumber numberWithBool:NO])
{
[annotationsToAdd addObject:[P objectForKey:@"point"]];
}
}
for (LocationGroupAnnotation *cluster in C)
{
[cluster updateCenterCoordinate];
}
[annotationsToAdd addObjectsFromArray:(NSArray *)C];
[D release];
[C release];
return (NSArray *)annotationsToAdd;
}
これを実行するとゾンビメッセージが表示され、[Dリリース]を削除するとゾンビは修正されますが、リークが発生することがわかりました。Instrumentsを見ると、メモリアドレスが最初にclusterAnnotationsでMallocされ、次に2、3回保持および解放され、次にregionQueryForPointによって何度も保持され(47参照のピークに達する)、clusterAnnotationsによって2回解放されることがわかります。 、その後、refcountが-1に達するまで[NSAutoreleasePoolドレイン]によって解放され、ゾンビメッセージエラーが発生します。regionQueryForPointのコードは次のとおりです。
- (NSSet *)regionQueryForPoint:(NSMutableDictionary *)P andEpsilon:(float)eps fromList:(NSMutableSet *)D
{
NSMutableSet *N = [[[NSMutableSet alloc] init] autorelease];
for (NSMutableDictionary *dictEntry in D)
{
if ((dictEntry != P) &&
([[dictEntry objectForKey:@"point"] isKindOfClass:[PostLocationAnnotation class]]))
{
CGPoint p1 = [ffMapView convertCoordinate:[[P objectForKey:@"point"] coordinate] toPointToView:self.view];
CGPoint p2 = [ffMapView convertCoordinate:[[dictEntry objectForKey:@"point"] coordinate] toPointToView:self.view];
float dX = p1.x - p2.x;
float dY = p1.y - p2.y;
if (sqrt(pow(dX,2)+pow(dY,2)) < eps)
{
[N addObject:dictEntry];
}
}
}
return (NSSet *)N;
}
多数の保持は、regionQueryForPointがexpandDbScanClusterWithPointメソッドから呼び出されたときに発生するように見えるため、完全を期すためにここに含めました。
- (void)expandDbscanClusterWithPoint:(NSMutableDictionary *)P andRegion:(NSMutableSet *)N
andCluster:(LocationGroupAnnotation *)cluster
andEpsilon:(float)eps
andMinPts:(int)minPts
fromList:(NSMutableSet *)D
{
[cluster addAnnotation:(PostLocationAnnotation *)[P objectForKey:@"point"]];
[P setValue:[NSNumber numberWithBool:YES] forKey:@"clustered"];
BOOL finished = NO;
while (!finished)
{
finished = YES;
for (NSMutableDictionary *nextP in N)
{
if ([nextP objectForKey:@"visited"] == [NSNumber numberWithBool:NO])
{
[nextP setValue:[NSNumber numberWithBool:YES] forKey:@"visited"];
NSSet *nextN = [self regionQueryForPoint:nextP andEpsilon:eps fromList:D];
if ([nextN count] >= minPts)
{
[N unionSet:nextN];
finished = NO;
break;
}
}
if ([nextP objectForKey:@"clustered"] == [NSNumber numberWithBool:NO])
{
[cluster addAnnotation:[nextP objectForKey:@"point"]];
[nextP setValue:[NSNumber numberWithBool:YES] forKey:@"clustered"];
}
}
}
}
私はこれを何年にもわたって分析し、参照を数え、ポインターを監視し、すべてを行ってきましたが、このDセットを安全に解放する方法を理解することはできません。誰かが私が見ていないものを見ることができますか?