SDWebImage源码解析之SDWebImageManager的注解

http://www.cocoachina.com/ios/20150612/12118.html


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

/*

 * This file is part of the SDWebImage package.

 * (c) Olivier Poitrey  *

 * For the full copyright and license information, please view the LICENSE

 * file that was distributed with this source code.

 */

#import "SDWebImageCompat.h"

#import "SDWebImageOperation.h"

#import "SDWebImageDownloader.h"

#import "SDImageCache.h"

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {

    /**

     * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won‘t keep trying.

     * This flag disable this blacklisting.

     */

    /**

     *默认情况下,如果一个url在下载的时候失败了,那么这个url会被加入黑名单并且library不会尝试再次下载,这个flag会阻止library把失败的url加入黑名单(简单来说如果选择了这个flag,那么即使某个url下载失败了,sdwebimage还是会尝试再次下载他.)

     */

    SDWebImageRetryFailed = 1 << 0,

    /**

     * By default, image downloads are started during UI interactions, this flags disable this feature,

     * leading to delayed download on UIScrollView deceleration for instance.

     */

    /**

     *默认情况下,图片会在交互发生的时候下载(例如你滑动tableview的时候),这个flag会禁止这个特性,导致的结果就是在scrollview减速的时候

     *才会开始下载(也就是你滑动的时候scrollview不下载,你手从屏幕上移走,scrollview开始减速的时候才会开始下载图片)

     */

    SDWebImageLowPriority = 1 << 1,

    /**

     * This flag disables on-disk caching

     */

    /*

     *这个flag禁止磁盘缓存,只有内存缓存

     */

    SDWebImageCacheMemoryOnly = 1 << 2,

    /**

     * This flag enables progressive download, the image is displayed progressively during download as a browser would do.

     * By default, the image is only displayed once completely downloaded.

     */

    /*

     *这个flag会在图片下载的时候就显示(就像你用浏览器浏览网页的时候那种图片下载,一截一截的显示(待确认))

     *

     */

    SDWebImageProgressiveDownload = 1 << 3,

    /**

     * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.

     * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.

     * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.

     * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.

     *

     * Use this flag only if you can‘t make your URLs static with embeded cache busting parameter.

     */

    /*

     *这个选项的意思看的不是很懂,大意是即使一个图片缓存了,还是会重新请求.并且缓存侧略依据NSURLCache而不是SDWebImage.

     *

     */

    SDWebImageRefreshCached = 1 << 4,

    /**

     * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for

     * extra time in background to let the request finish. If the background task expires the operation will be cancelled.

     */

    /*

     *启动后台下载,加入你进入一个页面,有一张图片正在下载这时候你让app进入后台,图片还是会继续下载(这个估计要开backgroundfetch才有用)

     */

    SDWebImageContinueInBackground = 1 << 5,

    /**

     * Handles cookies stored in NSHTTPCookieStore by setting

     * NSMutableURLRequest.HTTPShouldHandleCookies = YES;

     */

    /*

     *可以控制存在NSHTTPCookieStore的cookies.(我没用过,等用过的人过来解释一下)

     */

    SDWebImageHandleCookies = 1 << 6,

    /**

     * Enable to allow untrusted SSL ceriticates.

     * Useful for testing purposes. Use with caution in production.

     */

    /*

     *允许不安全的SSL证书,在正式环境中慎用

     */

    SDWebImageAllowInvalidSSLCertificates = 1 << 7,

    /**

     * By default, image are loaded in the order they were queued. This flag move them to

     * the front of the queue and is loaded immediately instead of waiting for the current queue to be loaded (which 

     * could take a while).

     */

    /*

     *默认情况下,image在装载的时候是按照他们在队列中的顺序装载的(就是先进先出).这个flag会把他们移动到队列的前端,并且立刻装载

     *而不是等到当前队列装载的时候再装载.

     */

    SDWebImageHighPriority = 1 << 8,

    /**

     * By default, placeholder images are loaded while the image is loading. This flag will delay the loading

     * of the placeholder image until after the image has finished loading.

     */

    /*

     *默认情况下,占位图会在图片下载的时候显示.这个flag开启会延迟占位图显示的时间,等到图片下载完成之后才会显示占位图.(等图片显示完了我干嘛还显示占位图?或许是我理解错了?)

     */

    SDWebImageDelayPlaceholder = 1 << 9,

    /**

     * We usually don‘t call transformDownloadedImage delegate method on animated images,

     * as most transformation code would mangle it.

     * Use this flag to transform them anyway.

     */

    /* 

     *是否transform图片(没用过,还要再看,但是据我估计,是否是图片有可能方向不对需要调整方向,例如采用iPhone拍摄的照片如果不纠正方向,那么图片是向左旋转90度的.可能很多人不知道iPhone的摄像头并不是竖直的,而是向左偏了90度.具体请google.)

     */

    SDWebImageTransformAnimatedImage = 1 << 10,

};

typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL);

typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);

typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url);

@class SDWebImageManager;

@protocol SDWebImageManagerDelegate @optional

/**

 * Controls which image should be downloaded when the image is not found in the cache.

 *

 * @param imageManager The current `SDWebImageManager`

 * @param imageURL     The url of the image to be downloaded

 *

 * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied.

 */

/*

 *主要作用是当缓存里没有发现某张图片的缓存时,是否选择下载这张图片(默认是yes),可以选择no,那么sdwebimage在缓存中没有找到这张图片的时候不会选择下载

 */

- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;

/**

 * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory.

 * NOTE: This method is called from a global queue in order to not to block the main thread.

 *

 * @param imageManager The current `SDWebImageManager`

 * @param image        The image to transform

 * @param imageURL     The url of the image to transform

 *

 * @return The transformed image object.

 */

/**

 *在图片下载完成并且还没有加入磁盘缓存或者内存缓存的时候就transform这个图片.这个方法是在异步线程执行的,防治阻塞主线程.

 *至于为什么在异步执行很简单,对一张图片纠正方向(也就是transform)是很耗资源的,一张2M大小的图片纠正方向你可以用instrument测试一下耗时.

 *很恐怖

 */

- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;

@end

/**

 * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes.

 * It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache).

 * You can use this class directly to benefit from web image downloading with caching in another context than

 * a UIView.

 *

 * Here is a simple example of how to use SDWebImageManager:

 *

 * @code

SDWebImageManager *manager = [SDWebImageManager sharedManager];

[manager downloadImageWithURL:imageURL

                      options:0

                     progress:nil

                    completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {

                        if (image) {

                            // do something with image

                        }

                    }];

 * @endcode

 */

/*

 *这一段是阐述SDWebImageManager是干嘛的.其实UIImageView+WebCache这个category背后执行操作的就是这个SDWebImageManager.他会绑定一个下载器也就是SDWebImageDownloader和一个缓存SDImageCache.后面的大意应该是讲你可以直接使用一个其他上下文环境的SDWebImageManager,而不是仅仅限于一个UIView.

 */

@interface SDWebImageManager : NSObject

@property (weak, nonatomic) id  delegate;

/**

 *如同上文所说,一个SDWebImageManager会绑定一个imageCache和一个下载器.

 */

@property (strong, nonatomic, readonly) SDImageCache *imageCache;

@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;

/**

 * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can

 * be used to remove dynamic part of an image URL.

 *

 * The following example sets a filter in the application delegate that will remove any query-string from the

 * URL before to use it as a cache key:

 *

 * @code

[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) {

    url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];

    return [url absoluteString];

}];

 * @endcode

 */

/*

 * 这个cacheKeyFilter是干嘛的呢?很简单.1他是一个block.2.这个block的作用就是生成一个image的key.因为sdwebimage的缓存原理你可以当成是一个字典,每一个字典的value就是一张image,那么这个value对应的key是什么呢?就是cacheKeyFilter根据某个规则对这个图片的url做一些操作生成的.上面的示例就显示了怎么利用这个block把image的url重新组合生成一个key.以后当sdwebimage检测到你

 */

@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter;

/**

 * Returns global SDWebImageManager instance.

 *

 * @return SDWebImageManager shared instance

 */

/*

 *这个不用我解释了吧,生成一个SDWebImagemanager的单例.

 */

+ (SDWebImageManager *)sharedManager;

/**

 * Downloads the image at the given URL if not present in cache or return the cached version otherwise.

 * 从给定的URL中下载一个之前没有被缓存的Image.

 *

 * @param url            The URL to the image

 * @param options        A mask to specify options to use for this request

 * @param progressBlock  A block called while image is downloading

 * @param completedBlock A block called when operation has been completed.

 *

 *   This parameter is required.

 

 *   This block has no return value and takes the requested UIImage as first parameter.

 *   In case of error the image parameter is nil and the second parameter may contain an NSError.

 *

 *   The third parameter is an `SDImageCacheType` enum indicating if the image was retrived from the local cache

 *   or from the memory cache or from the network.

 *

 *   The last parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is 

 *   downloading. This block is thus called repetidly with a partial image. When image is fully downloaded, the

 *   block is called a last time with the full image and the last parameter set to YES.

 *

 * @return Returns an NSObject conforming to SDWebImageOperation. Should be an instance of SDWebImageDownloaderOperation

 */

/*

 * 这个方法主要就是SDWebImage下载图片的方法了.  

 * 第一个参数是必须要的,就是image的url

 * 第二个参数就是我们上面的Options,你可以定制化各种各样的操作.详情参上. 

 * 第三个参数是一个回调block,用于图片在下载过程中的回调.(英文注释应该是有问题的.)

 * 第四个参数是一个下载完成的回调.会在图片下载完成后回调.

 * 返回值是一个NSObject类,并且这个NSObject类是conforming一个协议这个协议叫做SDWebImageOperation,这个协议很简单,就是一个cancel掉operation的协议.

 */

- (id )downloadImageWithURL:(NSURL *)url

                                         options:(SDWebImageOptions)options

                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock

                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;

/**

 * Saves image to cache for given URL

 *

 * @param image The image to cache

 * @param url   The URL to the image

 *

 */

/*

 * 将图片存入cache的方法,类似于字典的setValue: forKey:

 */

- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url;

/**

 * Cancel all current opreations

 */

/*

 *取消掉当前所有的下载图片的operation

 */

- (void)cancelAll;

/**

 * Check one or more operations running

 */

/*

 * check一下是否有一个或者多个operation正在执行(简单来说就是check是否有图片在下载)

 */

- (BOOL)isRunning;

/**

 *  Check if image has already been cached

 *

 *  @param url image url

 *

 *  @return if the image was already cached

 */

/*

 * 通过一个image的url是否已经存在,如果存在返回yes,否则返回no

 */

- (BOOL)cachedImageExistsForURL:(NSURL *)url;

/**

 *  Check if image has already been cached on disk only

 *

 *  @param url image url

 *

 *  @return if the image was already cached (disk only)

 */

/*

 * 检测一个image是否已经被缓存到磁盘(是否存且仅存在disk里).

 */

- (BOOL)diskImageExistsForURL:(NSURL *)url;

/**

 *  Async check if image has already been cached

 *

 *  @param url              image url

 *  @param completionBlock  the block to be executed when the check is finished

 *  

 *  @note the completion block is always executed on the main queue

 */

/*

 * 如果检测到图片已经被缓存,那么执行回调block.这个block会永远执行在主线程.也就是你可以在这个回调block里更新ui.

 */

- (void)cachedImageExistsForURL:(NSURL *)url

                     completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;

/**

 *  Async check if image has already been cached on disk only

 *

 *  @param url              image url

 *  @param completionBlock  the block to be executed when the check is finished

 *

 *  @note the completion block is always executed on the main queue

 */

/*

 * 如果检测到图片已经被缓存在磁盘(存且仅存在disk),那么执行回调block.这个block会永远执行在主线程.也就是你可以在这个回调block里更新ui.

 */

- (void)diskImageExistsForURL:(NSURL *)url

                   completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;

/**

 *Return the cache key for a given URL

 */

/*

 * 通过image的url返回image存在缓存里的key.有人会问了,为什么不直接把图片的url当做image的key来使用呢?而是非要对url做一些处理才能当做key.我的解释是,我也不太清楚.可能为了防止重复吧.

 */

- (NSString *)cacheKeyForURL:(NSURL *)url;

@end

#pragma mark - Deprecated

typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionBlock`");

typedef void(^SDWebImageCompletedWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) __deprecated_msg("Block type deprecated. Use `SDWebImageCompletionWithFinishedBlock`");

// 已被废弃

@interface SDWebImageManager (Deprecated)

/**

 *  Downloads the image at the given URL if not present in cache or return the cached version otherwise.

 *

 *  @deprecated This method has been deprecated. Use `downloadImageWithURL:options:progress:completed:`

 */

- (id )downloadWithURL:(NSURL *)url

                                    options:(SDWebImageOptions)options

                                   progress:(SDWebImageDownloaderProgressBlock)progressBlock

                                  completed:(SDWebImageCompletedWithFinishedBlock)completedBlock __deprecated_msg("Method deprecated. Use `downloadImageWithURL:options:progress:completed:`");

@end


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

/*

 * This file is part of the SDWebImage package.

 * (c) Olivier Poitrey  *

 * For the full copyright and license information, please view the LICENSE

 * file that was distributed with this source code.

 */

#import "SDWebImageManager.h"

#import // 内部类.

@interface SDWebImageCombinedOperation : NSObject @property (assign, nonatomic, getter = isCancelled) BOOL cancelled;

@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock;

@property (strong, nonatomic) NSOperation *cacheOperation;

@end

@interface SDWebImageManager ()

@property (strong, nonatomic, readwrite) SDImageCache *imageCache;

@property (strong, nonatomic, readwrite) SDWebImageDownloader *imageDownloader;

@property (strong, nonatomic) NSMutableSet *failedURLs;

@property (strong, nonatomic) NSMutableArray *runningOperations;

@end

@implementation SDWebImageManager

// 利用disptach_once 特性生成一个单例,用烂了的方法.不赘述.

+ (id)sharedManager {

    static dispatch_once_t once;

    static id instance;

    dispatch_once(&once, ^{

        instance = [self new];

    });

    return instance;

}

// 初始化方法.

// 1.获得一个SDImageCache的单例.2.获取一个SDWebImageDownloader的单例.3.新建一个MutableSet来存储下载失败的url.

// 4.新建一个用来存储下载operation的可变数组.

// 为什么不用MutableArray储存下载失败的URL?

// 因为NSSet类有一个特性,就是Hash.实际上NSSet是一个哈希表,哈希表比数组优秀的地方是什么呢?就是查找速度快.查找同样一个元素,哈希表只需要通过key

// 即可取到,而数组至少需要遍历依次.因为SDWebImage里有关失败URL的业务需求是,一个失败的URL只需要储存一次.这样的话Set自然比Array更合适.

- (id)init {

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

        _imageCache = [self createCache];

        _imageDownloader = [SDWebImageDownloader sharedDownloader];

        _failedURLs = [NSMutableSet new];

        _runningOperations = [NSMutableArray new];

    }

    return self;

}

// 获取一个cache的单例

- (SDImageCache *)createCache {

    return [SDImageCache sharedImageCache];

}

// 利用Image的URL生成一个缓存时需要的key.

// 这里有两种情况,第一种是如果检测到cacheKeyFilter不为空时,利用cacheKeyFilter来处理URL生成一个key.

// 如果为空,那么直接返回URL的string内容,当做key.

- (NSString *)cacheKeyForURL:(NSURL *)url {

    if (self.cacheKeyFilter) {

        return self.cacheKeyFilter(url);

    }

    else {

        return [url absoluteString];

    }

}

// 检测一张图片是否已被缓存.

// 首先检测内存缓存是否存在这张图片,如果已有,直接返回yes.

// 如果内存缓存里没有这张图片,那么调用diskImageExistsWithKey这个方法去硬盘缓存里找

- (BOOL)cachedImageExistsForURL:(NSURL *)url {

    NSString *key = [self cacheKeyForURL:url];

    if ([self.imageCache imageFromMemoryCacheForKey:key] != nil) return YES;

    return [self.imageCache diskImageExistsWithKey:key];

}

// 检测硬盘里是否缓存了图片

- (BOOL)diskImageExistsForURL:(NSURL *)url {

    NSString *key = [self cacheKeyForURL:url];

    return [self.imageCache diskImageExistsWithKey:key];

}

// 首先生成一个用来cache 住Image的key(利用key的url生成)

// 然后检测内存缓存里是否已经有这张图片

// 如果已经被缓存,那么再主线程里回调block

// 如果没有检测到,那么调用diskImageExistsWithKey,这个方法会在异步线程里,将图片存到硬盘,当然在存图之前也会检测是否已在硬盘缓存图片.

- (void)cachedImageExistsForURL:(NSURL *)url

                     completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {

    NSString *key = [self cacheKeyForURL:url];

    BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);

    if (isInMemoryCache) {

        // making sure we call the completion block on the main queue

        dispatch_async(dispatch_get_main_queue(), ^{

            if (completionBlock) {

                completionBlock(YES);

            }

        });

        return;

    }

    [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {

        // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch

        if (completionBlock) {

            completionBlock(isInDiskCache);

        }

    }];

}

//将图片存入硬盘

- (void)diskImageExistsForURL:(NSURL *)url

                   completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {

    NSString *key = [self cacheKeyForURL:url];

    [self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {

        // the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch

        if (completionBlock) {

            completionBlock(isInDiskCache);

        }

    }];

}

// 通过url建立一个operation用来下载图片.

- (id )downloadImageWithURL:(NSURL *)url

                                         options:(SDWebImageOptions)options

                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock

                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {

    // Invoking this method without a completedBlock is pointless

    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won‘t

    // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.

    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;

    }

    __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];

    __weak SDWebImageCombinedOperation *weakOperation = operation;

    BOOL isFailedUrl = NO;

    // 创建一个互斥锁防止现在有别的线程修改failedURLs.

    // 判断这个url是否是fail过的.如果url failed过的那么isFailedUrl就是true

    @synchronized (self.failedURLs) {

        isFailedUrl = [self.failedURLs containsObject:url];

    }

    // 如果url不存在那么直接返回一个block,如果url存在.那么继续进行判断.

    // options与SDWebImageRetryFailed这个option进行按位与操作.判断用户的options里是否有retry这个option.

    // 如果用户的options里没有retry这个选项并且isFaileUrl 是true.那么就回调一个error的block.

    if (!url || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {

        dispatch_main_sync_safe(^{

            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];

            completedBlock(nil, error, SDImageCacheTypeNone, YES, url);

        });

        return operation;

    }

    // 创建一个互斥锁防止现在有别的线程修改runningOperations.

    @synchronized (self.runningOperations) {

        [self.runningOperations addObject:operation];

    }

    NSString *key = [self cacheKeyForURL:url];

    // cacheOperation应该是一个用来下载图片并且缓存的operation

    operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {

        // 判断operation这时候有没有执行cancel操作,如果cancel掉了就把这个operation从我们的operation数组里remove掉然后return

        if (operation.isCancelled) {

            @synchronized (self.runningOperations) {

                [self.runningOperations removeObject:operation];

            }

            return;

        }

        if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {

            if (image && options & SDWebImageRefreshCached) {

                dispatch_main_sync_safe(^{

                    // If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image

                    // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.

                    completedBlock(image, nil, cacheType, YES, url);

                });

            }

            // download if no image or requested to refresh anyway, and download allowed by delegate

            // 下面都是判断我们的options里包含哪些SDWebImageOptions,然后给我们的downloaderOptions相应的添加对应的SDWebImageDownloaderOptions. downloaderOptions |= SDWebImageDownloaderLowPriority这种表达式的意思等同于

            // downloaderOptions = downloaderOptions | SDWebImageDownloaderLowPriority

            SDWebImageDownloaderOptions downloaderOptions = 0;

            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;

            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;

            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;

            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;

            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;

            if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;

            if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;

            if (image && options & SDWebImageRefreshCached) {

                // force progressive off if image already cached but forced refreshing

                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;

                // ignore image read from NSURLCache if image if cached but force refreshing

                downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;

            }

            // 调用imageDownloader去下载image并且返回执行这个request的download的operation

            id  subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {

                if (weakOperation.isCancelled) {

                    // Do nothing if the operation was cancelled

                    // See #699 for more details

                    // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data

                }

                else if (error) {

                    dispatch_main_sync_safe(^{

                        if (!weakOperation.isCancelled) {

                            completedBlock(nil, error, SDImageCacheTypeNone, finished, url);

                        }

                    });

                    if (error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut) {

                        @synchronized (self.failedURLs) {

                            [self.failedURLs addObject:url];

                        }

                    }

                }

                else {

                    if ((options & SDWebImageRetryFailed)) {

                        @synchronized (self.failedURLs) {

                            [self.failedURLs removeObject:url];

                        }

                    }

                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    if (options & SDWebImageRefreshCached && image && !downloadedImage) {

                        // Image refresh hit the NSURLCache cache, do not call the completion block

                    }

                    else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {

                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

                            UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];

                            if (transformedImage && finished) {

                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];

                                [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk];

                            }

                            dispatch_main_sync_safe(^{

                                if (!weakOperation.isCancelled) {

                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);

                                }

                            });

                        });

                    }

                    else {

                        if (downloadedImage && finished) {

                            [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];

                        }

                        dispatch_main_sync_safe(^{

                            if (!weakOperation.isCancelled) {

                                completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);

                            }

                        });

                    }

                }

                if (finished) {

                    @synchronized (self.runningOperations) {

                        [self.runningOperations removeObject:operation];

                    }

                }

            }];

            operation.cancelBlock = ^{

                [subOperation cancel];

                @synchronized (self.runningOperations) {

                    [self.runningOperations removeObject:weakOperation];

                }

            };

        }

        else if (image) {

            dispatch_main_sync_safe(^{

                if (!weakOperation.isCancelled) {

                    completedBlock(image, nil, cacheType, YES, url);

                }

            });

            @synchronized (self.runningOperations) {

                [self.runningOperations removeObject:operation];

            }

        }

        else {

            // Image not in cache and download disallowed by delegate

            dispatch_main_sync_safe(^{

                if (!weakOperation.isCancelled) {

                    completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);

                }

            });

            @synchronized (self.runningOperations) {

                [self.runningOperations removeObject:operation];

            }

        }

    }];

    return operation;

}

- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url {

    if (image && url) {

        NSString *key = [self cacheKeyForURL:url];

        [self.imageCache storeImage:image forKey:key toDisk:YES];

    }

}

// cancel掉所有正在执行的operation

- (void)cancelAll {

    @synchronized (self.runningOperations) {

        NSArray *copiedOperations = [self.runningOperations copy];

        [copiedOperations makeObjectsPerformSelector:@selector(cancel)];

        [self.runningOperations removeObjectsInArray:copiedOperations];

    }

}

// 判断是否有正在运行的operation

- (BOOL)isRunning {

    return self.runningOperations.count > 0;

}

@end

@implementation SDWebImageCombinedOperation

- (void)setCancelBlock:(SDWebImageNoParamsBlock)cancelBlock {

    // check if the operation is already cancelled, then we just call the cancelBlock

    if (self.isCancelled) {

        if (cancelBlock) {

            cancelBlock();

        }

        _cancelBlock = nil; // don‘t forget to nil the cancelBlock, otherwise we will get crashes

    else {

        _cancelBlock = [cancelBlock copy];

    }

}

- (void)cancel {

    self.cancelled = YES;

    if (self.cacheOperation) {

        [self.cacheOperation cancel];

        self.cacheOperation = nil;

    }

    if (self.cancelBlock) {

        self.cancelBlock();

        // TODO: this is a temporary fix to #809.

        // Until we can figure the exact cause of the crash, going with the ivar instead of the setter

//        self.cancelBlock = nil;

        _cancelBlock = nil;

    }

}

@end

@implementation SDWebImageManager (Deprecated)

// deprecated method, uses the non deprecated method

// adapter for the completion block

- (id )downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedWithFinishedBlock)completedBlock {

    return [self downloadImageWithURL:url

                              options:options

                             progress:progressBlock

                            completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {

                                if (completedBlock) {

                                    completedBlock(image, error, cacheType, finished);

                                }

                            }];

}

@end

下面来简单的解释一下OC里枚举的两种类型.

NS_ENUM和NS_OPTIONS

本质上是一样的都是枚举.

我举个例子.


1

2

3

4

5

6

7

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {

    UIViewAnimationTransitionNone,

    UIViewAnimationTransitionFlipFromLeft,

    UIViewAnimationTransitionFlipFromRight,

    UIViewAnimationTransitionCurlUp,

    UIViewAnimationTransitionCurlDown,

};


1

2

3

4

5

6

7

8

9

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {

    UIViewAutoresizingNone                 = 0,

    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,

    UIViewAutoresizingFlexibleWidth        = 1 << 1,

    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,

    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,

    UIViewAutoresizingFlexibleHeight       = 1 << 4,

    UIViewAutoresizingFlexibleBottomMargin = 1 << 5

};

应该可以看出一些苗头.

NS_ENUM这种声明出来的东西大部分是单选. NS_OPTIONS声明出来的大部分是多选.

像UIViewAnimationTransition这种在用的时候肯定是只能选一种效果,你要么从左翻到右,要么从右翻到左,你做动画的时候总不能同一时刻让他同时从左到右,又从右到左翻,对吧.

而UIViewAutosizing就不一样了.我要是让子view的宽高和父View一样,那么autoviewsizing的选项肯定是类似于这种.UIViewAutoresizingFlexibleWidth  | UIViewAutoresizingFlexibleHeight

没错吧,意思就是两个options的我都得选才行.

那么如果有个场景让我判断当前的view的Autoresizing有哪几个.我怎么判断呢?

很简单.用按位与操作就行了.

假设 autoResizings = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;

我们判断autoResizings里是否有UIViewAutoresizingFlexibleLeftMargin的时候只需要if(autoResizings & UIViewAutoresizingFlexibleLeftMargin)是否为true就可以了.

用二进制表示的话(这里不用care NSUInteger到底是几位的.就表示这么个意思)


1

2

3

UIViewAutoresizingFlexibleLeftMargin = 00000001

UIViewAutoresizingFlexibleWidth = 00000010

UIViewAutoresizingFlexibleRightMargin = 00000100

所以根据上面的表达式,我们的autoResizings = 00000111.

那么执行按位与操作是这样的.


1

2

3

 00000111

&00000001   

结果就是00000001,为true.表示含有这个选项.

 

时间: 2024-10-13 16:25:49

SDWebImage源码解析之SDWebImageManager的注解的相关文章

SDWebImage源码解析(二)

源码来源: https://github.com/rs/SDWebImage 版本: 3.7 SDWebImage是一个开源的第三方库,它提供了UIImageView的一个分类,以支持从远程服务器下载并缓存图片的功能.它具有以下功能: 提供UIImageView的一个分类,以支持网络图片的加载与缓存管理 一个异步的图片加载器 一个异步的内存+磁盘图片缓存 支持GIF图片 支持WebP图片 后台图片解压缩处理 确保同一个URL的图片不被下载多次 确保虚假的URL不会被反复加载 确保下载及缓存时,主

SDWebImage源码解析

在开发项目的过程中会用到很多第三方库,比如AFNetWorking,SDWebImage,FMDB等,但一直都没去好好的研究一下,最近刚好项目不是太紧,闲下来可以给自己充充电,先研究一下SDWebImage的底层实现,源码地址:SDWebImage 先介绍一下SDWebImage,我们使用较多的是它提供的UIImageView分类,支持从远程服务器下载并缓存图片.自从iOS5.0开始,NSURLCache也可以处理磁盘缓存,那么SDWebImage的优势在哪?首先NSURLCache是缓存原始数

【原】SDWebImage源码阅读(五)

[原]SDWebImage源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 前面的代码并没有特意去讲SDWebImage的缓存机制,主要是想单独开一章节专门讲解缓存.之前我们也遇到一些缓存的属性和方法,比如storeImage.queryDiskCacheForKey.memCache等等. SDWebImage的缓存分为两个部分,一个内存缓存,使用NSCache实现,另一个就是硬盘缓存(disk),使用NSFileManager实现. 不过这么多函数,

iOS开源库源码解析之SDWebImage

来自Leo的原创博客,转载请著名出处 我的stackoverflow 这个源码解析系列的文章 AsnycDispalyKit SDWebImage(本文) 前言 SDWebImage是iOS开发中十分流行的库,大多数的开发者在下载图片或者加载网络图片并且本地缓存的时候,都会用这个框架.这个框架相对来说,源代码还是比较少的.本文会详细的讲解这些类的架构关系和原理. 本文会先介绍类的整体架构关系,先有一个宏观的认识.然后讲解sd_setImageWithURL的加载逻辑,因为这是SDWebImage

eclipse中导入jdk源码、SpringMVC注解@RequestParam、SpringMVC文件上传源码解析、ajax上传excel文件

eclipse中导入jdk源码:http://blog.csdn.net/evolly/article/details/18403321, http://www.codingwhy.com/view/799.html. ------------------------------- SpringMVC注解@RequestParam:http://825635381.iteye.com/blog/2196911. --------------------------- SpringMVC文件上传源

Android xUtils3源码解析之注解模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 初始化 public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { su

异步任务spring @Async注解源码解析

1.引子 开启异步任务使用方法: 1).方法上加@Async注解 2).启动类或者配置类上@EnableAsync 2.源码解析 虽然spring5已经出来了,但是我们还是使用的spring4,本文就根据spring-context-4.3.14.RELEASE.jar来分析源码. 2.1.@Async org.springframework.scheduling.annotation.Async 源码注释翻译: 1 /** 2 * Annotation that marks a method

Spring @Import注解源码解析

简介 Spring 3.0之前,创建Bean可以通过xml配置文件与扫描特定包下面的类来将类注入到Spring IOC容器内.而在Spring 3.0之后提供了JavaConfig的方式,也就是将IOC容器里Bean的元信息以java代码的方式进行描述.我们可以通过@Configuration与@Bean这两个注解配合使用来将原来配置在xml文件里的bean通过java代码的方式进行描述 @Import注解提供了@Bean注解的功能,同时还有xml配置文件里标签组织多个分散的xml文件的功能,当

SDWebImage源码阅读-第三篇

这一篇讲讲不常用的一些方法. 1 sd_setImageWithPreviousCachedImageWithURL: placeholderImage: options: progress: completed: 取得上次缓存的图片,然后作为占位图的参数再次进行一次图片设置. - (void)sd_setImageWithPreviousCachedImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options