UIView+WebCache
SDWebImageManager
SDImageCache
SDWebImageDownloader
SDWebImageDownloaderOperation
SDWebImagePrefetcher
FLAnimatedImage
UIView+WebCache
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs;
- (void)sd_cancelCurrentAnimationImagesLoad;
下载图片有数个方法定义, 见UIView+WebCache.h, 最终都调用了UIView+WebCache中的 sd_internalSetImageWithURL 这个方法.
sd_internalSetImageWithURL 方法做的事情:
SDWebImageManager下载图片, 并将方法返回的 operation 与当前 View 绑定.Tips
dispatch_main_async_safe 定义了在主线程进行UI操作的宏:
#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
weakSelf 为 nil 时候直接结束避免崩溃或者其他错误.
SDImageCache从缓存中读取Image.NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url];
UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:key];
[self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock];
Tips : 在执行下载的过程中, 如果找到了缓存, 就忽略placeholder, 避免一次无效操作.
SDWebImageManager下载图片, 并将方法返回的 operation 装入数组,再将这个数组与当前 View 绑定.PS. 这也解释了 - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key 为什么会有如下看起来很奇怪的代码, operation的类型并不是固定的.
SDOperationsDictionary *operationDictionary = [self operationDictionary];
id operations = operationDictionary[key];
if (operations) {
if ([operations isKindOfClass:[NSArray class]]) {
for (id <SDWebImageOperation> operation in operations) {
if (operation) {
[operation cancel];
}
}
} else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
[(id<SDWebImageOperation>) operations cancel];
}
[operationDictionary removeObjectForKey:key];
}
Tips : [self operationDictionary] 使用 Runtime 为实例增加了变量.
Question : 如何保证下载的顺序?
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key
Tips : 关于id <SDWebImageOperation> operation解释:
每个 operaton 都有实现一个 - (void)cancel; 方法, 这个是在SDWebImageOperation协议中定义, 无论是什么类型实例, 只要实现了该协议, 都可以统一调用,详细解释可以搜索iOS+面向接口编程.
在 UIView+WebCache 模块中, 只做了一些简单的操作, 定义好了与 UIKit 交互接口, 下载与取消交给了 SDWebImageManager 处理, 缓存交给了 SDImageCache 处理.
SDWebImageManager
@property (strong, nonatomic, readonly, nullable) SDImageCache *imageCache;
@property (strong, nonatomic, readonly, nullable) SDWebImageDownloader *imageDownloader;
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock;
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url;
- (void)cancelAll;
- (BOOL)isRunning;
- (void)cachedImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
- (void)diskImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;
[SDImageCache sharedImageCache] 引用到了 SDImageCache 的单例.[SDWebImageDownloader sharedImageCache] 引用到了 SDWebImageDownloader 的单例.SDWebImageCombinedOperation 实例, 也就是上文提到过的一个operation, 然后将改operation加入self.runningOperations方便管理;[SDImageCache queryCacheOperationForKey: done:] 生成了一个NSOperation实例 并赋值给了上一步所使用的operation的cacheOperation属性, 方便执行cancel方法.SDImageCache 中先在内存中找图片的缓存,找到直接执行回调,若没找到则在硬盘上找缓存,若找到切可以在内存做缓存,则在内存中做缓存, 然后执行回调.queryCacheOperationForKey方法的回调中,如果发现当前Operation被取消了,则将该operation从self.runningOperations移除,并使用@synchronized保证线程安全.[SDWebImageDownloader downloadImageWithURL:options:progress:completed:]方法下载图片, 并将该方法换回的token标志绑定到operation的cancelBlock中, 方便取消下载请求;imageManager:transformDownloadedImage:withURL:方便使用者在下载完成时候立刻对图片做一些自定义处理,再将该图片进行缓存. 最后执行回调并移除改operation.[SDImageCache storeImage:forKey:toDisk:completion:]
self.runningOperations 的读写, 因此用 @synchronized 保证线程安全.self.runningOperations, 并使用 [NSArray makeObjectsPerformSelector:] 方法取消队列中所有的操作.self.runningOperations移除.@synchronized访问self.runningOperations
[SDImageCache imageFromMemoryCacheForKey:]和 [SDImageCache diskImageExistsWithKey:completion:] 方法检查是否在内存和硬盘上缓存.[SDImageCache diskImageExistsWithKey:completion:] 方法检查是是否在硬盘上缓存.self.cacheKeyFilter自定义存储Key,则使用该回调获取用于缓存索引的Key从SDWebImage可以看出作者考虑到了很多一般开发者不会去考虑的事情, 简单的如线程安全, 更细致的如imageManager:transformDownloadedImage:withURL:方法, 方便使用SDWebImage的人在使用之前先处理, 再缓存, 一个个简单的应用场景是用户想对一张网络图片进行模糊处理, 一般的步骤是先用SDWebImage下载,然后自行模糊处理,再展示. 但如果有大量图片要处理, 又涉及到tableView的复用问题, 为了提高性能, 使用者要自己对模糊之后的图片做缓存, 优化缓存策略和IO潜在地问题等等. 实际上SDWebImage 已经可以处理这个问题而不需要使用者再去考虑.
SDImageCache
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
- (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
- (void)clearMemory;
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
diskCacheDirectory:(nonnull NSString *)directory;
初始化各个属性:
@property (strong, nonatomic, nullable) dispatch_queue_t ioQueue;
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
生成的一个串行队列,专用于IO操作. 不再使用的时候应该使用dispatch_release释放队列.
@property (strong, nonatomic, nonnull) NSCache *memCache;
NSCache是iOS系统提供的缓存类,通过键值对对需要缓存的对象作强引用来达到缓存的目的.
NSFileManager *_fileManager;
dispatch_sync(_ioQueue, ^{
_fileManager = [NSFileManager new];
});
注意,生成_fileManager在ioQueue中,并且是是一个同步操作, 之后_fileManager都要在ioQueue中进行.
SDImageCacheConfig判断是否在在内存中缓存,使用[NSCache setObject:forKey:cost:]方法缓存.toDisk参数判断是否在磁盘中缓存,在ioQueue中调用[self storeImageDataToDisk:data forKey:key];缓存到硬盘,使用@autoreleasepool释放临时变量.Tips
NSCache 有最大缓存容积的设置totalCostLimit, 但是这个设置只有在设置缓存的时候指定要缓存对象占用的字节数(cost)才能生效. 但是对象的内存占用计算十分复杂, SDWebImage只是给出了一个大致值image.size.height * image.size.width * image.scale * image.scale;.
此方法只能在ioQueue中调用,奇怪的是SDImageCache并没有暴露ioQueue访问, 因此, 将此方法暴露在.h文件是没有意义的.
[NSfileManager createFileAtPath:contents:attributes:]将下载下来的图片的原始二进制数据写磁盘在某个版本之前, 硬盘缓存没有文件后缀名, 为了兼容, 要做两次查找
imageFromMemoryCacheForKey, 并用结果执行回调, 注意这一步是同步操作, 因此不需要 NSOperation 来取消操作, 故返回nil.diskImageDataBySearchingAllPathsForKey在硬盘找缓存, 如果找到并且条件允许, 在内存缓存该图片. 这一步要在ioQueue中异步执行, 可以利用 NSOperation 取消这一步操作. 因此在执行回调后返回该NSOperation.self.memCache读取, 可能情况是没有缓存-返回nil, 有缓存但是缓存已经被释放-返回nil, 或是寻找缓存命中-返回目标图片.UIImage *image = [UIImage sd_imageWithData:data];image = [self scaledImageForKey:key image:image];生成目标图片,若允许,在内存中缓存该图片.理论上来说, 这句话放在ioQueue中执行会好一些, 猜测可能是需要同步执行
self.memCache所有对象Tips : [[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]这个比较挺有意思.
这一个模块中,有内存与硬盘两级缓存, NSCache 在系统级别保证了线程安全,相对来说处理容易. 但是IO操作本身较为耗时, 单独创建一个队列作为ioQueue来进行IO操作, 达到在硬盘上缓存的目的.
SDWebImageDownloader
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration NS_DESIGNATED_INITIALIZER;
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field;
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
- (void)cancel:(nullable SDWebImageDownloadToken *)token;
- (void)setSuspended:(BOOL)suspended;
- (void)cancelAllDownloads;
_downloadQueue 是一个NSOperationQueue实例, 每个URL的请依赖这个queue进行管理._URLOperations 用于存储所有的operation实例, 每个URL对应一个operation._barrierQueue 是一个并行队列, operation的创建于取消都在这个队列中完成.self.session 是用于网络请求的NSURLSession组件, 所有的operation对这个session保持了弱引用.addProgressCallback.dispatch_barrier_sync方法, 这是一个同步方法, 但是参数self.barrierQueue是一个并发队列, 因此当前线程会等待bolck中执行完(由于使用的是dispatch_barrier_sync, 而不是dispatch_sync,所以当前block也会等待self.barrierQueue中已经添加的任务执行完).addProgressCallback最后一个参数createCallback(), 并返回一个operation, 注意,执行createCallback又回到了上一层方法downloadImageWithURL方法中.downloadImageWithURL方法, 首先组装好了一个request, 然后生成了一个SDWebImageDownloaderOperation或者其子类的的实例, 并将这个operaion加入了self.downloadQueue队列中. 如果这个队列严格采用LIFO(是栈不是队), 那么上一个加入的operation要依赖于这个operation, 用[sself.lastAddedOperation addDependency:operation];达成目的. 最后返回这个operation. 然后又回到了addProgressCallback这个方法. addProgressCallback方法后,执行[operation addHandlersForProgress:progressBlock completed:completedBlock]将两个Block绑定到operation中, 复制使用的[NSBlock copy]方法, 避免不必要的引用. 注意这儿同一个url可能被请求多次, 因此一个url绑定一个operation, 一个operation绑定多个执行回调
Tips : 怎么开始下载的? SDWebImageDownloaderOperation继承了NSOperation, 并重写了start()方法, 并在start()方法中调用了[self.dataTask resume];开始下载.
self.barrierQueue 异步执行.[SDWebImageDownloaderOperation cancel:]方法.Tips : [SDWebImageDownloaderOperation cancel:]首先将token对应的callback移除掉. 当所有的callbacl都移除掉之后, 会调用父类NSOperation的cancel方法, 这会将isCancelled属性置为YES, 在start方法调用的时候就不会真正执行. 最后调用[self.dataTask cancel];关闭数据传输.
Question: 手动调cancel方法后, 就不会执行失败的block了吗?
(self.downloadQueue).suspended = suspended;, 这儿利用了NSOperationQueue的功能.[self.downloadQueue cancelAllOperations];SDWebImageDownloaderOperation的cancel方法.这一个模块开始进行图片下载相关代码的执行, 然而真正的下载代码还是被放在了SDWebImageDownloaderOperation中, 'SDWebImageDownloader'模块的分析只是对SDWebImageDownloaderOperation做了简单的描述, 主要还是重点分析本模块所做的事情--管理所有的下载行为. 此外, self.downloadQueue保证了对self.URLOperations操作能并发, 但又不相互干扰(同时保证异步和并发, 但实际上并没有并发).
SDWebImageDownloaderOperation
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
inSession:(nullable NSURLSession *)session
options:(SDWebImageDownloaderOptions)options;
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
- (void)start;
- (void)cancel;
- (void)setFinished:(BOOL)finished;
- (void)setExecuting:(BOOL)executing;
- (BOOL)cancel:(nullable id)token;
request复制了一份, 注意, NSURLRequest实现了NSCopying协议._callbackBlocks.session复制给了_unownedSession属性, 注意这儿是弱引用, 避免不必要的引用._barrierQueue,用于_callbackBlocks的增删操作,保证线程安全.progressBlock和completedBlock两个block都复制了一份,再存储到_callbackBlocks中.dispatch_barrier_async方法, 这是个异步操作start方法, 但是不能调用[super start];SDWebImage中, Operation被加到SDWebImageDownloader的downloadQueue中后会被自动执行, (自动调用operation的start方法)self.unownedSession是否还在, 一般情况下是还在的, 因为默认的SDWebImageDownloader是个单例不会被释放, 但如果开发者自己初始化一个SDWebImageDownloader就会存在self.unownedSession不再引用一个session的情况.session和request生成一个dataTask, 并将自己标记为正在执行.isFinished标记为YES.[NSArray removeObjectIdenticalTo:]方法, 利用"本体性"而不是"相等性"去移除对应的回调, 个人猜测是为了提高查找的速度. 具体可以参考Equality这篇文章.dispatch_barrier_sync, 注意这儿是一个同步方法, 后面根据移除后_callbackBlocks是否为空判断是否要停止当前的下载.Tips: start与cancel用@synchronized保证的线程安全, 对_callbackBlocks的操作使用一个队列保障线程安全. 此外, operation持有两个session, 一个是unownedSession, 这个由SDWebImageDownloader持有, operation对它保持弱引用, 还有一个是ownedSession, 当初始化的session被释放时候, 使用自己生成的session, 并用ownedSession保持引用, 并在[self reset]中释放这个session.
这个Operation完成了SDWebImage最重要的下载功能. 将一个URL的下载下载封装成一个NSOperation, 特别是在线程安全上做了一些优化, 和使用异步或是同步, 哪些操作需要保证线程安全, 哪些元素需要复制, 值得思考. 在SDWebImage的issue中有很多于此模块有关的, 值得细看.
SDWebImagePrefetcher
- (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager;
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
- (void)cancelPrefetching;
SDWebImageManager实例, 在这儿使用了[SDWebImageManager new], 调用的是SDWebImageManager的方法默认初始化方法. 因此, 这儿的manager和[SDWebImageManager sharedManager]不是一个实例, 但是由于SDWebImageManager的默认初始化方法中使用的[SDImageCache sharedImageCache]和[SDWebImageDownloader sharedDownloader]单例, 所以在这儿初始化的manager和[SDWebImageManager sharedManager]共享的同一个_imageCache和_imageDownloader实例.[SDWebImageManager sharedManager]的内存地址是0x61000107f280, 而SDWebImagePrefetcher所持有的manager地址是0x000060000106ef00, 他们不是同一个manager, 当我打印[SDWebImageManager sharedManager]的各个属性时候, 如下方结果, _imageCache和_imageDownloader的地址是一致的.(lldb) pinternals 0x61000107f280
(SDWebImageManager) $12 = {
NSObject = {
isa = SDWebImageManager
}
_delegate = nil
_imageCache = 0x00006080010661c0
_imageDownloader = 0x00006080000ff800
_cacheKeyFilter = (null)
_failedURLs = 0x00006080006406f0 0 elements
_runningOperations = 0x00006080010671c0 @"1 element"
}
(lldb) pinternals 0x000060000106ef00
(SDWebImageManager) $8 = {
NSObject = {
isa = SDWebImageManager
}
_delegate = nil
_imageCache = 0x00006080010661c0
_imageDownloader = 0x00006080000ff800
_cacheKeyFilter = (null)
_failedURLs = 0x00006000006524b0 0 elements
_runningOperations = 0x0000600001277f80 @"1 element"
}
CFAbsoluteTimeGetCurrent()比较高效的获取时间的方法, 虽然后面好像没用到这个属性.startPrefetchingAtIndex方法, 在该方法中使用SDWebImageManager执行下载URL, 缓存也是在SDWebImageManager中做的, 详细可以参考SDWebImageManager内容.startPrefetchingAtIndex中一个URL缓存完成之后,方法中有如下一段代码, 目的是通过强制执行下一个请求缓存的目的来增加self.requestedCount的值, 已达到处理这种弄异常的目的, 但是一般情况下不会只想到这里面来.if (self.prefetchURLs.count > self.requestedCount) {
dispatch_async(self.prefetcherQueue, ^{
[self startPrefetchingAtIndex:self.requestedCount];
});
}
SDWebImageManager结束正在下载的任务.这一个模块大部分是依靠SDWebImageManager来完成主体功能, 我曾经在某篇博客上看到有人说SDWebImagePrefetcher是不支持并发的, 至少在目前这个版本看来, 是完全支持一组URL并发的, 但是不支持同时预加载多组URL.
FLAnimatedImage
SDWebImage 支持动态图的, 建立在Flipboard的开源项目FLAnimatedImage的基础之上, 增加的一个扩展, 使用方法是pod 'SDWebImage/GIF', 或者手动把SDWebImage文件夹中的FLAnimatedImage文件夹拖入工程.
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
[UIView sd_setImageWithURL:placeholderImage:options:progress:completed:]方法,这个方法在最开始对UIView+WebCache模块有介绍.UIImage *image, NSData *imageData, 如果imageData是Gif, 使用[FLAnimatedImage animatedImageWithGIFData:imageData]方法初始化gif并使用.FLAnimatedImage实例, 再回到主线程进行展示, 中间本不该回到主线程造成资源浪费. 前面说了, 这个模块式通过调用[UIView sd_setImageWithURL:placeholderImage:options:progress:completed:]来完成下载操作, 我们可以自己调用该方法(不需要引入SDWebImage/GIF子模块). 例子如下(self.gifImageView 是一个FLAnimatedImageView实例):#import "NSData+ImageContentType.h"
#import "UIView+WebCache.h"
[self.gifImageView sd_internalSetImageWithURL:[NSURL URLWithString:self.resource.previewImageUrl]
placeholderImage:nil
options:0
operationKey:nil
setImageBlock:^(UIImage * _Nullable image, NSData * _Nullable imageData) {
SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:imageData];
if (imageFormat == SDImageFormatGIF) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
FLAnimatedImage *animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData];;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.gifImageView.animatedImage = animatedImage;
});
});
weakSelf.gifImageView.image = nil;
} else {
weakSelf.gifImageView.image = image;
weakSelf.gifImageView.animatedImage = nil;
}
}
progress:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
}];
这个模块被应该做三件事情, 一件是下载, 这个在下载模块完成了; 第二个是从下载下来的二进制文件中生成一张图片, 这个在UIImage+MultiFormat模块中完成的, 有兴趣的同学可以看看这个文件; 第三个是展示二进制文件, 这个是FLAnimatedImage做的.
最近想看一下一些优秀的开源库是如何编写的, SDWebImage是我看的第一份源码(以前草草看的不算), 受益匪浅. 这次我边看边写笔记, 最终整理这篇博客, 不仅仅是对源码的流程讲解, 有一些小的细节小技巧我也有单独标出来. 平时码代码的过程还是太随意了, 因为工程的量级决定不需要太注重一些细节, 但是对于这些细节, 能注意的还是应该注意.
作者:wyanassert
原地址:SDWebImage4.0 分析
目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标
网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.
一、机器人介绍 此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接
目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'
我想使用ruby-prof和JMeter分析Rails应用程序。我对分析特定Controller/操作/或模型方法的建议方法不感兴趣,我想分析完整堆栈,从上到下。所以我运行这样的东西:RAILS_ENV=productionruby-prof-fprof.outscript/server>/dev/null然后我在上面运行我的JMeter测试计划。然而,问题是使用CTRL+C或SIGKILL中断它也会在ruby-prof可以写入任何输出之前杀死它。如何在不中断ruby-prof的情况下停止mongrel服务器? 最佳答案
文章目录认识unity打包目录结构游戏逆向流程Unity游戏攻击面可被攻击原因mono的打包建议方案锁血飞天无限金币攻击力翻倍以上统称内存挂透视自瞄压枪瞬移内购破解Unity游戏防御开发时注意数据安全接入第三方反作弊系统外挂检测思路狠人自爆实战查看目录结构用il2cppdumper例子2-森林whoishe后记认识unity打包目录结构dll一般很大,因为里面是所有的游戏功能编译成的二进制码游戏逆向流程开发人员代码被编译打包到GameAssembly.dll中使用il2ppDumper工具,并借助游戏名_Data\il2cpp_data\Metadata\global-metadata.dat
在笔者前面有一篇文章《驱动开发:断链隐藏驱动程序自身》通过摘除驱动的链表实现了断链隐藏自身的目的,但此方法恢复时会触发PG会蓝屏,偶然间在网上找到了一个作者介绍的一种方法,觉得有必要详细分析一下他是如何实现的进程隐藏的,总体来说作者的思路是最终寻找到MiProcessLoaderEntry的入口地址,该函数的作用是将驱动信息加入链表和移除链表,运用这个函数即可动态处理驱动的添加和移除问题。MiProcessLoaderEntry(pDriverObject->DriverSection,1)添加MiProcessLoaderEntry(pDriverObject->DriverSection,
目录1. 研究范围定义2. 流程中台市场分析3. 厂商评估:微宏科技4. 入选证书 1. 研究范围定义近年来,随着外部市场环境快速变化、客户需求愈发多样,企业逐渐意识到,自身业务需要更加敏捷、高效,具备根据市场需求快速迭代的能力。业务流程的自动化能够帮助企业实现业务的敏捷高效,因此受到越来越多企业的关注。企业的“自动化武器库”品类丰富,包括低/零代码平台、RPA、BPM、AI等。企业可以使用多项自动化工具,但结果往往是各项自动化工具处于各自的“自动化烟囱”之中,仅能实现碎片式自动化。例如,某企业的IT团队可能在使用低代码平台、财务团队可能在使用RPA、呼叫中心则可能在使用聊天机器人。自动
我可以使用什么来分析1.9.2中的代码?我发现所有版本的ruby-prof都针对1.9.2存在段错误。例如,当我添加gem"ruby-prof"到我的Rails项目的Gemfile并运行bundlebundleexecruby-profconfig/environment.rb我遇到段错误。城里有新的分析gem吗?有没有办法让ruby-prof玩得很好? 最佳答案 不确定它是否有帮助,但我偶然发现了这一点,它可能会增加一点清晰度或引导您走上不同的道路:http://www.devheads.net/development/r
查看原文>>>基于”PLUS模型+“生态系统服务多情景模拟预测实践技术应用目录第一章、理论基础与软件讲解第二章、数据获取与制备第三章、土地利用格局模拟第四章、生态系统服务评估第五章、时空变化及驱动机制分析第六章、论文撰写技巧及案例分析基于ArcGISPro、Python、USLE、INVEST模型等多技术融合的生态系统服务构建生态安全格局基于生态系统服务(InVEST模型)的人类活动、重大工程生态成效评估、论文写作等具体应用基于ArcGISPro、R、INVEST等多技术融合下生态系统服务权衡与协同动态分析实践应用 本文从数据、方法、实践三方面对生态系统服务多情景预测进行讲解。内容涵盖多