面试题答案
一键面试资源加载
- 懒加载:对于多媒体资源,不要在程序启动时就全部加载,而是在需要使用时再进行加载。例如,在
UITableView
或UICollectionView
的cellForRowAtIndexPath:
方法中,当某个cell
即将显示在屏幕上时,才开始加载对应的多媒体资源。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// 假设数据源数组为mediaArray,存储多媒体资源的路径或URL
NSString *mediaPath = self.mediaArray[indexPath.row];
// 开始加载多媒体资源
[self loadMediaWithPath:mediaPath completion:^(UIImage *image, NSError *error) {
if (image) {
cell.imageView.image = image;
}
}];
return cell;
}
- (void)loadMediaWithPath:(NSString *)path completion:(void (^)(UIImage *image, NSError *error))completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithContentsOfFile:path];
dispatch_async(dispatch_get_main_queue(), ^{
completion(image, nil);
});
});
}
- 异步加载:使用
GCD
(Grand Central Dispatch)将资源加载操作放到后台线程执行,避免阻塞主线程,保证界面的流畅性。如上面代码中loadMediaWithPath:completion:
方法就是使用GCD
在后台线程加载图片,加载完成后回到主线程更新UI。
缓存策略
- 内存缓存:使用
NSCache
类来实现内存缓存。NSCache
类似NSDictionary
,但它会根据系统内存情况自动释放内存,适合用于缓存多媒体资源。
@interface MediaCache : NSObject
@property (nonatomic, strong) NSCache *mediaCache;
+ (instancetype)sharedCache;
- (UIImage *)cachedImageForKey:(NSString *)key;
- (void)cacheImage:(UIImage *)image forKey:(NSString *)key;
@end
@implementation MediaCache
+ (instancetype)sharedCache {
static MediaCache *sharedCache = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedCache = [[MediaCache alloc] init];
sharedCache.mediaCache = [[NSCache alloc] init];
});
return sharedCache;
}
- (UIImage *)cachedImageForKey:(NSString *)key {
return [self.mediaCache objectForKey:key];
}
- (void)cacheImage:(UIImage *)image forKey:(NSString *)key {
[self.mediaCache setObject:image forKey:key];
}
@end
在加载多媒体资源时,先检查缓存中是否存在该资源:
- (void)loadMediaWithPath:(NSString *)path completion:(void (^)(UIImage *image, NSError *error))completion {
UIImage *cachedImage = [[MediaCache sharedCache] cachedImageForKey:path];
if (cachedImage) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(cachedImage, nil);
});
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithContentsOfFile:path];
if (image) {
[[MediaCache sharedCache] cacheImage:image forKey:path];
}
dispatch_async(dispatch_get_main_queue(), ^{
completion(image, nil);
});
});
}
- 磁盘缓存:对于不常使用但又不能轻易丢弃的多媒体资源,可以使用磁盘缓存。可以使用
SDWebImage
等第三方库来实现磁盘缓存,它不仅可以缓存图片,还提供了很多实用的功能,如图片下载、解码优化等。
内存优化
- 及时释放不再使用的资源:在
UITableView
或UICollectionView
的cell
被重用时,要及时释放之前加载的多媒体资源。可以在UITableViewCell
的prepareForReuse
方法中进行处理。
- (void)prepareForReuse {
[super prepareForReuse];
self.imageView.image = nil;
}
- 优化图片加载和显示:对于高分辨率图片,可以在加载时进行适当的缩放,使其符合显示需求,减少内存占用。可以使用
ImageIO
框架来优化图片解码过程,减少内存峰值。例如:
+ (UIImage *)scaleAndDecodeImage:(NSString *)imagePath targetSize:(CGSize)targetSize {
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:imagePath], NULL);
NSDictionary *options = @{
(id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue,
(id)kCGImageSourceThumbnailMaxPixelSize: @(MIN(targetSize.width, targetSize.height))
};
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef)options);
UIImage *scaledImage = [UIImage imageWithCGImage:thumbnail];
CFRelease(source);
CFRelease(thumbnail);
return scaledImage;
}
- 监控内存使用情况:使用 Instruments 工具中的 Memory Monitor 来监控内存使用情况,及时发现内存泄漏和内存增长过快的问题。可以定期打印内存使用信息,以便分析:
#import <mach/mach.h>
- (void)printMemoryUsage {
struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS) {
NSLog(@"Memory used: %f MB", (double)info.resident_size / 1024.0 / 1024.0);
}
}
避免性能瓶颈
- 优化图片解码:除了使用
ImageIO
框架优化图片解码,还可以提前解码图片,避免在主线程中进行解码操作。例如,在后台线程加载图片后,先进行解码,再将解码后的图片缓存起来或显示到UI上。 - 减少重复计算:对于一些需要对多媒体资源进行处理(如计算图片的尺寸、获取视频的时长等)的操作,要避免在每次显示时都进行重复计算,可以将计算结果缓存起来。
- 优化视频播放:对于视频播放,可以使用
AVPlayer
框架,并合理设置播放参数,如视频的分辨率、帧率等,以适应设备性能。同时,要注意在视频播放完成或暂停时,及时释放相关资源,避免内存泄漏。