开源库UITableView+FDTemplateLayoutCell学习

摘自:优化UITableViewCell高度计算Swift版优化UITableViewCell高度计算的那些事

本文带大家详细探索那篇文章所对应的库(1.2版),这个库就是利用缓存tableviewcell的高度提高滑动的流畅性。

主要是利用Runloop在空闲状态时,后台计算tableviewcell的高度并缓存起来。然后在使用的时候就直接从缓存中去,这里都放在一个数组里存在内存。

对Runloop以及几个mode不懂的可以看sunnyxx blog中的视频 视频可戳 , 文章的话可以看看 深入理解RunLoop、 【iOS程序启动与运转】- RunLoop个人小结

其实就是在kCFRunLoopDefaultMode模式下BeforWaitting状态去执行计算的。

下面来探究源码。首先在UITableView+FDTemplateLayoutCell 下载源码,下载1.2版本。

然后你得到的库就只有两个文件:

.m文件大概只有500行代码。

下面看下作者的视线思路:

1.  创建了一个_FDTemplateLayoutCellHeightCache类,就是管理Cache的一个类,里面有两个属性四个方法。

属性:

  • sections 这个变量就是用来存储缓存的height的一个二维数组。(因为tableview有section和row组成所以必须二维)
  • _FDTemplateLayoutCellHeightCacheAbsentValue 这个是一个静态常量,就是用来标记没有缓存高度的row 。

方法:

  • buildHeightCachesAtIndexPathsIfNeeded:indexPaths
    这个方法传入indexPaths数组来给sections中还没有初始化的元素进行初始化
  • hasCachedHeightAtIndexPath:indexPath 根据下标索引判断是否有缓存(其实就是判断是否等于上面那个静态常量)
  • cacheHeight:height:byIndexPath 根据indexPath给sections赋值。
  • cachedHeightAtIndexPath:indexPath 根据indexPath取值

这个类主要是操作和存储缓存的。这个类的代码如下:

@interface _FDTemplateLayoutCellHeightCache : NSObject
@property (nonatomic, strong) NSMutableArray *sections;  
@end

static CGFloat const _FDTemplateLayoutCellHeightCacheAbsentValue = -1;

@implementation _FDTemplateLayoutCellHeightCache

- (void)buildHeightCachesAtIndexPathsIfNeeded:(NSArray *)indexPaths {
    if (indexPaths.count == 0) {
        return;
    }

    if (!self.sections) {
        self.sections = @[].mutableCopy;
    }

    // Build every section array or row array which is smaller than given index path.
    [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
        for (NSInteger section = 0; section <= indexPath.section; ++section) {
            if (section >= self.sections.count) {
                self.sections[section] = @[].mutableCopy;
            }
        }
        NSMutableArray *rows = self.sections[indexPath.section];
        for (NSInteger row = 0; row <= indexPath.row; ++row) {
            if (row >= rows.count) {
                rows[row] = @(_FDTemplateLayoutCellHeightCacheAbsentValue);
            }
        }
    }];
}

- (BOOL)hasCachedHeightAtIndexPath:(NSIndexPath *)indexPath
{
    [self buildHeightCachesAtIndexPathsIfNeeded:@[indexPath]];
    NSNumber *cachedNumber = self.sections[indexPath.section][indexPath.row];
    return ![cachedNumber isEqualToNumber:@(_FDTemplateLayoutCellHeightCacheAbsentValue)];
}

- (void)cacheHeight:(CGFloat)height byIndexPath:(NSIndexPath *)indexPath
{
    [self buildHeightCachesAtIndexPathsIfNeeded:@[indexPath]];
    self.sections[indexPath.section][indexPath.row] = @(height);
}

- (CGFloat)cachedHeightAtIndexPath:(NSIndexPath *)indexPath
{
    [self buildHeightCachesAtIndexPathsIfNeeded:@[indexPath]];
#if CGFLOAT_IS_DOUBLE
    return [self.sections[indexPath.section][indexPath.row] doubleValue];
#else
    return [self.sections[indexPath.section][indexPath.row] floatValue];
#endif
}

@end

2. 接下来是UITableView的一个扩展UITableView + FDTemplateLayoutCellPrivate

  • 第一个方法fd_templateCellForReuseIdentifier:identifier,这个方法主要是通过你传入的一个identifier(就是复用的id)获取cell。

    第一句是这样的 NSMutableDictionary *templateCellsByIdentifiers = objc_getAssociatedObject(self, _cmd);

    OC中的 _cmd 代表的就是本方法,objc_getAssociatedObject 获取一个关联对象的属性。

  • 接下来提供了一个方法来获取管理Cache的_FDTemplateLayoutCellHeightCache的对象fd_cellHeightCache。
  • 属性:fd_autoCacheInvalidationEnabled 记录是否自动缓存高度
  • 属性:fd_precacheEnabled

这是一个私有类,下面给出这个类的完整代码:

@interface UITableView (FDTemplateLayoutCellPrivate)

/// Returns a template cell created by reuse identifier, it has to be registered to table view.
/// Lazy getter, and associated to table view.
- (id)fd_templateCellForReuseIdentifier:(NSString *)identifier;

/// A private height cache data structure.
@property (nonatomic, strong, readonly) _FDTemplateLayoutCellHeightCache *fd_cellHeightCache;

/// This is a private switch that I don‘t think caller should concern.
/// Auto turn on when you use "-fd_heightForCellWithIdentifier:cacheByIndexPath:configuration".
@property (nonatomic, assign) BOOL fd_autoCacheInvalidationEnabled;

/// It helps to improve scroll performance by "pre-cache" height of cells that have not
/// been displayed on screen. These calculation tasks are collected and performed only
/// when "RunLoop" is in "idle" time.
///
/// Auto turn on when you use "-fd_heightForCellWithIdentifier:cacheByIndexPath:configuration".
@property (nonatomic, assign) BOOL fd_precacheEnabled;

/// Debug log controlled by "fd_debugLogEnabled".
- (void)fd_debugLog:(NSString *)message;

@end

@implementation UITableView (FDTemplateLayoutCellPrivate)

- (id)fd_templateCellForReuseIdentifier:(NSString *)identifier
{
    NSAssert(identifier.length > 0, @"Expects a valid identifier - %@", identifier);

    NSMutableDictionary *templateCellsByIdentifiers = objc_getAssociatedObject(self, _cmd);
    if (!templateCellsByIdentifiers) {
        templateCellsByIdentifiers = @{}.mutableCopy;
        objc_setAssociatedObject(self, _cmd, templateCellsByIdentifiers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }

    UITableViewCell *templateCell = templateCellsByIdentifiers[identifier];

    if (!templateCell) {
        templateCell = [self dequeueReusableCellWithIdentifier:identifier];
        NSAssert(templateCell != nil, @"Cell must be registered to table view for identifier - %@", identifier);
        templateCell.fd_isTemplateLayoutCell = YES;
        templateCellsByIdentifiers[identifier] = templateCell;
        [self fd_debugLog:[NSString stringWithFormat:@"layout cell created - %@", identifier]];
    }

    return templateCell;
}

- (_FDTemplateLayoutCellHeightCache *)fd_cellHeightCache {
    _FDTemplateLayoutCellHeightCache *cache = objc_getAssociatedObject(self, _cmd);
    if (!cache) {
        cache = [_FDTemplateLayoutCellHeightCache new];
        objc_setAssociatedObject(self, _cmd, cache, OBJC_ASSOCIATION_RETAIN);
    }
    return cache;
}

- (BOOL)fd_autoCacheInvalidationEnabled
{
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setFd_autoCacheInvalidationEnabled:(BOOL)enabled
{
    objc_setAssociatedObject(self, @selector(fd_autoCacheInvalidationEnabled), @(enabled), OBJC_ASSOCIATION_RETAIN);
}

- (BOOL)fd_precacheEnabled
{
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setFd_precacheEnabled:(BOOL)precacheEnabled
{
    objc_setAssociatedObject(self, @selector(fd_precacheEnabled), @(precacheEnabled), OBJC_ASSOCIATION_RETAIN);
}

- (void)fd_debugLog:(NSString *)message
{
    if (!self.fd_debugLogEnabled) {
        return;
    }
    NSLog(@"** FDTemplateLayoutCell ** %@", message);
}

@end

3. 下面又是一个分类,(这个是重点计算高度,调用缓存管理方法的分类)UITableView + FDTemplateLayoutCellPrecache

这个里面的方法在他blog中也有提到就是在NSDefaultRunLoopMode下当状态将要进入休眠的时候把计算方法分解成多个RunLoop Source任务(source0)

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;

这个方法将创建一个 Source 0 任务,分发到指定线程的 RunLoop 中,在给定的 Mode 下执行,若指定的 RunLoop 处于休眠状态,则唤醒它处理事件.

主要逻辑就是先通过遍历所有section和row找到还没有缓存的row,然后加入到待缓存数组 ,创建一个observer去监听Runloop的状态 ,如果空闲了去创建source0任务,执行计算方法并缓存起来。如果预缓存任务完成了就把监听的Observer移除了。

下面给出这个类的代码:

@implementation UITableView (FDTemplateLayoutCellPrecache)

- (void)fd_precacheIfNeeded
{
    if (!self.fd_precacheEnabled) {
        return;
    }

    // Delegate could use "rowHeight" rather than implements this method.
    if (![self.delegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)]) {
        return;
    }

    CFRunLoopRef runLoop = CFRunLoopGetCurrent();

    // This is a idle mode of RunLoop, when UIScrollView scrolls, it jumps into "UITrackingRunLoopMode"
    // and won‘t perform any cache task to keep a smooth scroll.
    CFStringRef runLoopMode = kCFRunLoopDefaultMode;

    // Collect all index paths to be precached.
    NSMutableArray *mutableIndexPathsToBePrecached = self.fd_allIndexPathsToBePrecached.mutableCopy;

    // Setup a observer to get a perfect moment for precaching tasks.
    // We use a "kCFRunLoopBeforeWaiting" state to keep RunLoop has done everything and about to sleep
    // (mach_msg_trap), when all tasks finish, it will remove itself.
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler
    (kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) {
        // Remove observer when all precache tasks are done.
        if (mutableIndexPathsToBePrecached.count == 0) {
            CFRunLoopRemoveObserver(runLoop, observer, runLoopMode);
            return;
        }
        // Pop first index path record as this RunLoop iteration‘s task.
        NSIndexPath *indexPath = mutableIndexPathsToBePrecached.firstObject;
        [mutableIndexPathsToBePrecached removeObject:indexPath];

        // This method creates a "source 0" task in "idle" mode of RunLoop, and will be
        // performed in a future RunLoop iteration only when user is not scrolling.
        [self performSelector:@selector(fd_precacheIndexPathIfNeeded:)
                     onThread:[NSThread mainThread]
                   withObject:indexPath
                waitUntilDone:NO
                        modes:@[NSDefaultRunLoopMode]];
    });

    CFRunLoopAddObserver(runLoop, observer, runLoopMode);
}

- (void)fd_precacheIndexPathIfNeeded:(NSIndexPath *)indexPath
{
    if (![self.fd_cellHeightCache hasCachedHeightAtIndexPath:indexPath]) {
        CGFloat height = [self.delegate tableView:self heightForRowAtIndexPath:indexPath];
        [self.fd_cellHeightCache cacheHeight:height byIndexPath:indexPath];
        [self fd_debugLog:[NSString stringWithFormat:
                           @"precached - [%@:%@] %@",
                           @(indexPath.section),
                           @(indexPath.row),
                           @(height)]];
    }
}

- (NSArray *)fd_allIndexPathsToBePrecached
{
    NSMutableArray *allIndexPaths = @[].mutableCopy;
    for (NSInteger section = 0; section < [self numberOfSections]; ++section) {
        for (NSInteger row = 0; row < [self numberOfRowsInSection:section]; ++row) {
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
            if (![self.fd_cellHeightCache hasCachedHeightAtIndexPath:indexPath]) {
                [allIndexPaths addObject:indexPath];
            }
        }
    }
    return allIndexPaths.copy;
}

@end

4. 下面又是一个分类UITableView + FDTemplateLayoutCellAutomaticallyCacheInvalidation 

  因为我们会有一些操作导致cell的改变,所以这里作者要保证在每次cell改变的时候把sections数组改掉,然后如果新增或者修改了 需要重新计算高度。用到了methodSwizzle 黑魔法。这里作者把swizzle放在了UITableView的load类方法中。需要使用methodSwizzle的方法有:

  SEL selectors[] = {
        @selector(reloadData),
        @selector(insertSections:withRowAnimation:),
        @selector(deleteSections:withRowAnimation:),
        @selector(reloadSections:withRowAnimation:),
        @selector(moveSection:toSection:),
        @selector(insertRowsAtIndexPaths:withRowAnimation:),
        @selector(deleteRowsAtIndexPaths:withRowAnimation:),
        @selector(reloadRowsAtIndexPaths:withRowAnimation:),
        @selector(moveRowAtIndexPath:toIndexPath:)
    };

这个类的代码:

@implementation UITableView (FDTemplateLayoutCellAutomaticallyCacheInvalidation)

+ (void)load
{
    // All methods that trigger height cache‘s invalidation
    SEL selectors[] = {
        @selector(reloadData),
        @selector(insertSections:withRowAnimation:),
        @selector(deleteSections:withRowAnimation:),
        @selector(reloadSections:withRowAnimation:),
        @selector(moveSection:toSection:),
        @selector(insertRowsAtIndexPaths:withRowAnimation:),
        @selector(deleteRowsAtIndexPaths:withRowAnimation:),
        @selector(reloadRowsAtIndexPaths:withRowAnimation:),
        @selector(moveRowAtIndexPath:toIndexPath:)
    };

    for (NSUInteger index = 0; index < sizeof(selectors) / sizeof(SEL); ++index) {
        SEL originalSelector = selectors[index];
        SEL swizzledSelector = NSSelectorFromString([@"fd_" stringByAppendingString:NSStringFromSelector(originalSelector)]);

        Method originalMethod = class_getInstanceMethod(self, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);

        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

- (void)fd_reloadData
{
    if (self.fd_autoCacheInvalidationEnabled) {
        [self.fd_cellHeightCache.sections removeAllObjects];
    }
    [self fd_reloadData]; // Primary call
    [self fd_precacheIfNeeded];
}

- (void)fd_insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
    if (self.fd_autoCacheInvalidationEnabled) {
        [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
            [self.fd_cellHeightCache.sections insertObject:@[].mutableCopy atIndex:idx];
        }];
    }
    [self fd_insertSections:sections withRowAnimation:animation]; // Primary call
    [self fd_precacheIfNeeded];
}

- (void)fd_deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
    if (self.fd_autoCacheInvalidationEnabled) {
        [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
            [self.fd_cellHeightCache.sections removeObjectAtIndex:idx];
        }];
    }
    [self fd_deleteSections:sections withRowAnimation:animation]; // Primary call
}

- (void)fd_reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
{
    if (self.fd_autoCacheInvalidationEnabled) {
        [sections enumerateIndexesUsingBlock: ^(NSUInteger idx, BOOL *stop) {
            NSMutableArray *rows = self.fd_cellHeightCache.sections[idx];
            for (NSInteger row = 0; row < rows.count; ++row) {
                rows[row] = @(_FDTemplateLayoutCellHeightCacheAbsentValue);
            }
        }];
    }
    [self fd_reloadSections:sections withRowAnimation:animation]; // Primary call
    [self fd_precacheIfNeeded];
}

- (void)fd_moveSection:(NSInteger)section toSection:(NSInteger)newSection
{
    if (self.fd_autoCacheInvalidationEnabled) {
        [self.fd_cellHeightCache.sections exchangeObjectAtIndex:section withObjectAtIndex:newSection];
    }
    [self fd_moveSection:section toSection:newSection]; // Primary call
}

- (void)fd_insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
    if (self.fd_autoCacheInvalidationEnabled) {
        [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
            NSMutableArray *rows = self.fd_cellHeightCache.sections[indexPath.section];
            [rows insertObject:@(_FDTemplateLayoutCellHeightCacheAbsentValue) atIndex:indexPath.row];
        }];
    }
    [self fd_insertRowsAtIndexPaths:indexPaths withRowAnimation:animation]; // Primary call
    [self fd_precacheIfNeeded];
}

- (void)fd_deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
    if (self.fd_autoCacheInvalidationEnabled) {
        [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
            [self.fd_cellHeightCache.sections[indexPath.section] removeObjectAtIndex:indexPath.row];
        }];
    }
    [self fd_deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation]; // Primary call
}

- (void)fd_reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
    if (self.fd_autoCacheInvalidationEnabled) {
        [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
            NSMutableArray *rows = self.fd_cellHeightCache.sections[indexPath.section];
            rows[indexPath.row] = @(_FDTemplateLayoutCellHeightCacheAbsentValue);
        }];
    }
    [self fd_reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation]; // Primary call
    [self fd_precacheIfNeeded];
}

- (void)fd_moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
    if (self.fd_autoCacheInvalidationEnabled) {
        NSMutableArray *sourceRows = self.fd_cellHeightCache.sections[sourceIndexPath.section];
        NSMutableArray *destinationRows = self.fd_cellHeightCache.sections[destinationIndexPath.section];

        NSNumber *sourceValue = sourceRows[sourceIndexPath.row];
        NSNumber *destinationValue = destinationRows[destinationIndexPath.row];

        sourceRows[sourceIndexPath.row] = destinationValue;
        destinationRows[destinationIndexPath.row] = sourceValue;
    }
    [self fd_moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; // Primary call
}

@end

5. 下面还有一个分类UITableView + FDTemplateLayoutCell,这个类提供外界获取cell高度的方法

  • fd_heightForCellWithIdentifier:configuration:configuration
  • fd_heightForCellWithIdentifier:cacheByIndexPath:configuration:configuration

  这个类的方法如下:

@implementation UITableView (FDTemplateLayoutCell)

- (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id))configuration
{
    if (!identifier) {
        return 0;
    }

    // Fetch a cached template cell for `identifier`.
    UITableViewCell *cell = [self fd_templateCellForReuseIdentifier:identifier];

    // Manually calls to ensure consistent behavior with actual cells (that are displayed on screen).
    [cell prepareForReuse];

    // Customize and provide content for our template cell.
    if (configuration) {
        configuration(cell);
    }

    // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead
    // of growing horizontally, in a flow-layout manner.
    NSLayoutConstraint *tempWidthConstraint =
    [NSLayoutConstraint constraintWithItem:cell.contentView
                                 attribute:NSLayoutAttributeWidth
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:nil
                                 attribute:NSLayoutAttributeNotAnAttribute
                                multiplier:1.0
                                  constant:CGRectGetWidth(self.frame)];
    [cell.contentView addConstraint:tempWidthConstraint];

    // Auto layout engine does its math
    CGSize fittingSize = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

    [cell.contentView removeConstraint:tempWidthConstraint];

    // Add 1px extra space for separator line if needed, simulating default UITableViewCell.
    if (self.separatorStyle != UITableViewCellSeparatorStyleNone) {
        fittingSize.height += 1.0 / [UIScreen mainScreen].scale;
    }

    return fittingSize.height;
}

- (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier cacheByIndexPath:(NSIndexPath *)indexPath configuration:(void (^)(id))configuration
{
    if (!identifier || !indexPath) {
        return 0;
    }

    // Enable auto cache invalidation if you use this "cacheByIndexPath" API.
    if (!self.fd_autoCacheInvalidationEnabled) {
        self.fd_autoCacheInvalidationEnabled = YES;
    }
    // Enable precache if you use this "cacheByIndexPath" API.
    if (!self.fd_precacheEnabled) {
        self.fd_precacheEnabled = YES;
        // Manually trigger precache only for the first time.
        [self fd_precacheIfNeeded];
    }

    // Hit the cache
    if ([self.fd_cellHeightCache hasCachedHeightAtIndexPath:indexPath]) {
        [self fd_debugLog:[NSString stringWithFormat:
                           @"hit cache - [%@:%@] %@",
                           @(indexPath.section),
                           @(indexPath.row),
                           @([self.fd_cellHeightCache cachedHeightAtIndexPath:indexPath])]];
        return [self.fd_cellHeightCache cachedHeightAtIndexPath:indexPath];
    }

    // Do calculations
    CGFloat height = [self fd_heightForCellWithIdentifier:identifier configuration:configuration];
    [self fd_debugLog:[NSString stringWithFormat:
                       @"calculate - [%@:%@] %@",
                       @(indexPath.section),
                       @(indexPath.row),
                       @(height)]];

    // Cache it
    [self.fd_cellHeightCache cacheHeight:height byIndexPath:indexPath];

    return height;
}

- (BOOL)fd_debugLogEnabled
{
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setFd_debugLogEnabled:(BOOL)debugLogEnabled
{
    objc_setAssociatedObject(self, @selector(fd_debugLogEnabled), @(debugLogEnabled), OBJC_ASSOCIATION_RETAIN);
}

@end
时间: 2024-08-06 16:02:24

开源库UITableView+FDTemplateLayoutCell学习的相关文章

Android开源库与设计模式开源组SAOS建立

Android开源库与设计模式开源组建立 简介 在2014年年底突然参与了CSDN的博客之星评选,看着自己的博客水平实在太低,于是就想一定得写一些跟别人不太一样的博客出来.经过自己的一番思考,觉得在Android开源库的深入实现上并没有什么太多的资料,或者只是大概讲述了一些基本原理.这样我觉得是不够的,很多事情你没有自己去经历你不会有很深的认识,或者你根本不知道原来它会出现这样的问题.于是我就想我没通过学习轮子制造过程来更加深入的学习,这样不仅能够了解那些知名的开源库,也能够从实战的角度学习开源

MXNet 学习 (1) --- 最易上手的深度学习开源库 --- 安装及环境搭建

安装环境:Win 10 专业版 64位 + Visual Studio 2015 Community. 记录下自己在有GPU的环境下安装配置MXNet的过程.该过程直接使用MXNet release 的 pre-built 包,没有自己使用CMake编译.网上有很多自己编译的教程,过程都比较繁琐,直接使用release包对新手来说更加简单方便. 选择MXNet的原因是因为看了<Caffe.TensorFlow.MXNet三个开源库的对比>这篇博文,其中指出MXNet相对来说是最易上手的深度学习

DCMTK开源库的学习笔记4:利用ini配置文件对dcm影像进行归档

转:http://blog.csdn.net/zssureqh/article/details/8846337 背景介绍: 医学影像PACS工作站的服务端需要对大量的dcm文件进行归档,写入数据库处理.由于医学图像的特殊性,每一个患者(即所谓的Patient)每做一次检查(即Study)都至少会产生一组图像序列(即Series),而每一组图像序列下会包含大量的dcm文件(例如做一次心脏CTA的诊断,完整的一个心脏断层扫描序列大约有200幅图像).DICOM3.0协议中对每一幅影像是按照特定的三个

async-http-client开源库学习笔记(一)

0. 文前闲话 作为一个Android开发的大龄初学者,面对扑面而来的各种姿势的Android的开源组件,让人倍感窒息,难以应对.无奈为了养家糊口,虽然已近不惑,人老珠黄,也只能废寝忘食,逐个体位细细揣摩研究,不断以身实践,争取早日小成. 话说那是一个阳光明媚的下午,我坐在街口转角处优雅的网络会所里,品着一杯上好的Coca-Cola,研读着oschina客户端源码,身旁不时传来:"一起上搞死他,他没蓝了...!". 从火蚁(oschina客户端的开发者之一,向他们致敬)那里知道了asy

iOS 热门高效开源库集锦,收藏备用

一.推荐使用的第三方库 1:基于响应式编程思想的ReactiveCocoa 地址:https://github.com/ReactiveCocoa/ReactiveCocoa 2:iOS解耦与组件化开发神器-FFRouter 地址:https://github.com/imlifengfeng/FFRouter 3:XML/HTML解析 地址:https://github.com/topfunky/hpple 4:有文字输入时,能根据键盘是否弹出来调整自身显示内容的位置 地址:https://g

开源中国iOS客户端学习

开源中国iOS客户端学习 续写前言 <开源中国iOS客户端学习>续写前系列博客    http://blog.csdn.net/column/details/xfzl-kykhd.html 开源中国iOS客户端学习——序 说到这款开源软件就得提到她的娘家了--开源中国社区: 开源中国社区简介:开源中国 www.oschina.net 成立于2008年8月,是目前中国最大的开源技术社区.传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现.使用.并交流开源技术的平台.目前开源中国社区已收

iOS经典开源库

iOS开源库 youtube下载神器:https://github.com/rg3/youtube-dl我擦咧 vim插件:https://github.com/Valloric/YouCompleteMevim插件配置:https://github.com/spf13/spf13-vim ----------------Mac完整项目----------电台:https://github.com/myoula/sostart豆瓣FM:https://github.com/turingou/do

iOS开源库

youtube下载神器:https://github.com/rg3/youtube-dl我擦咧 vim插件:https://github.com/Valloric/YouCompleteMevim插件配置:https://github.com/spf13/spf13-vim ----------------Mac完整项目----------电台:https://github.com/myoula/sostart豆瓣FM:https://github.com/turingou/douban.fm

【计算机视觉】OpenCV的最近邻开源库FLANN

FLANN介绍 FLANN库全称是Fast Library for Approximate Nearest Neighbors,它是目前最完整的(近似)最近邻开源库.不但实现了一系列查找算法,还包含了一种自动选取最快算法的机制. flann::Index_类 该类模板是最近邻索引类,该类用于抽象不同类型的最近邻搜索的索引. 以下是flann::Index_类的声明: template <typename T> class #ifndef _MSC_VER FLANN_DEPRECATED #e