zoukankan      html  css  js  c++  java
  • SDWebImage源码解读 之 SDWebImageCompat

    第三篇

    前言

    本篇主要解读SDWebImage的配置文件。正如compat的定义,该配置文件主要是兼容Apple的其他设备。也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的意义。这篇文章的重点是抽取出对于iOS很重要的用法,能够在项目开发中提高效率。

    #import <TargetConditionals.h>

    导入这个头文件,我们就能访问系统提供的配置选项了,我们接下来会对该文件出现的配置属性做出解释。

    _OBJC_GC_

    #ifdef __OBJC_GC__
        #error SDWebImage does not support Objective-C Garbage Collection
    #endif
    

    SDWebImage不支持垃圾回收机制,垃圾回收(Gargage-collection)是Objective-c提供的一种自动内存回收机制。在iPad/iPhone环境中不支持垃圾回收功能。
    当启动这个功能后,所有的retain,autorelease,release和dealloc方法都将被系统忽略。

    SD_MAC

    // Apple's defines from TargetConditionals.h are a bit weird.
    // Seems like TARGET_OS_MAC is always defined (on all platforms).
    // To determine if we are running on OSX, we can only relly on TARGET_OS_IPHONE=0 and all the other platforms
    #if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
        #define SD_MAC 1
    #else
        #define SD_MAC 0
    #endif
    

    该指令主要用于判断当前平台是不是MAC,单纯使用TARGET_OS_MAC是不靠谱的。这样判断的缺点是,当Apple出现新的平台时,判断条件要修改。

    • TARGET_OS_IPHONE
    • TARGET_OS_IOS
    • TARGET_OS_TV
    • TARGET_OS_WATCH

    SD_UIKIT

    // iOS and tvOS are very similar, UIKit exists on both platforms
    // Note: watchOS also has UIKit, but it's very limited
    #if TARGET_OS_IOS || TARGET_OS_TV
        #define SD_UIKIT 1
    #else
        #define SD_UIKIT 0
    #endif
    

    iOS 和 tvOS 是非常相似的,UIKit在这两个平台中都存在,但是watchOS在使用UIKit时,是受限的。因此我们定义SD_UIKIT为真的条件是iOS 和 tvOS这两个平台。至于为什么要定义SD_UIKIT后边会解释的。

    SD_IOS

    #if TARGET_OS_IOS
        #define SD_IOS 1
    #else
        #define SD_IOS 0
    #endif
    

    SD_TV

    #if TARGET_OS_TV
        #define SD_TV 1
    #else
        #define SD_TV 0
    #endif
    

    SD_WATCH

    #if TARGET_OS_WATCH
        #define SD_WATCH 1
    #else
        #define SD_WATCH 0
    #endif
    

    平台兼容适配

    #if SD_MAC
        #import <AppKit/AppKit.h>
        #ifndef UIImage
            #define UIImage NSImage
        #endif
        #ifndef UIImageView
            #define UIImageView NSImageView
        #endif
        #ifndef UIView
            #define UIView NSView
        #endif
    #else
        #if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
            #error SDWebImage doesn't support Deployment Target version < 5.0
        #endif
    
        #if SD_UIKIT
            #import <UIKit/UIKit.h>
        #endif
        #if SD_WATCH
            #import <WatchKit/WatchKit.h>
        #endif
    #endif
    

    观察上边的代码,可以发现,在MAC平台上进行了如下的转换,这算是一个编程技巧:

    • UIImage -----> NSImage
    • UIImageView -----> NSImageView
    • UIView -----> NSView

    SDWebImage不支持5.0以下的iOS版本。SD_UIKIT为真时,导入UIKit,SD_WATCH为真时,导入WatchKit。

    基础设置

    #ifndef NS_ENUM
    #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
    #endif
    
    #ifndef NS_OPTIONS
    #define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
    #endif
    
    #if OS_OBJECT_USE_OBJC
        #undef SDDispatchQueueRelease
        #undef SDDispatchQueueSetterSementics
        #define SDDispatchQueueRelease(q)
        #define SDDispatchQueueSetterSementics strong
    #else
        #undef SDDispatchQueueRelease
        #undef SDDispatchQueueSetterSementics
        #define SDDispatchQueueRelease(q) (dispatch_release(q))
        #define SDDispatchQueueSetterSementics assign
    #endif
    

    接口

    extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
    
    typedef void(^SDWebImageNoParamsBlock)();
    
    extern NSString *const SDWebImageErrorDomain;
    
    static int64_t kAsyncTestTimeout = 5;
    

    dispatch_main_async_safe

    我们来看看这个宏,按理说我使用dispatch_main_async就可以了,为什么要加入safe呢?那么这个safe主要是解决那些不安全的问题呢?

    #ifndef dispatch_main_async_safe
    #define dispatch_main_async_safe(block)
        if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {
            block();
        } else {
            dispatch_async(dispatch_get_main_queue(), block);
        }
    #endif
    
    • 第一,我们可以像这样在定义宏的时候使用换行,但需要添加 操作符
    • 第二,如果当前线程已经是主线程了,那么在调用dispatch_async(dispatch_get_main_queue(), block)有可能会出现crash
    • 第三,如果当前线程是主线程,直接调用,如果不是,调用dispatch_async(dispatch_get_main_queue(), block)

    UIImage *SDScaledImageForKey(NSString *key, UIImage *image)

    inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) {
        if (!image) {
            return nil;
        }
        
    #if SD_MAC
        return image;
    #elif SD_UIKIT || SD_WATCH
        if ((image.images).count > 0) {
            NSMutableArray<UIImage *> *scaledImages = [NSMutableArray array];
    
            for (UIImage *tempImage in image.images) {
                [scaledImages addObject:SDScaledImageForKey(key, tempImage)];
            }
    
            return [UIImage animatedImageWithImages:scaledImages duration:image.duration];
        }
        else {
    #if SD_WATCH
            if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) {
    #elif SD_UIKIT
            if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    #endif
                CGFloat scale = 1;
                if (key.length >= 8) {
                    NSRange range = [key rangeOfString:@"@2x."];
                    if (range.location != NSNotFound) {
                        scale = 2.0;
                    }
                    
                    range = [key rangeOfString:@"@3x."];
                    if (range.location != NSNotFound) {
                        scale = 3.0;
                    }
                }
    
                UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
                image = scaledImage;
            }
            return image;
        }
    #endif
    }
    

    这个方法是根据key来修改图片的尺寸,需要注意的地方有:

    • inline 内联函数
    • 递归函数

    总结

    通过对该配置文件的理解,让我对配置相关的信息更加了解了,我产生了收集这些预编译的想法,生成一个内容比较丰富的文件,能够很好地让别人拿过去用。代码应该写的简洁,稳定。

    1. SDWebImage源码解读 之 NSData+ImageContentType 简书 博客园
    2. SDWebImage源码解读 之 UIImage+GIF 简书 博客园
  • 相关阅读:
    作业9
    第八次作业特征选择
    大数据应用技术课程实践--选题与实践方案
    机器学习——15 手写数字识别-小数据集
    机器学习——14 深度学习-卷积
    机器学习——13-垃圾邮件分类2
    机器学习——12.朴素贝叶斯-垃圾邮件分类
    机器学习——11.分类与监督学习,朴素贝叶斯分类算法
    机器学习——09、主成分分析
    机器学习——08、特征选择
  • 原文地址:https://www.cnblogs.com/machao/p/6137517.html
Copyright © 2011-2022 走看看