我正在编写一个应用程序,它会将图像格式化为256 * 256并将这些图块文件写回目录中。如果有任何更新,我每次更新我的URL并将这些图像平铺回来并存储在iphone文件夹中。我担心两件事: 1)内存消耗 - 5个大小为200 KB的图像的内存消耗量是多少? 2)如果我必须同时使用图像平铺5个不同的URL,我可以多快处理我的应用程序?
我已经编写了一个代码来平铺并保存在一个URL的目录中,并希望对5个URL执行相同的操作。建议采用这种方法还是有人采用不同的方法?
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *URLString = @"http://www.abc.com/abc.html?event=123";
NSURL *url = [[NSURL alloc] initWithString:URLString];
NSData * dataImage = [NSData dataWithContentsOfURL:url];
NSString *directoryPath = [[NSBundle mainBundle] bundlePath];
UIImage *big = [UIImage imageWithData:dataImage];
[self saveTilesOfSize:(CGSize){256,256} forImage:big toDirectory:directoryPath usingPrefix:@"image_124_"];
TileView *tv = [[TileView alloc] initWithFrame:(CGRect){{0,0}, (CGSize){5000,5000}}];
[tv setTileTag:@"image_110_"];
[tv setTileDirectory:directoryPath];
[scrollView addSubview:tv];
[scrollView setContentSize:(CGSize){5000,5000}];
}
- (void)saveTilesOfSize:(CGSize)size
forImage:(UIImage*)image
toDirectory:(NSString*)directoryPath
usingPrefix:(NSString*)prefix
{
CGFloat cols = [image size].width / size.width;
CGFloat rows = [image size].height / size.height;
int fullColumns = floorf(cols);
int fullRows = floorf(rows);
CGFloat remainderWidth = [image size].width -
(fullColumns * size.width);
CGFloat remainderHeight = [image size].height -
(fullRows * size.height);
if (cols > fullColumns) fullColumns++;
if (rows > fullRows) fullRows++;
CGImageRef fullImage = [image CGImage];
for (int y = 0; y < fullRows; ++y) {
for (int x = 0; x < fullColumns; ++x) {
CGSize tileSize = size;
if (x + 1 == fullColumns && remainderWidth > 0) {
// Last column
tileSize.width = remainderWidth;
}
if (y + 1 == fullRows && remainderHeight > 0) {
// Last row
tileSize.height = remainderHeight;
}
CGImageRef tileImage = CGImageCreateWithImageInRect(fullImage,
(CGRect){{x*size.width, y*size.height},
tileSize});
NSData *imageData = UIImagePNGRepresentation([UIImage imageWithCGImage:tileImage]);
NSString *path = [NSString stringWithFormat:@"%@/%d.png",
directoryPath, prefix];
[imageData writeToFile:path atomically:NO];
}
}
}
答案 0 :(得分:1)
我已经针对类似的问题实现了解决方案(区别在于,我没有将它们保存在目录中,这些仅用于显示目的。),采用不同的方法。
在我的问题中,I have 84 images of 250x250 dimension with size 8KB each
(我在scrollView
添加了它们,在滚动时我加载了它们,有点类似于Google地图,但更加流畅)。起初我使用的是与你相同的方法,但性能却有问题。所以,我使用了异步加载概念。我用连接委托编写了一个UIImageView子类,因此UIImageView子类负责加载它的图像。因为加载是异步的所以性能要好得多。
正如你问的那样
1)内存消耗 - 5个大小为200 KB的图像的内存消耗量是多少?
Ans
:5x200KB = 1MB~1.2MB左右(所以你需要那么多的内存来显示,如果你有那么多的内存,那么你不用担心。)..在我的情况下84x8KB = 672~900KB(因为我使用了一些额外的东西,比如每个imageview的活动指示器)。
2)如果我必须同时使用图像平铺5个不同的URL,我可以多快处理我的应用程序?
Ans
:当你在viewDidLoad ...或主线程中加载它时,性能将是一个问题(阻塞可能会发生,因为我不完全确定你是否使用线程)。
快速建议:
1. write an UIImageView subclass which has connection delegate methods.
2. have some method that you can call from outside to message this imageView to start loading.(give the url)
3. do proper deallocation of resources like responseData and connection object, once the downloading is complete.
4. when you move from this view to other view do proper deallocation and removal of all these imageviews.
5. use intruments to look for the allocations by this.
代码:
的 TileImageView.h
强> 的
@interface TileImageView : UIImageView
{
NSURLConnection *serverConnection;
BOOL isImageRequested;
NSMutableData *responseData;
}
-(void) startImageDownloading:(NSString *)pRequestURL
-(void) deallocateResources;
-(BOOL) isImageRequested;
-(void)cancelConnectionRequest;
-(void) addActivityIndicator;
-(void) removeActivityIndicator;
@end
的 TileImageView.m
强> 的
@implementation TileImageView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self)
{
// Initialization code.
isImageRequested = NO;
}
return self;
}
-(BOOL) isImageRequested
{
return isImageRequested;
}
-(void) startImageDownloading:(NSString *)pRequestURL
{
if (!isImageRequested)
{
NSURL *pServerURL = [[NSURL alloc] initWithString:pRequestURL];
if (pServerURL != nil)
{
isImageRequested = YES;
[self addActivityIndicator];
[self setBackgroundColor:[UIColor lightGrayColor]];
NSURLRequest *pServerRequest = [[NSURLRequest alloc]initWithURL:pServerURL];
serverConnection = [[NSURLConnection alloc] initWithRequest:pServerRequest delegate:self];
if(serverConnection)
{
responseData = [[NSMutableData alloc] init];
}
[pServerURL release];
[pServerRequest release];
}
}
}
-(void) addActivityIndicator
{
UIActivityIndicatorView *tempActivityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
CGFloat size = self.frame.size.width*0.12;
[tempActivityIndicator setFrame:CGRectMake(0, 0, size, size)];
[tempActivityIndicator setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/2)];
[tempActivityIndicator setTag:1000];
[tempActivityIndicator setHidesWhenStopped:YES];
[tempActivityIndicator startAnimating];
[self addSubview:tempActivityIndicator];
[tempActivityIndicator release];
}
-(void) removeActivityIndicator
{
UIActivityIndicatorView *tempActivityIndicator = (UIActivityIndicatorView *)[self viewWithTag:1000];
if (tempActivityIndicator != nil)
{
[tempActivityIndicator stopAnimating];
[tempActivityIndicator removeFromSuperview];
}
}
-(void)cancelConnectionRequest
{
if (isImageRequested && serverConnection != nil)
{
[serverConnection cancel];
[self removeActivityIndicator];
[self deallocateResources];
isImageRequested = NO;
}
}
// Name : connection: didReceiveAuthenticationChallenge:
// Description : NSURLConnectionDelegate method. Method that gets called when server sends an authentication challenge.
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
// Name : connection: didReceiveResponse:
// Description : NSURLConnectionDelegate method. Method that gets called when response for the launched URL is received..
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response
{
[responseData setLength:0];
}
// Name : connection: didReceiveData:
// Description : NSURLConnectionDelegate method. Method that gets called when data for the launched URL is received..
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
{
[responseData appendData:data];
}
// Name : connection: didFailWithError:
// Description : NSURLConnectionDelegate method. Method that gets called when an error for the launched URL is received..
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error
{
NSLog(@"Error occured while loading image : %@",error);
[self removeActivityIndicator];
[self deallocateResources];
UILabel *tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 30)];
[tempLabel setBackgroundColor:[UIColor clearColor]];
[tempLabel setFont:[UIFont systemFontOfSize:11.0f]];
[tempLabel setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/2)];
[tempLabel setText:@"Image not available."];
[self addSubview:tempLabel];
[tempLabel release];
}
// Name : connectionDidFinishLoading
// Description : NSURLConnectionDelegate method. Method that gets called when connection loading gets finished.
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
[self removeActivityIndicator];
UIImage *tempImage = [[UIImage alloc] initWithData:responseData];
self.image = tempImage;
[tempImage release];
[self deallocateResources];
}
-(void) deallocateResources
{
if (serverConnection != nil)
{
[serverConnection release];
serverConnection = nil;
}
if (responseData != nil)
{
[responseData release];
responseData = nil;
}
}
- (void)dealloc {
[super dealloc];
}
@end
所以,如果你使用上面的代码,那么你只需要添加TileImageView的对象,只需调用方法-(void) startImageDownloading:(NSString *)pRequestURL
。
Please use instruments to track allocations.
更新:
**How do I add TileImageView on scrollView ? :**
//像这样我在2D形状(12 x 7)网格中添加了84个图像......一旦添加了图像,我就按照完整的网格大小设置了scrollView的contentSize。
TileImageView *tileImageView = [[TileImageView alloc]initWithFrame:<myFrameAsPerMyNeeds>];
[tileImageView setTag:<this is the identifier I use for recognizing the image>];
[myImageScrollView addSubView:tileImageView];
[tileImageView release];
..稍后在代码中,当用户滚动和其他图像视图进入可见性时。我使用以下代码...
TileImageView * loadableImageView =(TileImageView *)[myImageScrollView viewWithTag:]; [loadableImageView startImageDownloading:];
我不需要在drawRect
中做任何事情,因为我不需要做客户绘图。
对于图像名称,您可以使用imageView
中的标记属性,但如果您需要一些更像字符串的不同名称,则可以在imageView
中为图像名称添加另一个属性,并在添加时设置它图像视图。为了保存数据,您可以使用didFinishLoading
TileImageView
方法下载图片后调用您的方法,您可以使用该名称。
SECODN UPDATE
我如何在TileImageView
ScrollView
gridCount = 0;
rows = 7;
columns = 12;
totalGrids = rows*columns;
//*above : all are NSInteger type variable declared at class level
chunkWidth = 250;
chunkHeight = 250;
contentWidth = 0.0;
contentHeight = 0.0;
//*above : all are CGFloat type variable declared at class level
for (int i=0; i<rows; i++)
{
contentWidth = 0.0;
for (int j=0 ; j<columns; j++)
{
gridCount++;
CGRect frame = CGRectMake(contentWidth, contentHeight, chunkWidth, chunkHeight);
[self addNewImageViewWithTag:gridCount frame:frame];
contentWidth += chunkWidth;
}
contentHeight += chunkHeight;
}
[imageScrollView setContentSize:CGSizeMake(contentWidth, contentHeight)];
[imageScrollView setContentOffset:CGPointMake(0, 0)];
[imageScrollView setUserInteractionEnabled:YES];
并使用ScrollViewDelegate
方法。
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
if (isZoomed)
{
xOffset = scrollView.contentOffset.x;
yOffset = scrollView.contentOffset.y;
//*above : both are CGFloat type variable declared at class level
visibleColumn = xOffset/chunkWidth+1;
visibleRow = yOffset/chunkHeight+1;
gridNumber = (visibleRow-1)*columns+visibleColumn;
adjGrid1 = gridNumber+1;
adjGrid2 = gridNumber+columns;
adjGrid3 = adjGrid2+1;
//*above : all are NSInteger type variable declared at class level
if (gridNumber ==1)
{
[self createAndSendScrollRequest:gridNumber];
}
if (adjGrid1 > 0 && adjGrid1 <= totalGrids)
{
[self createAndSendScrollRequest:adjGrid1];
}
if (adjGrid2 > 0 && adjGrid2 <= totalGrids)
{
[self createAndSendScrollRequest:adjGrid2];
}
if (adjGrid3 > 0 && adjGrid3 <= totalGrids)
{
[self createAndSendScrollRequest:adjGrid3];
}
}
}
这就是createAndSendScrollRequest
的实施方式。
- (void) createAndSendScrollRequest:(NSInteger)chunkId
{
TileImageView *loadingImageView = (TileImageView *)[imageScrollView viewWithTag:chunkId];
if ([loadingImageView image]==nil)
{
[loadingImageView startImageDownloading:<and here I pass url my url is based on tag so In reality I dont pass anything I just use it from the imageview's tag property>];
}
}
谢谢,