簡體   English   中英

IOS NSString 內存泄漏

[英]IOS NSString Memory Leak

嗨,我在找出內存泄漏的原因時遇到了麻煩,我開始懷疑它是否是某種 IOS 錯誤? 泄漏幾乎是隨機出現的,調試器工具中泄漏的堆棧跟蹤顯示沒有我物理編碼的方法? 泄漏儀器屏幕截圖如下! 謝謝!

儀器截圖

這是第一個可能的罪魁禍首!

#import "BreakfastViewController.h"
#import "AppDelegate.h"
#import "CustomCell.h"
#import "Recipe.h"
#import "RecipeAzure.h"
#import "DetailViewController.h"

@interface BreakfastViewController ()

@end

@implementation BreakfastViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];


    // Do any additional setup after loading the view.
    _tv.dataSource = self;
    _tv.delegate = self;
    _ai = [[AzureInteraction alloc] initAzureInteraction];
    _ai.delegate = self;
    _searchBar.delegate = self;

    self.ai.busyUpdate = ^(BOOL busy)
    {
        if (busy)
        {

        } else
        {

        }
    };

    [_ai getBreakfasts:nil];


}

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


-(void)update
{


}
-(BOOL)canBecomeFirstResponder {
    return YES;
}

-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated {
    [self resignFirstResponder];
    [super viewWillDisappear:animated];
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (motion == UIEventSubtypeMotionShake)
    {
        [_ai getBreakfasts:nil];


        NSLog(@"SHAKE");
    }
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSLog(@"breakfast sections   %d",[_ai breakfastCount]);
   NSInteger c = [_ai breakfastCount];
    return c;
}



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
      NSLog(@"breakfast sections   %d",[_ai breakfastCount]);
    RecipeAzure *r = [_ai getBreakfastAtIndex:indexPath];
    CustomCell *c = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    [c.imageView setContentMode:UIViewContentModeScaleAspectFit];
    if(r.img64 != nil)
    {
        NSString *str = r.img64;
        NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:0];

        c.RecipeImage.image = [UIImage imageWithData:data];
    }
    c.RecipeTitle.text = r.title;
    c.IngrediantsLabel.text = r.ingrediants;
    return c;
}

-(void)userFinished:(NSString*)title:(NSString*)ingrediants:(NSString*)method:(NSString*)password:
(NSString*)image
{
    RecipeAzure *createdRecipe = [[RecipeAzure alloc]init];

    createdRecipe.title = title;
    createdRecipe.ingrediants = ingrediants;
    createdRecipe.method = method;
    createdRecipe.password = password;
    createdRecipe.img64 = image;
    createdRecipe.type = @"breakfast";
    NSLog(@"GT HERE");
    [_ai uploadRecipe:createdRecipe];

}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

    if ([[segue identifier] isEqualToString:@"createBreakfast"])
    {

        CreateRecipeViewController *crvc = [segue destinationViewController];


        crvc.delegate = self;
    }
    if ([[segue identifier] isEqualToString:@"detailbreakfast"])
    {

        DetailViewController *dvc = [segue destinationViewController];

        NSIndexPath *ind = [_tv indexPathForSelectedRow];
        NSLog(@"%@",[_ai getBreakfastAtIndex:ind]);
        [dvc setRecipe:[_ai getBreakfastAtIndex:ind]];
        [dvc setDelegate:self];
    }
}
-(void)updateTableview
{
    [_tv reloadData];
}
-(void)setEditChanges:(RecipeAzure *)recipe
{
    NSLog(@"OH MY GOOD %@",recipe.rid);

   [_ai updateRecipe:recipe];
}

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
    NSLog(@"SEARCH BEEN PRESSED");
    [_ai getBreakfasts:searchBar.text];
    [searchBar resignFirstResponder];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar
{
    [_ai getBreakfasts:nil];
    searchBar.text = @"";
    [searchBar resignFirstResponder];
}

@end

這是第二個可能的罪魁禍首!

#import "CreateRecipeViewController.h"
#import "Recipe.h"
#import "AppDelegate.h"
#import "BreakfastViewController.h"

@interface CreateRecipeViewController ()
{
    float textViewY;
    //NSString *uepassword;
}

@end

@implementation CreateRecipeViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _Title.delegate = self;
    _Recipe.delegate = self;
    _Ingrediants.delegate = self;
    _Recipe.text = @"Enter Recipe Here...";

    _FinishedEditing.hidden = YES;
    [self.Image setContentMode:UIViewContentModeScaleAspectFit];

    if(_delegate == nil)
    {
        NSLog(@"WTF");
    }
}

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

- (BOOL)textViewShouldBeginEditing:(UITextView *)textField
{

    return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    textField.text = @"";


}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
    textView.text = @"";
     textViewY = textView.frame.origin.y;

    //If we begin editing on the text field we need to move it up to make sure we can still
    //see it when the keyboard is visible.
    //
    //I am adding an animation to make this look better
    if(textView == _Recipe)
    {
        _Title.hidden = YES;
        _Ingrediants.hidden = YES;
        _Image.hidden = YES;
        _AddImage.hidden = YES;
        _FinishedEditing.hidden = NO;


    [UIView beginAnimations:@"Animate Text Field Up" context:nil];
    [UIView setAnimationDuration:.3];
    [UIView setAnimationBeginsFromCurrentState:YES];

        textViewY = _RecipeView.frame.origin.y;


    _RecipeView.frame = CGRectMake(_RecipeView.frame.origin.x,
                               80 , //this is just a number to put it above the keyboard
                               _RecipeView.frame.size.width,
                               _RecipeView.frame.size.height);

    [UIView commitAnimations];
    }

}

- (void)textFieldDidEndEditing:(UITextField *)textField
{

    [textField resignFirstResponder];



}
- (void)textViewDidEndEditing:(UITextView *)textView
{
    [textView resignFirstResponder];
}




- (IBAction)finishedPressed:(id)sender {

      [_Recipe resignFirstResponder];

           [UIView beginAnimations:@"Animate Text Field Up" context:nil];
        [UIView setAnimationDuration:.3];
        [UIView setAnimationBeginsFromCurrentState:YES];

        _RecipeView.frame = CGRectMake(_RecipeView.frame.origin.x,
                                       textViewY ,
                                       _RecipeView.frame.size.width,
                                       _RecipeView.frame.size.height);

        [UIView commitAnimations];
        _Title.hidden = NO;
        _Ingrediants.hidden = NO;
        _Image.hidden = NO;
        _AddImage.hidden = NO;
}

- (IBAction)takePicture:(id)sender {

    UIImagePickerController *picker = [[UIImagePickerController alloc]init];
    picker.sourceType =  UIImagePickerControllerSourceTypePhotoLibrary;

    picker.allowsEditing = YES;
    picker.delegate = self;

    [self presentViewController:picker animated:YES completion:nil];



}

- (IBAction)resignKeyboard:(id)sender {

    [sender resignFirstResponder];
}
- (IBAction)saveButton:(id)sender {
    NSLog(@"HELLLOOO");



    UIAlertView * alert =[[UIAlertView alloc ] initWithTitle:@"Password" message:@"Enter a password to be able to edit this recipe in the future!" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles: nil];
    alert.alertViewStyle = UIAlertViewStyleSecureTextInput;
    [alert addButtonWithTitle:@"Enter"];
    [alert show];





   // uepassword = nil;

    _FinishedEditing.hidden = true;
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1)
    {
        UITextField *password = [alertView textFieldAtIndex:0];
        //uepassword = password.text;

        //self.createdRecipe.title = _Title.text;
        //self.createdRecipe.ingrediants = _Ingrediants.text;
        //self.createdRecipe.method = _Recipe.text;
        //self.createdRecipe.img64 = _imagestring;
        //NSString *imageString;
        //if(_Image.image != nil)
        //{
          //  NSData *data = UIImagePNGRepresentation(_Image.image);
            //imageString = [data base64EncodedStringWithOptions:0];
        //}

       // [self.delegate userFinished:_Title.text :_Ingrediants.text :_Recipe.text :@"password" :imageString];

        [self performSegueWithIdentifier:@"back" sender:self];



    }
}

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{

    UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
    self.Image.image = image;





    [self dismissViewControllerAnimated:YES completion:nil];
}



@end

AzureInteraction 類

#import "AzureInteraction.h"
#import "Recipe.h"
#import "RecipeAzure.h"


@implementation AzureInteraction
{

}




-(id)initAzureInteraction
{
    self = [super init];

    if(self)
    {





        self.client = [client clientWithFilter:self];
         self.table = [_client tableWithName:@"Recipe"];

         self.busyCount = 0;





        return self;

    }
    return nil;
}



-(NSMutableArray*)getBreakfasts:(NSString*)pred
{
    NSLog(@"yeah its doing this but ");

    NSPredicate *bpred;
   if(pred == nil)
   {

     bpred = [NSPredicate predicateWithFormat:@"type == 'breakfast'"];
   }
   else{

        bpred = [NSPredicate predicateWithFormat:@"type == 'breakfast' AND title == %@",pred];
   }


    [_table readWithPredicate:bpred completion:^(NSArray *items, NSInteger totalCount, NSError *error) {


        _breakfasts = [items mutableCopy];


        [_delegate updateTableview];

    }];

    return _breakfasts;





}
-(NSMutableArray*)getLunches:(NSString*)pred
{
    NSLog(@"yeah its doing this but ");
    NSLog(@"%@",_recipes);

    NSPredicate *bpred;
    if(pred == nil)
    {
    bpred = [NSPredicate predicateWithFormat:@"type == 'lunch'"];
    }
    else{
        bpred = [NSPredicate predicateWithFormat:@"type == 'lunch' AND title == %@",pred];
    }


    [_table readWithPredicate:bpred completion:^(NSArray *items, NSInteger totalCount, NSError *error) {


        _lunches = [items mutableCopy];


        [_delegate updateTableview];

    }];

    return _lunches;


}
-(NSMutableArray*)getDinners:(NSString*)pred
{
    NSLog(@"yeah its doing this but ");
    NSLog(@"%@",_recipes);
    NSPredicate *bpred;
    if(pred == nil)
    {
     bpred = [NSPredicate predicateWithFormat:@"type == 'dinner'"];
    }
    else{
        bpred = [NSPredicate predicateWithFormat:@"type == 'dinner' AND title == %@",pred];
    }


    [_table readWithPredicate:bpred completion:^(NSArray *items, NSInteger totalCount, NSError *error) {


        _dinners = [items mutableCopy];


        [_delegate updateTableview];

    }];

    return _dinners;


}
-(void)updateTable
{






}
- (void)busy:(BOOL)busy
{
    // assumes always executes on UI thread
    if (busy)
    {
        if (self.busyCount == 0 && self.busyUpdate != nil)
        {
            self.busyUpdate(YES);
        }
        self.busyCount ++;
    }
    else
    {
        if (self.busyCount == 1 && self.busyUpdate != nil)
        {
            self.busyUpdate(FALSE);
        }
        self.busyCount--;
    }
}

- (void)handleRequest:(NSURLRequest *)request
                 next:(MSFilterNextBlock)next
             response:(MSFilterResponseBlock)response
{
    // A wrapped response block that decrements the busy counter
    MSFilterResponseBlock wrappedResponse = ^(NSHTTPURLResponse *innerResponse, NSData *data, NSError *error)
    {
        [self busy:NO];
        NSLog(@"OK");

        response(innerResponse, data, error);
    };

    // Increment the busy counter before sending the request
    [self busy:YES];
    NSLog(@"HMM");
    next(request, wrappedResponse);
}

-(RecipeAzure*)getBreakfastAtIndex:(NSIndexPath*)indexPath
{
    NSDictionary *d = [_breakfasts objectAtIndex:indexPath.row];

    RecipeAzure* t = [[RecipeAzure alloc] init];

    t.rid = [d valueForKey:@"id"];

    t.title = [d valueForKey:@"title"];
    t.ingrediants = [d valueForKey:@"ingrediants"];
    t.method = [d valueForKey:@"method"];
    t.type = [d valueForKey:@"type"];
    t.img64 = [d valueForKey:@"imageencoded"];
    t.password = [d valueForKey:@"password"];


    return t;
}
-(RecipeAzure*)getLunchAtIndex:(NSIndexPath*)indexPath
{
    NSDictionary *d = [_lunches objectAtIndex:indexPath.row];

    RecipeAzure* t = [[RecipeAzure alloc] init];
    t.rid = [d valueForKey:@"id"];
    t.title = [d valueForKey:@"title"];
    t.ingrediants = [d valueForKey:@"ingrediants"];
    t.method = [d valueForKey:@"method"];
    t.img64 = [ d valueForKey:@"imageencoded"];
    t.password = [d valueForKey:@"password"];

    return t;
}
-(RecipeAzure*)getDinnerAtIndex:(NSIndexPath*)indexPath
{
    NSDictionary *d = [_dinners objectAtIndex:indexPath.row];

    RecipeAzure* t = [[RecipeAzure alloc] init];
    t.rid = [d valueForKey:@"id"];
    t.title = [d valueForKey:@"title"];
    t.ingrediants = [d valueForKey:@"ingrediants"];
    t.method = [d valueForKey:@"method"];
    t.img64 = [ d valueForKey:@"imageencoded"];
    t.password = [d valueForKey:@"password"];

    return t;
}
-(NSInteger)breakfastCount
{
    NSLog(@"COUNTING");
    return [_breakfasts count];
}
-(NSInteger)lunchCount
{
    return [_lunches count];
}
-(NSInteger)dinnerCount
{
    return [_dinners count];
}
-(void)uploadRecipe:(RecipeAzure*)recipe
{
    NSDictionary *createdDictionary;
    if(recipe.img64 != nil)
    {
   createdDictionary = @{@"imageencoded": recipe.img64,@"ingrediants":recipe.ingrediants,
                               @"method":recipe.method,@"password":recipe.password,@"title":recipe.title,@"type":recipe.type};
   }
    else{
        createdDictionary = @{@"imageencoded": @"no image",@"ingrediants":recipe.ingrediants,@"method":recipe.method,@"password":recipe.password,@"title":recipe.title,@"type":recipe.type};
    }

    [_table insert:createdDictionary completion:^(NSDictionary *insertedItem, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error);
        } else {
            NSLog(@"Item inserted, id: %@", [insertedItem objectForKey:@"id"]);
        }
    }];

}

-(void)updateRecipe:(RecipeAzure*)r
{
    NSLog(@"BEFORE AN EXCEPTION");
    NSLog(r.rid);
  NSDictionary *createdDictionary = @{@"id":r.rid,@"imageencoded": r.img64,@"ingrediants":r.ingrediants,
                          @"method":r.method,@"password":r.password,@"title":r.title,@"type":r.type};
    [_table update:createdDictionary completion:^(NSDictionary *item, NSError *error) {
        if(error)
        {
            NSLog(@"%@",error);
        }
    }];
}




@end

CustomCell 類實際上並沒有任何邏輯。所以這里是 .h 文件!

#import <UIKit/UIKit.h>

@interface CustomCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UILabel *RecipeTitle;
@property (weak, nonatomic) IBOutlet UILabel *IngrediantsLabel;
@property (weak, nonatomic) IBOutlet UIImageView *RecipeImage;

@end

不確定你在這里做什么:

// A wrapped response block that decrements the busy counter
MSFilterResponseBlock wrappedResponse = ^(NSHTTPURLResponse *innerResponse, NSData *data, NSError *error)
{
    [self busy:NO];
    NSLog(@"OK");

    response(innerResponse, data, error);
};

但是使用 weakSelf 模式:

塊前: __weak AzureInteraction* weakSelf = self;

然后在塊中: [weakSelf busy:NO]

除非引用顯式弱,否則調用self塊將保留 self 。

此外,您在塊中使用_delegate也同樣可怕。 盡管該塊可能比這個塊更同步地完成。

由於堆棧跟蹤,我正在仔細檢查您的塊,但它可能是另一個您沒有在此處顯示的塊,因為它與像素再現有關。 無論如何,您需要知道您正在調用的這些塊將保留指向其中的對象的指針。

我看不到問題,但我建議進行大量代碼清理。 你有很多像這樣奇怪的事情:

NSPredicate *bpred;
if(pred == nil)
{
  bpred = [NSPredicate predicateWithFormat:@"type == 'lunch'"];
}
else{
  bpred = [NSPredicate predicateWithFormat:@"type == 'lunch' AND title == %@",pred];
}

它怎么可能不是零?

而這個,什么都不做:

self.ai.busyUpdate = ^(BOOL busy)
{
    if (busy)
    {

    } else
    {

    }
};

我會致力於版本控制(您正在使用版本控制嗎?)並刪除/修復所有這些東西。 然后看看泄漏是否仍然存在。

如果泄漏仍然存在,也許開始刪除大塊代碼只是為了找出泄漏的位置。 然后,一旦您縮小了問題的范圍,您應該能夠找到問題並從版本控制中恢復所有已刪除的代碼。

這極不可能是 iOS 中的錯誤。 NSString 類是在 1980 年代早期創建的,此后沒有太大變化,很久以前就修復了任何錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM