【问题标题】:In trouble with NSMutableArray retentionNSMutableArray 保留有问题
【发布时间】:2011-11-04 16:33:08
【问题描述】:

我已经有这个问题好几天了,我用谷歌搜索了很多,但没有发现任何有用的东西,所以我想在这里问。 我有一个 NSMutableArray 的保留问题,但是我不明白问题出在哪里以及如何解决它。 我正在使用 Quartz 2D 绘制折线图,​​为此我将 UIView 子类化为 GraphView。标题如下所示:

@interface GraphView : UIView {
    NSMutableArray *data;
}

- (void)drawBar:(CGRect)rect context:(CGContextRef)ctx;
- (void)drawLineGraphWithContext:(CGContextRef)ctx;
- (void)addValue:(float)value;

@property(nonatomic, retain) NSMutableArray *data;

@end

我使用“数据”数组来存储要绘制的数据点,在 drawLineGraphWithContext: 方法中。另一方面,addValue: 方法在主控制器中用于填充数据数组。在我做的控制器的viewDidLoad中,填写数据数组:

graphView = [[GraphView alloc] init];

float data[] = {0.7, 0.4, 0.9, 1.0, 0.2, 0.85, 0.11, 0.75, 0.53, 0.44, 0.88, 0.77, 0.99, 0.55};

for(int i = 0; i < 14; i++)
{
    [graphView addValue:data[i]];
}

此时一切都很好,在调试中我可以看到数组中填充了所有元素。问题是在调用 drawLineGraphWithContext: 方法时;出于某种原因,数组突然变空,其保留计数为 0。我尝试更改访问对象的方式,使用点表示法或省略 self,但行为看起来相同。奇怪的是,我尝试过识别僵尸(使用 GDB 和 Instruments),但似乎没有找到。事实上,在drawLineGraphWithContext:下面的主循环开始了数据数组有零个元素,并且没有绘制任何内容。下面是完整的文件(我省略了不相关的部分)。

我正在使用带有自动引用计数的 Xcode 4.2。

提前感谢任何提供帮助的人!

GraphView.h:

@interface GraphView : UIView {
    NSMutableArray *data;
}

- (void)drawBar:(CGRect)rect context:(CGContextRef)ctx;
- (void)drawLineGraphWithContext:(CGContextRef)ctx;
- (void)addValue:(float)value;

@property(nonatomic, retain) NSMutableArray *data;

@end

GraphView.m:

#import "GraphView.h"

@implementation GraphView
@synthesize data;

- (id) init
{
    self = [super init];
    if(self) {
        data = [NSMutableArray array];
        NSLog(@"%s", __FUNCTION__);
    }
    return self;
}

- (void)addValue:(float)value
{
    NSMutableArray *tempArr = [self data];
    NSNumber *val = [NSNumber numberWithFloat:value];
    [tempArr addObject:val];
    [self setData:tempArr];
}

- (void)drawLineGraphWithContext:(CGContextRef)ctx
{
    CGContextSetLineWidth(ctx, 2.0);
    CGContextSetStrokeColorWithColor(ctx, [[UIColor colorWithRed:1.0 green:0.5 blue:0 alpha:1] CGColor]);

    int maxGraphHeight = kGraphHeight - kOffsetY;

    CGContextBeginPath(ctx);

    CGContextMoveToPoint(ctx, kOffsetX, kGraphHeight - maxGraphHeight * [[[self data] objectAtIndex:0] floatValue]);

    int i = 0;
    NSMutableArray *arr = [self data];

    for(NSNumber *elem in arr)
    {
        float val = [elem floatValue];
        CGContextAddLineToPoint(ctx, kOffsetX + i * kStepX, kGraphHeight - maxGraphHeight * val);
       i++;
    }

    CGContextDrawPath(ctx, kCGPathStroke);    

}

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetLineWidth(context, 0.6);
    CGContextSetStrokeColorWithColor(context, [[UIColor lightGrayColor] CGColor]);

    CGFloat dash[] = {2.0, 2.0};
    CGContextSetLineDash(context, 0.0, dash, 2);

    int lines = (kDefaultGraphWidth - kOffsetX)/kStepX;
    for(int i = 0; i < lines; i++)
    {
        CGContextMoveToPoint(context, kOffsetX + i * kStepX, kGraphTop);
        CGContextAddLineToPoint(context, kOffsetX + i * kStepX, kGraphBottom);
    }

    int horizontalLines = (kDefaultGraphWidth-kOffsetY)/kStepY;
    for(int i = 0; i < horizontalLines; i++)
    {
        CGContextMoveToPoint(context, kOffsetX, kGraphBottom - kOffsetY - i * kStepY);
        CGContextAddLineToPoint(context, kDefaultGraphWidth, kGraphBottom - kOffsetY -i * kStepY);
    }
    CGContextStrokePath(context);
    CGContextSetLineDash(context, 0, NULL, 0);

    [self drawLineGraphWithContext:context];
}

@end

GRViewController.m:

#import "GRViewController.h"

@implementation GRViewController
@synthesize scroller;
@synthesize graphView;

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
scroller.contentSize = CGSizeMake(kDefaultGraphWidth, kGraphHeight);
    graphView = [[GraphView alloc] init];

    float data[] = {0.7, 0.4, 0.9, 1.0, 0.2, 0.85, 0.11, 0.75, 0.53, 0.44, 0.88, 0.77, 0.99, 0.55};

    for(int i = 0; i < 14; i++)
    {
        [graphView addValue:data[i]];
    }
}

@结束

GRViewController.h:

#import <UIKit/UIKit.h>
#import "GraphView.h"

@interface GRViewController : UIViewController
@property (retain, nonatomic) IBOutlet UIScrollView *scroller;
@property (retain, nonatomic) IBOutlet GraphView *graphView;

@end

【问题讨论】:

    标签: iphone objective-c nsmutablearray xcode4.2


    【解决方案1】:

    听起来您有两个不同的 GraphView 实例——一个是您使用 [[GraphView alloc] init] 在该方法中创建的,另一个是实际显示的实例(可能在 nib 中)。获取数据的 GraphView 不显示,显示的 GraphView 从不获取任何数据。您可以通过在相关的 GraphView 方法中执行 NSLog(@"I am %@", self) 来确认是这种情况。您将看到两个不同的对象报告。

    【讨论】:

    • 谢谢。我尝试在 drawLineGraphWithContext: 方法中插入该行,但是该行仅在 GDB 控制台中出现一次。
    • @Antonio:这是我们怀疑在另一个实例上调用的方法,所以这是有道理的。尝试登录addValue: 看看self 是否与drawLineGraphWithContext: 中的地址相同。
    • 我找到了解决这个问题的方法,但我非常想了解为什么这个解决方案有效,以及这是否是正确的解决方案。我设法通过简单地删除它在 GraphView.h 中的声明并在 GraphView.m 中本地声明它来保留该数组。并没有真正做任何其他事情,所以 addValue: 和其他方法的主体相同。呸,我的问题是:为什么会这样?或者更好的是,为什么在标头中声明数组会导致问题?你们那里有愿意得到这个的大师吗? :-) 无论如何,谢谢所有回复的人。
    • @Antonio:我假设您实际上并不是指“在本地声明它”,就像在方法中的局部变量中那样。我怀疑您实际上所做的是在 GraphView.m 内(即在任何方法之外)globally 声明它。 “工作”的原因是因为类的所有实例共享相同的变量,而在每个实例之前都有自己的数组变量。这种解决方案通常不受欢迎,因为它是一个更脆弱的设计 - 在这种情况下它绝对错误,因为它没有解决根本问题,即你有两个不同的对象。
    • 我明白了,但是你有什么建议呢?
    【解决方案2】:

    在您的 init 方法中使用...

    self.data = [NSMutableArray array];
    

    或者...

    data = [[NSMutableArray alloc] init];
    

    如果不使用“self”,您将无法通过该属性。因此不保留数组。

    【讨论】:

    • 感谢您的回复,但这似乎不是问题所在。我都试过了(实际上,第二个是我的第一选择),但没有任何帮助
    • 您可能还有其他问题,但@simon 是正确的。除非您的项目使用 ARC,但这是另一个主题。
    • 我的项目正在使用 ARC,我在原始帖子中提到了这一点。谢谢。
    • data = [NSMutableArray array]; 在 ARC 中完全没问题。这个答案具有误导性。
    • 我找到了解决这个问题的方法,但我非常想了解为什么这个解决方案有效,以及这是否是正确的解决方案。我设法通过简单地删除它在 GraphView.h 中的声明并在 GraphView.m 中本地声明它来保留该数组。并没有真正做任何其他事情,所以 addValue: 和其他方法的主体相同。呸,我的问题是:为什么会这样?或者更好的是,为什么在标头中声明数组会导致问题?你们那里有愿意得到这个的大师吗? :-) 无论如何,谢谢所有回复的人。
    【解决方案3】:

    试试这个。

    - (void)addValue:(float)value
    {
        NSNumber *val = [NSNumber numberWithFloat:value];
        [data addObject:val];
    }
    

    您可以只使用数据 ivar,而不必担心获取或设置属性。这可能会解决它,因为每次调用 addValue 时它都不会使用您的 getter 和 setter。

    【讨论】:

    • 谢谢,但不幸的是,事实并非如此。
    • 我找到了解决这个问题的方法,但我非常想了解为什么这个解决方案有效,以及这是否是正确的解决方案。我设法通过简单地删除它在 GraphView.h 中的声明并在 GraphView.m 中本地声明它来保留该数组。并没有真正做任何其他事情,所以 addValue: 和其他方法的主体相同。呸,我的问题是:为什么会这样?或者更好的是,为什么在标头中声明数组会导致问题?你们那里有愿意得到这个的大师吗? :-) 无论如何,谢谢所有回复的人。
    猜你喜欢
    • 2011-10-30
    • 2010-11-02
    • 1970-01-01
    • 2012-03-01
    • 2011-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多