SDWebImage知识点总结二

2018-05-28  本文已影响25人  woniu
“宜未雨而绸缪;毋临渴而掘井。”凡事早做打算,方为上上之策!------前言

一、SDWebImage中的weakself&strongself

//#import "SDWebImageDownloaderOperation.h"
if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
            __weak __typeof__ (self) wself = self;
            UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
            self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
                __strong __typeof (wself) sself = wself;

                if (sself) {
                    [sself cancel];

                    [app endBackgroundTask:sself.backgroundTaskId];
                    sself.backgroundTaskId = UIBackgroundTaskInvalid;
                }
            }];
        }

疑问:

1、为什么使用weakSelf?

因为Block是一个结构体,它会将一个全局变量保存为一个属性(__strong),而self强引用了Block这会造成循环引用,所以使用__weak修饰weakSelf。

2、为什么在Block里面使用strongSelf?

为了保证block在执行完毕之前self不会被释放,而strongSelf是为了保证Block内部执行的时候不会被释放,但是存在执行前就已经被释放的情况,导致strongSelf=nil。注意判空处理,防止出现崩溃。typeof是编译时确定变量类型,所以这里写self 不会被循环引用。

总结:

外部使用了weakSelf,里面使用strongSelf却不会造成循环,究其原因就是因为weakSelf是block截获的属性,而strongSelf是一个局部变量会在“函数”执行完释放。

扩展1:

1、__block可以让block修改局部变量,而__weak不能。
2、MRC中__block是不会引起retain;但在ARC中__block则会引起retain,因为,block也是一个强引用,引起循环引用,会引起循环引用。所以ARC中应该使用__weak。

扩展2:

[UIView animateWithDuration:duration animations:^{ [self.superview layoutIfNeeded]; }]; 

2、NSNotification

[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification" 
                                                 object:nil 
                          queue:[NSOperationQueue mainQueue]
                                             usingBlock:^(NSNotification * notification) {
                                                   self.someProperty = xyz; }]; 

3、NSOperation

[[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.someProperty = xyz; }]; 

二、SDWebImage为类别添加属性

类别本身不允许为已有的类添加属性,及时你使用@property添加属性,这个属性也是无法使用的,因为它是不会生成setter/getter方法。为了解决这个问题,我们可以使用runtime来为类别添加新的属性并手动生成setter/getter方法。

/*
     objc_AssociationPolicy参数使用的策略:
     OBJC_ASSOCIATION_ASSIGN;            //assign策略
     OBJC_ASSOCIATION_COPY_NONATOMIC;    //copy策略
     OBJC_ASSOCIATION_RETAIN_NONATOMIC;  // retain策略
     
     OBJC_ASSOCIATION_RETAIN;
     OBJC_ASSOCIATION_COPY;
     */
//#import "UIImageView+WebCache.h"
static char imageURLKey;
static char TAG_ACTIVITY_INDICATOR;
static char TAG_ACTIVITY_STYLE;
static char TAG_ACTIVITY_SHOW;

- (UIActivityIndicatorView *)activityIndicator {
    return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR);
}
    // 参数1:源对象
    // 参数2:关联时用来标记属性的key(因为可能要添加很多属性)
    // 参数3:关联的对象
    // 参数4:关联策略
    // 关联方法
- (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
    objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN);
}

- (void)setShowActivityIndicatorView:(BOOL)show{
    objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, [NSNumber numberWithBool:show], OBJC_ASSOCIATION_RETAIN);
}

- (BOOL)showActivityIndicatorView{
    return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue];
}

runtime增加对象属性

Category为什么可以添加方法,而不可以添加成员变量?
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE;  // 父类
    const char *name                        OBJC2_UNAVAILABLE;  // 类名
    long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
    long info                               OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识
    long instance_size                      OBJC2_UNAVAILABLE;  // 该类的实例变量大小
    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 该类的成员变量链表
    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定义的链表
    struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法缓存
    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 协议链表
#endif
} OBJC2_UNAVAILABLE;

三、SDWebImage中的断言NSAssert

在iOS开发中,可以使用宏NSAssert()在程序中进行断言处理。NSAssert()使用正确,可以帮助开发者尽快定位bug。

//#import "SDWebImageManager.h"
NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
- (void)printMyName:(NSString *)myName  
{  
    NSAssert(myName != nil, @"名字不能为空!");  
    NSLog(@"My name is %@.",myName);  
}  

当传给函数的参数(myName)为空时,断言将被执行,程序Crash,并打印出断言中的描述信息。本例中,在控制台打印出了如下的日志:

2018-05-29 11:25:46.846909+0800 autoReleasePoolTest[78930:6611559] *** Assertion failure in -[ViewController printMyName:], /Users/zhangzhangkai/Library/Autosave Information/autoReleasePoolTest/autoReleasePoolTest/ViewController.m:55
2018-05-29 11:25:46.861768+0800 autoReleasePoolTest[78930:6611559] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '名字不能为空!'

断言(NSAssert)的使用

四、SDWebImage中GCD的使用

GCD实现线程同步的方法:
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue;

 __block NSArray *callbacksForURL;
  dispatch_barrier_sync(sself.barrierQueue, ^{
   callbacksForURL = [sself.URLCallbacks[url] copy];
    if (finished) {
    [sself.URLCallbacks removeObjectForKey:url];
     }
    });

【iOS沉思录】GCD实现线程同步的方法
GCD剖析

五、内联函数inline

inline定义:内联函数是指用inline关键字修饰的函数。内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。
inline内联函数的说明
为什么inline能取代宏?
extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image);

//#import "SDWebImageCompat.h"
inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image) {
    if (!image) {
        return nil;
    }
    
    if ([image.images count] > 0) {
        NSMutableArray *scaledImages = [NSMutableArray array];

        for (UIImage *tempImage in image.images) {
            [scaledImages addObject:SDScaledImageForKey(key, tempImage)];
        }

        return [UIImage animatedImageWithImages:scaledImages duration:image.duration];
    }
    else {
        if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
            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;
    }
}

六、iOS网络请求NSURLSession

NSURLSession

使用方法介绍

七、clang diagnostic的使用

SDWebImage中SDWebImageDownloader.m中的设置如下:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
#pragma clang diagnostic pop

看到这里我也是一头雾水,不明所以。查过资料之后发现,clang为诊断设置,辅助我们编码。
-Warc-performSelector-leaks的解释为“performSelector may cause a leak because its selector is unknown”因为方法位置,所以可能会导致泄漏。这里就是忽略这个告警,SDWebImage作者八成是个处女座。

#pragma clang diagnostic push  
#pragma clang diagnostic ignored "-相关命令"  
    // 你自己的代码  
#pragma clang diagnostic pop  

栗子1:忽略弃用的警告⚠️

//1、如果直接写已经废弃的方法会报警
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"" message:@"" delegate:nil cancelButtonTitle:@"" otherButtonTitles:@"", nil];
[alertView show]

//2、忽略方法弃用告警    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    UIAlertView *alertViewTmp = [[UIAlertView alloc]initWithTitle:@"" message:@"" delegate:nil cancelButtonTitle:@"" otherButtonTitles:@"", nil];
    [alertViewTmp show];
#pragma clang diagnostic pop

栗子2:忽略不兼容指针类型⚠️

//   不兼容指针类型 
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
    //
#pragma clang diagnostic pop

栗子3:循环引用⚠️

break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
//    self.completionBlock = ^ {
//        ...
//    };
#pragma clang diagnostic pop

栗子4:未使用变量 ⚠️

// 未使用变量   
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
    int a;
#pragma clang diagnostic pop
告警使用列表

clang diagnostic的使用
告警使用列表注释

上一篇 下一篇

猜你喜欢

热点阅读