简体   繁体   English

IOS NSString 内存泄漏

[英]IOS NSString Memory Leak

Hi I am having trouble figuring out the cause of a memory leak and I'am starting to wonder if maybe its some sort of IOS bug?嗨,我在找出内存泄漏的原因时遇到了麻烦,我开始怀疑它是否是某种 IOS 错误? The leak appears almost randomly and the stack trace for the leak within the debugger tool shows no methods that I have physically coded?泄漏几乎是随机出现的,调试器工具中泄漏的堆栈跟踪显示没有我物理编码的方法? The leaks instrument screen shot is below!泄漏仪器屏幕截图如下! Thanks!谢谢!

仪器截图

Here's the first possible culprit!这是第一个可能的罪魁祸首!

#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

And here's the second possible culprit!这是第二个可能的罪魁祸首!

#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 class 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

The CustomCell class doesn't really have any logic within it.. So here's the .h file instead! 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

Not sure what you're doing here:不确定你在这里做什么:

// 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);
};

But use the weakSelf pattern:但是使用 weakSelf 模式:

Before the block: __weak AzureInteraction* weakSelf = self;块前: __weak AzureInteraction* weakSelf = self;

And then in the block: [weakSelf busy:NO]然后在块中: [weakSelf busy:NO]

Blocks that invoke self will retain self unless the reference is explicitly weak.除非引用显式弱,否则调用self块将保留 self 。

Also, your use of _delegate in a block is equally scary.此外,您在块中使用_delegate也同样可怕。 Though that block likely completes more synchronously than this one.尽管该块可能比这个块更同步地完成。

I'm scrutinizing your blocks because of the stack trace, but it could be another that you don't show here given that it's something about pixel rendition.由于堆栈跟踪,我正在仔细检查您的块,但它可能是另一个您没有在此处显示的块,因为它与像素再现有关。 Regardless, you need to be aware that these blocks you are invoking will retain pointers to the objects within them.无论如何,您需要知道您正在调用的这些块将保留指向其中的对象的指针。

I can't see the problem but I suggest doing a bunch of code cleanup.我看不到问题,但我建议进行大量代码清理。 You have a whole lot of weird stuff going on like this:你有很多像这样奇怪的事情:

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

How can it ever not be nil?它怎么可能不是零?

And this, which does nothing at all:而这个,什么都不做:

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

    } else
    {

    }
};

I would commit to version control (you're using version control right?) and delete/fix all of that stuff.我会致力于版本控制(您正在使用版本控制吗?)并删除/修复所有这些东西。 Then see if the leak is still there.然后看看泄漏是否仍然存在。

If the leak is still there, perhaps start deleting big chunks of code just to find out where the leak is.如果泄漏仍然存在,也许开始删除大块代码只是为了找出泄漏的位置。 Then once you've narrowed the problem down you should be able to find the issue and restore all your deleted code from version control.然后,一旦您缩小了问题的范围,您应该能够找到问题并从版本控制中恢复所有已删除的代码。

It's extremely unlikely to be a bug in iOS.这极不可能是 iOS 中的错误。 The NSString class was created in the early 1980's and hasn't changed much since, any bugs were fixed a long long time ago. NSString 类是在 1980 年代早期创建的,此后没有太大变化,很久以前就修复了任何错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM