Интересная ошибка памяти - NSMutableArray заменяется последовательно - PullRequest
0 голосов
/ 14 февраля 2012

В моем приложении для iPad у меня есть следующая функция:

+ (void)addDot:(Dot *)d {
    if(dots == nil)
        dots = [[NSMutableArray alloc] init];

    NSLog(@"before adding, dots = %@",dots);
    NSLog(@"adding dot %@",d);
    [dots addObject:d];
    NSLog(@"dots is now %@",dots);
}

Обратите внимание, что в результате печати точки координаты x, y разделяются пробелом.

Каждый раз, когда субъект касается экрана, рисуется точка, и вызывается этот метод, который добавляет точку. Обратите внимание, что dots определяется как static NSMutableArray *dots в верхней части класса, в котором находится эта функция. Что-то странное происходит. Каждый элемент массива заменяется. Посмотрите на этот вывод из NSLog в самом начале после нажатия на экран, сделав 2 точки:

before adding, dots = (
)
2012-02-13 23:58:48.159 MoreMost[520:707] adding dot 418.000000 548.000000
2012-02-13 23:58:48.161 MoreMost[520:707] dots is now (
    "418.000000 548.000000"
)
2012-02-13 23:58:48.748 MoreMost[520:707] before adding, dots = (
    "635.000000 410.000000"
)
2012-02-13 23:58:48.749 MoreMost[520:707] adding dot 635.000000 410.000000
2012-02-13 23:58:48.750 MoreMost[520:707] dots is now (
    "635.000000 410.000000",
    "635.000000 410.000000"
)

Видите, как весь массив заменяется входящим элементом? Почему это? И нет, функция не вызывается нигде в коде. Он вызывается только один раз, каждый раз, когда пользователь нажимает на экран, чтобы нарисовать точку в этом месте).

Обратите внимание, что точки больше нигде в программе не используются. Только эта функция .

Вот реализация Dot:

#import "Dot.h"

@implementation Dot
@synthesize tapPosition;
CGRect frame;
UIColor *dotColor;

float radius = 20;

- (id)initWithTapPosition:(CGPoint)pt color:(UIColor *)col {
    if(self = [super init]) {
    tapPosition = pt;
    float topLeftX = pt.x - radius;
    float topLeftY = pt.y - radius;
    if(topLeftX + (radius*2) >= 1024)
        return nil;
    if(topLeftY + (radius*2) >= 728)
        return nil;
    if(topLeftY <= 0 || topLeftX <= 0)
        return nil;

    frame = CGRectMake(topLeftX, topLeftY, radius*2, radius*2);
    dotColor = col;
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone
{
   Dot *dot = [[[self class] allocWithZone:zone] initWithTapPosition:tapPosition color:dotColor];
   return dot;
}

- (CGRect)getFrame {
    return frame;
}

- (UIColor *)getColor {
    return dotColor;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"%f %f",frame.origin.x,frame.origin.y];
}

@end

и заголовок:

    #import <Foundation/Foundation.h>

@interface Dot : NSObject {
@public
    CGPoint tapPosition;
}

@property (nonatomic, assign) CGPoint tapPosition;

- (CGRect)getFrame;
- (id)initWithTapPosition:(CGPoint)pt color:(UIColor *)col;

- (UIColor *)getColor;
@end

Ответы [ 2 ]

1 голос
/ 14 февраля 2012

Переместить

CGRect frame;
UIColor *dotColor;
CGPoint tapPosition;
float radius = 20;

от @implementation до @interface:

@interface Dot : NSObject
{
   CGRect frame;
   // etc.
}
...
@end

То, как вы объявляете их, делает их фактически "глобальными переменными", поэтому все экземпляры Dot будут иметь одинаковое значение. Помещение их в @interface делает их «переменными экземпляра», чтобы каждая точка могла иметь разные значения.


Старый ответ

Обратите внимание, что

[dots addObject:d];

добавляет только ссылку точки d. Если вы измените точку на месте позже, например,

d.x = 123;
d.y = 456;

тогда тот же массив будет видеть то же самое изменение.

Вам нужно добавить копию, например,

[dots addObject:[d copy]];
// note: you need to implement -copyWithZone: with the <NSCopying> protocol
0 голосов
/ 14 февраля 2012

Ваш класс точек должен быть изменен так, чтобы tapPosition не был статической переменной.

.h

@interface Dot : NSObject{
    @public
    CGPoint tapPosition;
}
@property (nonatomic, assign) CGPoint tapPosition;
@end

.m

@implementation Dot
@synthesize tapPosition;
//...
@end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...