5

モーダルで表示されるUIViewControllerがあります。メモリ割り当てインストルメントを見ると、ビューが表示されたときにメモリ使用量が増加しますが、ビューが終了したときにメモリが解放されません。ビューを開いたり閉じたりし続けると、メモリが増え続けます。Instrumentsはメモリリークを報告しません!これを引き起こしている可能性がありますか?View Controllerコードは以下のとおりです(didSelectRowコードはスキップしました)。Deallocは常に呼び出されます。

編集-私はARCを使用しています

.h

#import <UIKit/UIKit.h>
@class OutlineTextUILabel;

@interface StoreViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {

    int starCount;
    NSMutableArray *_singleUseArray;
    NSMutableArray *_fullUseArray;

}

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet OutlineTextUILabel *starCountLbl;
- (IBAction)exitBtnPressed:(id)sender;

.m

#import "StoreViewController.h"
#import "NSUserDefaults+MPSecureUserDefaults.h"
#import "PowerUpCell.h"
#import "OutlineTextUILabel.h"
#import "PowerUpSingleton.h"
#import "PowerUp.h"

#define kPrefsNumberOfStars             @"numberOfStars"

@interface StoreViewController ()

@end

@implementation StoreViewController
@synthesize tableView = _tableView;
@synthesize starCountLbl;

#pragma mark View Methods

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Display star count
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    BOOL valid = NO;
    starCount = [prefs secureIntegerForKey:kPrefsNumberOfStars valid:&valid];
    if (!valid) {
        NSLog(@"Stars Tampered With!");
        self.starCountLbl.text = @"Err";
    } else {
        self.starCountLbl.text = [NSString stringWithFormat:@"%d",starCount];
    }

    // Tableview setup
    CGRect frame2 = CGRectMake(0, 0, 320, 40);
    UIView *footer = [[UIView alloc] initWithFrame:frame2];
    footer.backgroundColor = [UIColor clearColor];
    self.tableView.tableFooterView = footer;
    self.tableView.opaque = NO;
    self.tableView.backgroundView = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:YES];

    if (![[PowerUpSingleton sharedList] refreshArray]) {
        NSLog(@"Error, %s",__FUNCTION__);
    } else {
        [self performSelectorOnMainThread:@selector(workOutSingleUseToDisplay) withObject:nil waitUntilDone:YES];
        [self performSelectorOnMainThread:@selector(workOutFullUseToDisplay) withObject:nil waitUntilDone:YES];
        [self.tableView reloadData];
    }
}

- (void)workOutSingleUseToDisplay
{
    _singleUseArray = [[NSMutableArray alloc] init];
    for (PowerUp *pu in [[PowerUpSingleton sharedList] sharedArray]) {
        if (!pu.fullUnlock) {
            [_singleUseArray addObject:pu];
        }
    }
}

- (void)workOutFullUseToDisplay
{
    _fullUseArray = [[NSMutableArray alloc] init];
    for (PowerUp *pu in [[PowerUpSingleton sharedList] sharedArray]) {
        if (pu.prefFullName != nil) {
            [_fullUseArray addObject:pu];
        }
    }

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}

- (void)viewDidUnload {
    [self setTableView:nil];
    [self setStarCountLbl:nil];
    [super viewDidUnload];
}

#pragma mark TableView Setup Methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 2;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (section == 0) {
        return @"Single Use";
    } else if (section == 1) {
        return @"Use forever";
    }

    return nil;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section == 0) {
        return [_singleUseArray count];
    } else if (section == 1) {
        return [_fullUseArray count];
    }

    return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier;
    if (indexPath.section == 0) {
        cellIdentifier = @"powerUpCellSingleUse";
    } else if (indexPath.section == 1) {
        cellIdentifier = @"powerUpCell";
    }

    PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil) {
        cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    }

    if (indexPath.section == 0) {
        PowerUp *tmpPU = [_singleUseArray objectAtIndex:indexPath.row];
        cell.descriptionLbl.text = tmpPU.displayName;
        int cost = tmpPU.costSingle;
        cell.costLbl.text = [NSString stringWithFormat:@"%d",cost];
        if (cost > starCount) {
            cell.costLbl.textColor = [UIColor redColor];
        } else {
            cell.costLbl.textColor = [UIColor blueColor];
        }
        int howMany = tmpPU.numberOwned;
        cell.howManyLbl.text = [NSString stringWithFormat:@"%d",howMany];

    } else if (indexPath.section == 1) {
        PowerUp *tmpPU = [_fullUseArray objectAtIndex:indexPath.row];
        cell.descriptionLbl.text = tmpPU.displayName;
        int cost = tmpPU.costFull;
        cell.costLbl.text = [NSString stringWithFormat:@"%d",cost];
        if (cost > starCount) {
            cell.costLbl.textColor = [UIColor redColor];
        } else {
            cell.costLbl.textColor = [UIColor blueColor];
        }
        if (tmpPU.fullUnlock) {
            cell.costLbl.textColor = [UIColor greenColor];
            cell.costLbl.text = @"---";
        }
    }

    return cell;
}

#pragma mark -

- (IBAction)exitBtnPressed:(id)sender
{
    [self dismissModalViewControllerAnimated:YES];
}

- (void)dealloc
{
    NSLog(@"%s",__FUNCTION__);
    self.tableView = nil;
    self.starCountLbl = nil;
}

@end

編集-------------何かが正しくないようです。セル割り当てにNSLogを追加しましたが、セルが作成されても呼び出されません。

PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil) {
        NSLog(@"new cell");
        cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    }

7月1日編集------ナビゲーションコントローラーを追加し、モーダルの代わりにプッシュを使用するようになりましたが、この問題はまだ発生しています。ビュー間を数回前後に移動してInstrumentsでヒープショットを撮りましたが、このスクリーンショットは以前のビューのロードからのジェスチャ認識機能を示しているため、セルがまだぶら下がっているようです。 スクリーンショット

4

5 に答える 5

3

これは、IBOutletsを。weakではなくとして使用したためですstrong

この種の動作を警告する必要があるため、これはXCode環境の欠陥であると実際に信じています。

ベストプラクティスとして、このような厄介な落とし穴を避けるために、ビューをInterfaceBuilderのコードにドラッグしてXCodeにIBOutletsを生成させることをお勧めします。

于 2012-07-02T09:08:52.443 に答える
2

あなたはすでにこれを回避するいくつかの方法を見つけたようですが、これが役立つ場合に備えて:

1)デバッグ中にゾンビがオンになっていないことを確認します。これにより、オブジェクトの割り当てを解除する必要があると考えた後、オブジェクトがハングします([スキームの編集]->[実行]->[診断])。

2)ARCを使用しているので、ストーリーボードまたは少なくともストーリーボード/ NIBのUITableViewセルのプロトタイプを想定していますか?その場合、以下のNSLog()が呼び出されない理由は、dequeueReusableCellWithIdentifier呼び出しが、定義されたcellIdentifierを介してこれらのプロトタイプセルからセルを作成することを認識しているためです。かなり便利です。

PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (cell == nil) {
        NSLog(@"new cell");
        cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    }

UITableViewCellsのこのキャッシュを管理し、それらを適切に解放するには、UITableViewに依存する必要があります。したがって、UITableViewがリリースされていないために、それらがただぶらぶらしている可能性があります(リリースされていると言っていると思いますが)。

于 2012-07-03T21:40:47.407 に答える
0

[編集]

viewWillAppearメソッドで、else句を移動する頻度を確認するために印刷しましたか。私には、workOutSingleUseToDisplayメソッドとworkOutFullUseToDisplayメソッドを呼び出しているようです。それらを呼び出すたびに、_singleUseArrayと_fullUseArrayを割り当てます。ビューに出入りするからといって、それがdeallocを呼び出すことや、現在の配列を自動解放することを意味するわけではありません。私があなたが見ていると思うのは、ビューの外に移動すると、それらの2つの配列は解放されませんが、それらを再割り当てしようとするということです。

[オリジナル] そうですね、viewDidLoadで、割り当てを実行します。あなたのdeallocには、[フッターリリース]が表示されません。これはあなたのリークかもしれません!!! また、_singleUseArrayまたは_fullUseArray配列のリリースも表示されません

于 2012-06-26T14:42:19.637 に答える
0

私が答えを得たかどうかはわかりませんが、あなたのコードに何か奇妙なことがあります:

あなたは弱いプロパティを使用しています:

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet OutlineTextUILabel *starCountLbl;

しかし、ドキュメント(「弱い」を検索)によると、weakプロパティはに非常に似ていassignます。

あなたのdeallocでは、あなたは

self.tableView = nil;
self.starCountLbl = nil;

これらのプロパティの生成されたセッターは、それらをまったく解放しないと確信しています。

ただし、次のようにプロパティを宣言すると、次のようになります。

@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet OutlineTextUILabel *starCountLbl;

生成されたセッターは次のようになります

(void)setTableView(UITableView *)newTableView {
    [tableView release];
    if(newTableView != nil)
        tableView = [newTableView retain];
}

そして、あなたのプロパティは解放されます。

于 2012-06-26T14:42:45.023 に答える
0

少なくとも、リーク機器を使用してメモリリークを監視します。Allocationsインスツルメントは、実際にはメモリリークを表示しません。Analyzeを実行すると、リークの原因となっている可能性のある行が表示されます。

これはあなたのコードです:

 PowerUpCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
 if (cell == nil) {
    NSLog(@"new cell");
    cell = [[PowerUpCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
 }

ほら、cellそうなることはないでしょうnil...これは次のAPIドキュメントに記載されていますdequeueReusableCellWithIdentifier:

戻り値

関連する識別子を持つUITableViewCellオブジェクト。または、そのようなオブジェクトが再利用可能セルキューに存在しない場合はnil。

とにかく、リークがある場合、おそらくそれらは非常に原因です:

_singleUseArray = [[NSMutableArray alloc] init];

_fullUseArray = [[NSMutableArray alloc] init];

宣言したとき

NSMutableArray *_singleUseArray;
NSMutableArray *_fullUseArray;

__strongデフォルトでは、両方に修飾子が割り当てられていると思います。よくわかりませんが、これが問題の本当の原因である可能性があります。代わりにこれを宣言するのはどうですか?

NSMutableArray * __weak _singleUseArray;
NSMutableArray * __weak _fullUseArray;

また、宣言する前に

_singleUseArray = [[NSMutableArray alloc] init];

_fullUseArray = [[NSMutableArray alloc] init];

nil以前の参照を削除するために最初にそれを割り当てるのはどうですか?

_singleUseArray = nil;
_singleUseArray = [[NSMutableArray alloc] init];

_fulUseArray = nil;
_fullUseArray = [[NSMutableArray alloc] init];
于 2012-06-29T05:59:53.823 に答える