[iOS]SDWebImage 源码阅读(一)

这是我看的第四份比较著名的源码了,看了不只一遍,因为里面蕴含的知识太多了,而且刚开始也有一些地方不懂,不过经过不断的查资料等等终于是差不多理解了,下面就把我看的地方记录下来,希望能对正在看这份代码的人有所帮助,我会尽量的每一处地方都讲到。老规矩,按照一次代码调用的全程来分析。

首先看最常用的方法

/**
 * Set the imageView `image` with an `url`.
 *
 * The download is asynchronous and cached.
 *
 * @param url The url for the image.
 */
- (void)sd_setImageWithURL:(NSURL *)url;

这里只要传入url即可。

但是整个框架有很多个初始化方法,如下:

- (void)sd_setImageWithURL:(NSURL *)url ;

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder ;

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options ;

- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock ;

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock ;

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock ;

所有的这些初始化方法都调用了同一个初始化方法:

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock ;

这个叫做全能初始化方法,详见effctive Objective-C 2.0中的第16条,以后我们设计接口的时候也应该这么设计。


if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            self.image = placeholder;
        });
    }

先看这个判断,它是这样定义的
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {

    SDWebImageRetryFailed = 1 << 0,


    SDWebImageLowPriority = 1 << 1,<span style="font-family: Arial, Helvetica, sans-serif;">}</span>

这是利用了二进制,如果选定了这个选项,这个选项就被置为1.

然后在判断里用与操作就能得到是否是这个选项,好处是同一个变量可以被赋予多重含义,且运算速度快。

这里代码的意义是placeholder是否要延迟,如果不延迟,马上就把placeholder先放上去。

而且作者在这里定义了一个宏,这个宏的作用是,如果当前线程不是主线程,他会把代码强制放到主线程中执行。

然后看下面的代码

if ([self showActivityIndicatorView]) {
            [self addActivityIndicator];
        }
如果用户允许显示activityIndicatorView,那就添加一个activityIndicator.

下面讲activityIndicator的添加过程。

这块其实是一个lazy load

先判断是否为空,如果不为空则直接开始动画。

如果为空,则创建一个,然后首先添加约束。

self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
这句,如果从代码层面开始使用autolayout,需要把这个属性设置为NO。然后可以通过代码添加约束。

[self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
                                                             attribute:NSLayoutAttributeCenterX
                                                             relatedBy:NSLayoutRelationEqual
                                                                toItem:self
                                                             attribute:NSLayoutAttributeCenterX
                                                            multiplier:1.0
                                                              constant:0.0]];
            [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
                                                             attribute:NSLayoutAttributeCenterY
                                                             relatedBy:NSLayoutRelationEqual
                                                                toItem:self
                                                             attribute:NSLayoutAttributeCenterY
                                                            multiplier:1.0
                                                              constant:0.0]];

以上是添加的约束,字面就可以理解。


然后。

__weak __typeof(self)wself = self;

这句话是为了防止循环引用。


之后用到了SDWebImageManager这个单例类。


id <SDWebImageOperation> operation = [[SDWebImageManager sharedManager] downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {...}


看一下这个类的初始化

- (instancetype)init {
    SDImageCache *cache = [SDImageCache sharedImageCache];
    SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    return [self initWithCache:cache downloader:downloader];
}

它初始化了图片缓存模块的类和图片下载模块的类。

关于SDWebImageOperation这个协议我们先不管,包括operation都先放下,以后再说。

下面进入到上面那个downloadImageWithURL方法中去看一看。


    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    // Prevents app crashing on argument type error like sending NSNull instead of NSURL
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

如果用户没有传入NSURL对象,而是传了一个字符串进来,把它变成NSURL对象。

    BOOL isFailedUrl = NO;
    @synchronized (self.failedURLs) {
        isFailedUrl = [self.failedURLs containsObject:url];
    }

这是一个失败url的列表,并且加了锁,防止多线程的问题。

这个与后面的SDWebImageRetryFailed选项是有关系的。

    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        dispatch_main_sync_safe(^{
            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
            completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
        });
        return operation;
    }

如果url的长度为0或者url是失败的且不重试,则直接创建NSError并且直接completedBlock。

这里有一个点,就是在程序中最好不好使用try catch,因为这很容易会造成内存泄漏,因为当程序发生错误返回的时候,会导致一些对象不能正常被释放,所以最好用传递NSError的方式来报告错误。


NSString *key = [self cacheKeyForURL:url];

这里,它对每一个url做了缓存,key就是url对应的字符串,SDWebImage的缓存也是根据这个key来找到对应的图片的。

这里还有一个对url的处理,需要手动设置,不看源码是不会发现的。

就是

[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) {
    url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
    return [url absoluteString];
}];
你可以做一个这样的设置,就可以过滤掉url后面的参数,一般这些参数很长,可以节省内存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值