iPhone应用程序中的内存分配、释放和NSURLConnection

发布于 2024-08-08 17:54:23 字数 5457 浏览 6 评论 0原文

我希望有人能帮助我解决这个问题。我正在努力寻找一个本应简单的问题的答案。顺便说一句,这是我使用 c 和 c# 多年后的第一个主要 obj-c 项目,所以请随意指出我失败的地方。

我有一个 iPhone 应用程序,旨在将照片相册加载到 UIScrollView 中。它还具有随机图像功能,使用相同的过程但仅显示单个图像。它的工作原理如下:

  1. 读取外部 XML feed(存储在 ruby​​-on-rails 网站上),其中包含解析后的照片随机 url 的路径。
  2. URL 的内容是使用 NSURLConnection 下载到 NSData 的。
  3. ScrollView 被创建并推送
  4. 子类 UIView 分配一个 UIImageView,用 NSData 分配一个 UIImage,用 UIimage 初始化 UIImageView,最后将 imageview 添加到 UIView。
  5. 然后父视图将 UIView 添加到 UIScrollView 中,然后将其推到前面。

当需要下一个随机图像时,该过程会再次发生。在显示整个图像相册时,它也使用相同的过程,只是将几个 UIView 添加到 UIScrollView 中。

问题是,尽管在适当的情况下使用了release和delloc,但活动工具表明当请求下一个图像时,NSURLConnection和UIImage使用的内存没有从内存中释放。通过在 iPhone 上构建应用程序进一步证明了这一点。连续请求多个图像会导致应用程序崩溃,可能是由于内存消耗。

以下是一些代码,因为由于合同协议,我无法发布整个项目。

URLDownload 类(使用 DataDownload)

@implementation URLDownload
    -(NSData *)GetURL:(NSURL *)strURL
{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    DataDownload *request = [DataDownload alloc];
request.strURL = strURL;
[request init];

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (request.isLoading && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

[pool release];

return [request urlData];
[request release];

}

DataDownload 类

@implementation DataDownload
@synthesize urlData, connection, strURL, isLoading;

- (id)init
{
if(self = [super init])
{

    self.isLoading = YES;

    NSURLRequest *dataRequest = [NSURLRequest requestWithURL:self.strURL
                                             cachePolicy:NSURLRequestReloadIgnoringCacheData
                                         timeoutInterval:60];

    /* establish the connection */  
    self.connection = [[NSURLConnection alloc]
                 initWithRequest:dataRequest
                 delegate:self
    ];

    if (connection == nil) {
        self.urlData = nil; 
    } else {
        self.urlData = [NSMutableData data];   
    }

}

return self;

}

- (void)dealloc {
[connection cancel];
[connection release];
[urlData release];
[strURL release];
[super dealloc];
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil;
}

 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response
 {
[urlData setLength:0];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
 }

 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)incrementalData
{
[self.urlData appendData:incrementalData];
}

-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.isLoading = NO;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
   self.isLoading = NO;
}

@end

ImageView 子类

@implementation ImageView

@synthesize strURL, imvImageView, image, currentlyRotated, imgOptionsView, startDate;

- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {       

    self.imvImageView = [UIImageView alloc];    

    if (self.strURL != nil){
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
        NSData *datImageData = [NSData dataWithContentsOfURL: [NSURL URLWithString:self.strURL]];
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

        self.image = [UIImage imageWithData: datImageData];


        CGSize imgSize = image.size;
        CGFloat fltWidth = imgSize.width;
        CGFloat fltHeight = imgSize.height;

        [imvImageView initWithImage:image];

        // If landscape rotate image
        if (fltWidth > fltHeight){
            imvImageView.frame = CGRectMake(-80.0, 80.0, 480.0, 320.0);

            CGAffineTransform rotate = CGAffineTransformMakeRotation(-1.57079633);
            [imvImageView setTransform:rotate];

            self.currentlyRotated = YES;
        }else{
            imvImageView.frame = CGRectMake(0.0, 0.0, 320.0, 480.0);

            self.currentlyRotated = NO;
        }

        [image release];
    }else{
        self.image = [UIImage alloc];
        [imvImageView initWithImage:image];
        [image release];
    }

    [self addSubview:imvImageView];

}

[imvImageView release];

return self;
}

- (void)drawRect:(CGRect)rect {
// Drawing code
}


- (void)dealloc {
[strURL release];
[imvImageView release];
[image release];
[imgOptionsView release];
[startDate release];
[super dealloc];
}

如何显示图像的示例代码

- (void)displayImage:(NSString *)strURL {
NSString *strFullURL = @"http://www.marklatham.co.uk";
strFullURL = [strFullURL stringByAppendingString:strURL];

self.scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 480.0)];
[scroller setContentSize:CGSizeMake(320.0, 540.0)];
[self.view addSubview:scroller];

CGRect rect = CGRectMake(0.0, 0.0, 320.0, 480.0);   
self.imageView = [ImageView alloc];
imageView.strURL = strFullURL;
[imageView initWithFrame:rect];
[scroller addSubview:imageView];

[scroller release];
[imageView release];

}

下图显示了所有分配以说明所发生的情况。 1 显示启动时分配,2 显示第一个映像加载后,3 显示第二个映像加载后。

http://www.gretanova.com/misc/iphone/1.png & 2.png& 3.png

谢谢大家, 李

I'm hoping someone can help me with this. I'm struggling to find an answer to what should be an easy question. By the way, this is my first major obj-c project after years of using c and c# so feel free to point out things I'm failing on.

I have an iPhone app designed to load an album of photos into a UIScrollView. It also has a random image function which uses the same process but only displays a single image. It works like so:

  1. Read an external XML feed (stored on ruby-on-rails website) which contains a path to a random url of photo once parsed.
  2. Content of URL is downloaded using NSURLConnection to NSData.
  3. ScrollView is created and pushed
  4. Subclassed UIView allocs an UIImageView, allocs a UIImage with the NSData which, inits the UIImageView with the UIimage and finally adds the imageview to the UIView.
  5. The parent view then adds the UIView to the UIScrollView which is then pushed to the front.

This process occurs again when when the next random image is required. It also uses the same process when displaying an entire album of images except several UIViews are added to the UIScrollView.

The problem is, despite using release and delloc where appropriate, the activity tool indicates that the memory used by NSURLConnection and UIImage isn't being released from memory when the next image is requested. This is further proven by building the app to the iPhone. Requesting several images in a row causes the app the crash presumably from memory consumption.

Below is some of the code as I'm unable to post the entire project due to contractual agreements.

URLDownload class (uses DataDownload)

@implementation URLDownload
    -(NSData *)GetURL:(NSURL *)strURL
{

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    DataDownload *request = [DataDownload alloc];
request.strURL = strURL;
[request init];

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (request.isLoading && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

[pool release];

return [request urlData];
[request release];

}

DataDownload class

@implementation DataDownload
@synthesize urlData, connection, strURL, isLoading;

- (id)init
{
if(self = [super init])
{

    self.isLoading = YES;

    NSURLRequest *dataRequest = [NSURLRequest requestWithURL:self.strURL
                                             cachePolicy:NSURLRequestReloadIgnoringCacheData
                                         timeoutInterval:60];

    /* establish the connection */  
    self.connection = [[NSURLConnection alloc]
                 initWithRequest:dataRequest
                 delegate:self
    ];

    if (connection == nil) {
        self.urlData = nil; 
    } else {
        self.urlData = [NSMutableData data];   
    }

}

return self;

}

- (void)dealloc {
[connection cancel];
[connection release];
[urlData release];
[strURL release];
[super dealloc];
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return nil;
}

 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response
 {
[urlData setLength:0];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
 }

 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)incrementalData
{
[self.urlData appendData:incrementalData];
}

-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
self.isLoading = NO;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
   self.isLoading = NO;
}

@end

ImageView subclass

@implementation ImageView

@synthesize strURL, imvImageView, image, currentlyRotated, imgOptionsView, startDate;

- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {       

    self.imvImageView = [UIImageView alloc];    

    if (self.strURL != nil){
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
        NSData *datImageData = [NSData dataWithContentsOfURL: [NSURL URLWithString:self.strURL]];
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

        self.image = [UIImage imageWithData: datImageData];


        CGSize imgSize = image.size;
        CGFloat fltWidth = imgSize.width;
        CGFloat fltHeight = imgSize.height;

        [imvImageView initWithImage:image];

        // If landscape rotate image
        if (fltWidth > fltHeight){
            imvImageView.frame = CGRectMake(-80.0, 80.0, 480.0, 320.0);

            CGAffineTransform rotate = CGAffineTransformMakeRotation(-1.57079633);
            [imvImageView setTransform:rotate];

            self.currentlyRotated = YES;
        }else{
            imvImageView.frame = CGRectMake(0.0, 0.0, 320.0, 480.0);

            self.currentlyRotated = NO;
        }

        [image release];
    }else{
        self.image = [UIImage alloc];
        [imvImageView initWithImage:image];
        [image release];
    }

    [self addSubview:imvImageView];

}

[imvImageView release];

return self;
}

- (void)drawRect:(CGRect)rect {
// Drawing code
}


- (void)dealloc {
[strURL release];
[imvImageView release];
[image release];
[imgOptionsView release];
[startDate release];
[super dealloc];
}

Sample code of how images are being displayed

- (void)displayImage:(NSString *)strURL {
NSString *strFullURL = @"http://www.marklatham.co.uk";
strFullURL = [strFullURL stringByAppendingString:strURL];

self.scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 480.0)];
[scroller setContentSize:CGSizeMake(320.0, 540.0)];
[self.view addSubview:scroller];

CGRect rect = CGRectMake(0.0, 0.0, 320.0, 480.0);   
self.imageView = [ImageView alloc];
imageView.strURL = strFullURL;
[imageView initWithFrame:rect];
[scroller addSubview:imageView];

[scroller release];
[imageView release];

}

The following images show all allocations to illustrate what's happening. 1 shows alloc on startup, 2 shows after first image is loaded and 3 after after second image.

http://www.gretanova.com/misc/iphone/1.png & 2.png & 3.png

Thanks everyone,
Lee

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ˇ宁静的妩媚 2024-08-15 17:54:23

有几件事很突出。例如,在 URLDownload 类中,永远不会到达对 [request release] 的调用,因为它位于 return 语句之后

return [request urlData];
[request release];

A couple of things stick out. For example within the URLDownload class the call to [request release] will never be reached as its placed after the return statement

return [request urlData];
[request release];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文