【问题标题】:Objective-C Memory management: ViewController in a ViewController that's inside a UITabBarControllerObjective-C 内存管理:UITabBarController 内的 ViewController 中的 ViewController
【发布时间】:2011-10-02 10:25:44
【问题描述】:

我有一个带有三个 ViewController(A、B 和 C)的 UITabBarController。在 ViewControllerB 我有一个 UIScrollView。 UIScrollView 由我的 PhotoViewController 的几个实例组成。这些 PhotoViewController 对象是从 ViewControllerA 调用的,而不是从它们所在的 ViewController B 调用的。

PhotoViewController 实例有一个 UIImage 和一个两个按钮。起初,当我单击其中一个 PhotoViewController 实例中的按钮时,我收到了“线程 1:程序接收信号:“EXC_BAD_ACCESS”错误。在stackoverflow上环顾四周,只要出现内存管理问题,似乎就会出现错误。

由于我是通过在 ViewControllerA 中调用的方法在循环中创建 PhotoViewController 对象并释放这些对象,我认为当我切换到 ViewControllerB 时它们已经被释放 - 因此存在内存问题。

但这只是我的猜测。你能告诉我是否应该停止在循环代码中释放 PhotoViewController 对象吗?因为这就是我所做的(只是评论了那条线)并且程序“运行良好”。但是,我仍然不确定这是否是处理它的正确方法以及它是否会导致未知的内存管理问题。

这是我的一些代码:

ViewControllerA.m

//在ViewControllerB中创建相册,相册中的照片是PhotoViewController对象

-(IBAction)showAlbum:(UIButton *)sender
{
    //Go goes here to get an album and display it in the UIScrollView
    albumID = @"ALBUM_ID";
    NSString* graphUrl = [NSString stringWithFormat:@"%@/photos?limit=10", albumID];
    [_facebook requestWithGraphPath:graphUrl andDelegate:self];    
}

...

- (void)request:(FBRequest *)request didLoad:(id)result {

    //Code for array of photos
    NSLog(@"%@",result);
    NSString *requestType = [request.url stringByReplacingOccurrencesOfString:@"https://graph.facebook.com/" withString:@""];

    if ([requestType isEqualToString:[NSString stringWithFormat:@"%@/photos?limit=10", albumID]]){
        NSArray *photoAlbumArray=(NSArray*)[result valueForKey:@"data"];
        [self.label setText:[NSString stringWithFormat:@"%i", [photoAlbumArray count]]];
        for(UIViewController *controller in self.tabBarController.viewControllers)
        {
            if([controller isKindOfClass:[ViewControllerB class]])
            {
                ViewControllerB *mtbvc = (ViewControllerB *)controller;
                [mtbvc setArray:photoAlbumArray];
                self.tabBarController.selectedIndex = 1;//switch over to the second view to see if it worked
            }
        }
    }
...
@end

ViewControllerB.m

//我创建 PhotoViewController 对象的循环

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:YES];
    arrayCount = [array count];
    scroller.delegate=self;
    scroller.pagingEnabled=YES;
    scroller.directionalLockEnabled=YES;
    scroller.showsHorizontalScrollIndicator=NO;
    scroller.showsVerticalScrollIndicator=NO;

    //should have an array of photo objects and the number of objects, correct?
    scrollWidth = 0;
    scroller.contentSize=CGSizeMake(arrayCount*scroller.frame.size.width, scroller.frame.size.height);

    for (int i = 0; i < arrayCount;i++) {
        PhotoViewController *pvc = [[PhotoViewController alloc] initWithNibName:@"PhotoViewController" bundle:nil];        
        UIImageView *scrollImageView = [[UIImageView alloc] initWithFrame:CGRectOffset(scroller.bounds, scrollWidth, 0)];
        CGRect rect = scrollImageView.frame;
        pvc.view.frame  = rect;
        [pvc view];
        pvc.label.textColor = [UIColor whiteColor];
        id individualPhoto = [array objectAtIndex:i];
        NSLog(@"%@",individualPhoto);
        NSArray *keys=[individualPhoto allKeys];
        NSLog(@"%@",keys);
        NSString *imageURL=[individualPhoto objectForKey:@"source"];
        //here you can use this imageURL to get image-data and display it in imageView  
        NSURL *url = [NSURL URLWithString:imageURL];
        NSData  *data = [NSData dataWithContentsOfURL:url];
        pvc.imageView.image = [[UIImage alloc] initWithData:data];
        pvc.label.text = [individualPhoto objectForKey:@"id"];
        //check to make sure the proper URL was passed
        //I have an imageView next to the UIScrollView to test whether that works - it does.
        [scroller addSubview:pvc.view];
        [scrollImageView release];

        //[pvc release];

        scrollWidth += scroller.frame.size.width;
    }


    if (arrayCount > 3) {
        pageControl.numberOfPages=3;
    } else {
    pageControl.numberOfPages=arrayCount;
    }
    pageControl.currentPage=0;
    //[self.view addSubview:scroller];
}

PhotoViewController.m

#import "PhotoViewController.h"


@implementation PhotoViewController
@synthesize label, imageView;

-(IBAction)likeButton:(UIButton *)sender
{
    //code goes here 
    for(UIViewController *controller in self.tabBarController.viewControllers)
    {
        if([controller isKindOfClass:[DemoAppViewController class]])
        {
            DemoAppViewController *davc = (DemoAppViewController *)controller;
            [davc likePicture:self.label.text];
        }
    }
    self.tabBarController.selectedIndex = 0;//switch over to the third view to see if it worked
}

-(IBAction)skipButton:(UIButton *)sender
{
    //code goes here
}

-(IBAction)likeCommentButton:(UIButton *)sender
{
    //code goes here
}

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

- (void)dealloc
{
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

@end

PhotoViewController.h

#import <UIKit/UIKit.h>
#import "DemoAppViewController.h"
#import "MyTabBarViewController.h"

@interface PhotoViewController : UIViewController {

    IBOutlet UILabel *label;
    IBOutlet UIImageView *imageView;
    UIButton *likeButton;
    UIButton *skipButton;
    UIButton *likeCommentButton;
}

@property (nonatomic, retain) UILabel *label;
@property (nonatomic, retain) UIImageView *imageView;

-(IBAction)likeButton:(UIButton *)sender;
-(IBAction)skipButton:(UIButton *)sender;
-(IBAction)likeCommentButton:(UIButton *)sender;


@end

【问题讨论】:

    标签: iphone objective-c object memory-management uiviewcontroller


    【解决方案1】:

    要编写 iOS 应用程序,了解memory management rules 至关重要。

    在 ViewControllerB,viewDidLoad,你分配 pvc。 再往下,您将 pvc 的视图添加为滚动条的子视图。这保留了 pvc 的观点,但不保留 pvc 本身。然后当你释放 pvc 时,它的保留计数为零,当你稍后引用它时,它就消失了。碰撞。看来您需要传入并保留对正在使用它的控制器中的 pvc 的引用。

    【讨论】:

    • 谢谢@Rayfleck - 直到最后一句话我都明白了。当您说我应该传入并保留对 pvc 的引用时,您是指在 ViewControllerB 中“设置”一个 pvc 吗? ...像[svc setViewController:pvc]???我需要传入并保留每个 pvc 对象的引用吗?
    • 是的。在 svc 中声明 @property (nonatomic,retain) PhotoViewController thePvc;然后你可以说 svc.thePvc = pvc;那将保留它。 “对于每个 pvc 对象” - 是的,对于 pvc 的每个实例。
    • 嗨@Rayfleck - 我刚刚尝试这样做,但没有成功。而且我认为问题源于 svc (ViewControllerB) 是发生循环的控制器(即,我分配初始化 pvc 的位置)。所以基本上我在 ViewControllerB 中做 self.thePVC = pvc。你能帮我弄清楚我哪里出错了吗?因为我仍然无法想象在哪里应用您的建议。
    • 不确定我的上一条评论是否有帮助,但基本上 ViewControllerB 是使用 pvc 的控制器。您最初的回复提到我“需要传入并在使用它的控制器中保留对 pvc 的引用”。由于 ViewControllerB 是我分配 pvc 的位置,我应该在哪里保留对它的引用?
    • 让我们后退一分钟 - 当应用程序崩溃时日志到底说了什么 - 它在哪一行?
    【解决方案2】:

    我不确定您为什么使用PhotoViewController(子类化UIViewController) 而不是PhotoView(子类化UIView)。因为您没有使用视图控制器的任何功能(没有生命周期方法和其他)。

    如果您将PhotoViewControllerUIView 子类化并删除视图控制器的方法,它将起作用并且不会导致任何内存问题,因为您已经在与Rayfleck 的讨论中讨论过。 (它将由父视图控制器保留。)

    如果您正在考虑事件,那么这些也将由视图本身处理。但是,如果您想在控制器中处理它,那么您可以轻松委托或传递您的控制器引用并在其上调用事件。

    谢谢,

    【讨论】:

    • 感谢拉文。我尝试这样做,但 UIViews 从未出现过。此外,它有点搞砸了其他部分。不过谢谢:-)
    猜你喜欢
    • 1970-01-01
    • 2011-06-28
    • 1970-01-01
    • 2020-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多