Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

1068 lines (638 sloc) 35.369 kB
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>北武灵尊</title>
<meta name="author" content="lingzuer">
<meta name="description" content="易信年会收获: 1、能不能再多做一点 2、三等奖净水器 不好的: 家里已经安装了净水器
">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://lingzuer.github.io">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<link href="/atom.xml" rel="alternate" title="北武灵尊" type="application/atom+xml">
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="/javascripts/libs/jquery.min.js"%3E%3C/script%3E'))</script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="//fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
</head>
<body >
<header role="banner"><hgroup>
<h1><a href="/">北武灵尊</a></h1>
<h2>记录一些事情,方便查阅。</h2>
</hgroup>
</header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
<form action="https://www.google.com/search" method="get">
<fieldset role="search">
<input type="hidden" name="sitesearch" value="lingzuer.github.io">
<input class="search" type="text" name="q" results="0" placeholder="Search"/>
</fieldset>
</form>
<ul class="main-navigation">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/02/06/yi-xin-nian-hui/">易信年会</a></h1>
<p class="meta">
<time class='entry-date' datetime='2015-02-06T23:23:04+08:00'><span class='date'><span class='date-month'>Feb</span> <span class='date-day'>6</span><span class='date-suffix'>th</span>, <span class='date-year'>2015</span></span> <span class='time'>11:23 pm</span></time>
</p>
</header>
<div class="entry-content"><p>易信年会收获:</p>
<p>1、能不能再多做一点</p>
<p>2、三等奖净水器</p>
<p>不好的:</p>
<p>家里已经安装了净水器</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/02/05/alassetslibraryxuan-ze-tu-pian-you-hua/">ALAssetsLibrary选择图片优化</a></h1>
<p class="meta">
<time class='entry-date' datetime='2015-02-05T14:17:52+08:00'><span class='date'><span class='date-month'>Feb</span> <span class='date-day'>5</span><span class='date-suffix'>th</span>, <span class='date-year'>2015</span></span> <span class='time'>2:17 pm</span></time>
</p>
</header>
<div class="entry-content"><p>背景:</p>
<p>老板的手机iphone 6 plus有5000+张相片,每次选择相片的时候需要长时间的loading,不能忍受。</p>
<p>排查问题:</p>
<p>打log排查到最耗时的地方是枚举相片的时候,调用的方法是:</p>
<pre><code>- (void)enumerateAssetsUsingBlock:(ALAssetsGroupEnumerationResultsBlock)enumerationBlock;
</code></pre>
<p>这个在1000张左右的时候还算OK,但是相片多的时候就耗时了。</p>
<p>然后分析了微信、Lofter、QQ和易信,大概时间是这样的:</p>
<p>环境:iphone6+,相片6000张</p>
<p>微信:1秒</p>
<p>Lofter:1秒</p>
<p>QQ:秒开(无loading)</p>
<p>易信:4秒</p>
<p>注意我说的是大概感受的时间。</p>
<p>这一对比就不淡定了,通过查资料,最后决定使用方案:</p>
<pre><code>- (void)enumerateAssetsAtIndexes:(NSIndexSet *)indexSet options:(NSEnumerationOptions)options usingBlock:(ALAssetsGroupEnumerationResultsBlock)enumerationBlock;
</code></pre>
<p>对,其实就是换了一个方法,哈哈,那思路就完全不一样了,之前是枚举出了所有的图片,保存到内存中,后面都使用这个数组,这样,开始的时候就比较耗时,后面使用的时候倒是很爽,之后呢,是通过IndexSet方法配合Table的cellForRow,产看那个加载那个,一张张加载,是不是很爽。</p>
<p>换了新方法后,我也把loading给拿掉了,表现能达到QQ。</p>
<p>然后问题来了,预览大图的时候怎么处理?预览大图还要排除掉视频,怎么保证顺序和currentIndex?</p>
<p>又拿QQ和微信做了分析,发现:</p>
<p>QQ点击相片的时候是[选中]操作,最下面的“预览”按钮是预览所有选中的相片。</p>
<p>微信点击相片是预览大图,在预览大图的时候可以选中该相片。</p>
<p>我个人觉得QQ是很合理,策划说微信的交互比较方便,那挑战就来了,在我们的速度达到QQ以后,交互还要达到微信。</p>
<p>难点:</p>
<p>1、点击其中一个相片预览大图,大图可以左右滑动查看所有相片。</p>
<p>2、预览大图左右滑动的时候需要把视频过滤掉。</p>
<p>分析:</p>
<p>1、在显示相片列表的时候已经枚举了这一屏所显示的20几张相片,点击其中任何一个相片,预览大图都没有问题,那么滑动的时候我们再动态枚举后面的相片,就OK了吧。</p>
<p>2、动态枚举后面的相片时,过滤掉视频,然后再排序,然后再定位当前相片在新的数组中的index就好了,这个地方牵扯出了一个新的难点:动态枚举出新的相片产生新的数组后,什么时候更新PageView呢?</p>
<p>解决新的难题:</p>
<p>滑动预览时,预加载到index==0(PageView只显示三个图片,会预加载提前一个图片)的时候,我们通过IndexSet预加载接下来的20(自己定义)张相片,然后过滤掉视频,并且从新排序,把新取到的数组先放着,等到用户不滑动的时候更新PageView,重新定位currentIndex值。</p>
<p>后续:</p>
<p>1、继续重构、优化;</p>
<p>2、提供Demo</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/02/02/assetslibraryzi-ding-yi-tu-pian-xuan-ze-qi/">AssetsLibrary自定义图片选择器</a></h1>
<p class="meta">
<time class='entry-date' datetime='2015-02-02T22:55:30+08:00'><span class='date'><span class='date-month'>Feb</span> <span class='date-day'>2</span><span class='date-suffix'>nd</span>, <span class='date-year'>2015</span></span> <span class='time'>10:55 pm</span></time>
</p>
</header>
<div class="entry-content"><p>1、ALAssetsLibraryAccessor单例</p>
<p>ALAssetsLibraryAccessor.h</p>
<pre><code>#import &lt;Foundation/Foundation.h&gt;
@class ALAssetsLibrary;
@interface ALAssetsLibraryAccessor : NSObject {
ALAssetsLibrary *assetsLibrary_;
}
+ (ALAssetsLibraryAccessor *)sharedInstance;
- (ALAssetsLibrary *)assetsLibrary;
- (void)refreshAssetsLibrary;
@end
</code></pre>
<p>ALAssetsLibraryAccessor.m</p>
<pre><code>#import "ALAssetsLibraryAccessor.h"
#import &lt;AssetsLibrary/AssetsLibrary.h&gt;
@implementation ALAssetsLibraryAccessor
- (id)init {
self = [super init];
if (self) {
assetsLibrary_ = [[ALAssetsLibrary alloc] init];
}
return self;
}
+ (ALAssetsLibraryAccessor *)sharedInstance
{
static ALAssetsLibraryAccessor *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&amp;onceToken, ^{
sharedInstance = [[ALAssetsLibraryAccessor alloc] init];
});
return sharedInstance;
}
- (ALAssetsLibrary *)assetsLibrary {
return assetsLibrary_;
}
- (void)refreshAssetsLibrary {
assetsLibrary_ = nil;
assetsLibrary_ = [[ALAssetsLibrary alloc] init];
}
@end
</code></pre>
<p>2、ALAssetHelper获取AlAsset图片</p>
<p>ALAssetHelper.h</p>
<pre><code>#import &lt;Foundation/Foundation.h&gt;
#import &lt;AssetsLibrary/AssetsLibrary.h&gt;
@interface ALAssetHelper : NSObject
+ (UIImage *)getImage:(ALAsset *)asset original:(BOOL)original;
@end
</code></pre>
<p>ALAssetHelper.m</p>
<pre><code>#import "ALAssetHelper.h"
@implementation ALAssetHelper
+ (UIImage *)getImage:(ALAsset *)asset original:(BOOL)original
{
UIImage *image = nil;
CGImageRef ref = nil;
ALAssetRepresentation *rep = [asset defaultRepresentation];
if (original) {
// 获取ios剪裁后的图片
NSData *xmpData = rep.metadata[@"AdjustmentXMP"];
if (xmpData != nil) {
ref = [rep fullScreenImage];
image = [UIImage imageWithCGImage:ref];
} else {
ref = [rep fullResolutionImage];
image = [UIImage imageWithCGImage:ref
scale:[rep scale]
orientation:(UIImageOrientation)[rep orientation]];
}
} else {
ref = [rep fullScreenImage];
image = [UIImage imageWithCGImage:ref];
}
return image;
}
@end
</code></pre>
<p>3、枚举相册</p>
<pre><code>- (void)enumerateAssetsGroups {
self.assetGroups = [NSMutableArray arrayWithCapacity:0];
ALAssetsLibrary *library = [[ALAssetsLibraryAccessor sharedInstance] assetsLibrary];
// Load Albums into assetGroups
dispatch_async(dispatch_get_main_queue(), ^{
@autoreleasepool {
// Group enumerator Block
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
{
if (group) {
[self.assetGroups addObject:group];
} else {
// group is nil, so there are no more groups to enumerate
}
};
// Group Enumerator Failure Block
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
if (error.code == ALAssetsLibraryAccessUserDeniedError ||
error.code == ALAssetsLibraryAccessGloballyDeniedError) {
// Denied Error
}
};
// Enumerate Albums
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:assetGroupEnumerator
failureBlock:assetGroupEnumberatorFailure];
}
});
}
</code></pre>
<p>4、枚举相片</p>
<pre><code>- (void)enumerateAssets:(ALAssetsGroup *)assetGroup {
NSMutableArray *assets = [NSMutableArray arrayWithCapacity:0];
ALAssetsGroupEnumerationResultsBlock groupEnumeration = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
[assets addObject:result];
} else {
//
}
};
[assetGroup enumerateAssetsUsingBlock:groupEnumeration];
}
</code></pre>
<p>枚举相片的三个方法:</p>
<pre><code>// These methods are used to retrieve the assets that match the filter.
// The caller can specify which results are returned using an NSIndexSet. The index set's count or lastIndex cannot exceed -numberOfAssets.
// 'enumerationBlock' is used to pass back results to the caller and provide the opportunity to stop the filter.
// When the enumeration is done, 'enumerationBlock' will be called with result set to nil and index set to NSNotFound.
// If the application has not been granted access to the data, 'enumerationBlock' will be called with result set to nil, index set to NSNotFound, and stop set to YES.
- (void)enumerateAssetsUsingBlock:(ALAssetsGroupEnumerationResultsBlock)enumerationBlock;
- (void)enumerateAssetsWithOptions:(NSEnumerationOptions)options usingBlock:(ALAssetsGroupEnumerationResultsBlock)enumerationBlock;
- (void)enumerateAssetsAtIndexes:(NSIndexSet *)indexSet options:(NSEnumerationOptions)options usingBlock:(ALAssetsGroupEnumerationResultsBlock)enumerationBlock;
</code></pre>
<p>提问:</p>
<p>如何发送一个ALAsset对象的视频?</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/02/01/chang-yong-gong-ju/">常用工具</a></h1>
<p class="meta">
<time class='entry-date' datetime='2015-02-01T22:41:28+08:00'><span class='date'><span class='date-month'>Feb</span> <span class='date-day'>1</span><span class='date-suffix'>st</span>, <span class='date-year'>2015</span></span> <span class='time'>10:41 pm</span></time>
</p>
</header>
<div class="entry-content"><p>1、Unused</p>
<p>网址:<a href="https://github.com/jeffhodnett/Unused">https://github.com/jeffhodnett/Unused</a></p>
<p>优点:找到相同图片可以预览</p>
<p>缺点:</p>
<p>①没有考虑@3x图片(作者好像不维护了);</p>
<p>②没有排除某个文件夹下面的图片不过滤,导致查找时间较长;</p>
<p>不过这两点都可以自己试着改掉。</p>
<p>使用感受:</p>
<p>需求做完,bug改完,后可以试着跑一下。用处不是很大,该功能下面这个工具也有。主要是以前没有看到过这种用Xcode开发Mac上使用的小工具,比较新奇,玩一下。</p>
<p>2、FauxPas</p>
<p>网址:<a href="http://fauxpasapp.com">http://fauxpasapp.com</a></p>
<p>102 rules: <a href="http://fauxpasapp.com/rules/">http://fauxpasapp.com/rules/</a></p>
<p>这个工具不多说了,直接看官网,是我目前发现比较好用的工具,一是可以帮助我找到项目中写的不好的代码,还有一些建议,二是可以通过这些建议对照想要的代码深入学习理解OC。</p>
<p>使用FauxPas后我的思考和困惑:</p>
<p>1、这样的优化意义大不大?</p>
<p>2、怎么才能让真个团队都提高这种写代码的素质?</p>
<p>3、可以有什么新的产出么?比如做一个Web的分析平台?</p>
<p>之前写的时候<a href="http://mouapp.com">Mou</a>奔溃了,只截了一张图片</p>
<p><img src="/images/blog/used-tools.png" alt="How" /></p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/01/31/xctestce-shi-shu-xing-bian-hua/">XCTest测试属性变化</a></h1>
<p class="meta">
<time class='entry-date' datetime='2015-01-31T21:01:07+08:00'><span class='date'><span class='date-month'>Jan</span> <span class='date-day'>31</span><span class='date-suffix'>st</span>, <span class='date-year'>2015</span></span> <span class='time'>9:01 pm</span></time>
</p>
</header>
<div class="entry-content"><p>之前有一篇写XCTest异步测试,现在写一下对属性值变化的测试。比如设置某个属性的值,然后调用了某个方法后,值的变化是不是我们预期得到的。下面的代码是作者jtang写的,我修改了一句以适应ARC模式。</p>
<p>1、创建一个NSObject的扩展类(Category)</p>
<p>NSObject+Properties.h</p>
<pre><code>#import &lt;Foundation/Foundation.h&gt;
@interface NSObject (Properties)
// 仅可用于单元测试 ivarName是成员变量名字,不是属性名字
// 对于对象直接返回,如果是原始数据类型.返回NSValue
// 示例:
// NSValue *value = [self getIvarFromString_ONLY_USE_IN_UNIT_TEST:@"_test"];
// int x;
// [value getValue:&amp;x];
- (id)getIvarFromString_ONLY_USE_IN_UNIT_TEST:(NSString *)ivarName;
@end
</code></pre>
<p>NSObject+Properties.m</p>
<pre><code>#import "NSObject+Properties.h"
#import &lt;objc/runtime.h&gt;
@implementation NSObject (Properties)
- (id)getIvarFromString_ONLY_USE_IN_UNIT_TEST:(NSString *)ivarName
{
Ivar ivar = class_getInstanceVariable(self.class, [ivarName UTF8String]);
// Ivar ivar = object_getInstanceVariable(self, [ivarName UTF8String], NULL);
if (ivar)
{
id ivarID = object_getIvar(self, ivar);
const char *typeEncoding = ivar_getTypeEncoding(ivar);
if (typeEncoding[0] == '@')
{
return ivarID;
}
else
{
return [NSValue valueWithBytes:&amp;ivarID objCType:typeEncoding];
}
}
return nil;
}
@end
</code></pre>
<p>2、用法举例</p>
<p>ViewController.h</p>
<pre><code>#import &lt;UIKit/UIKit.h&gt;
@interface ViewController : UIViewController
- (void)test;
@end
</code></pre>
<p>ViewController.m</p>
<pre><code>#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSString *userName;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_userName = @"abc";
}
- (void)test {
_userName = @"def";
}
@end
</code></pre>
<p>测试文件MyAppTests.m</p>
<pre><code>#import &lt;UIKit/UIKit.h&gt;
#import &lt;XCTest/XCTest.h&gt;
#import "ViewController.h"
#import "NSObject+Properties.h"
@interface MyAppTests : XCTestCase {
// add instance variables to the CalcTests class
ViewController *viewController;
}
@end
@implementation MyAppTests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
viewController = [[ViewController alloc] init];
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testUserName {
[viewController test];
NSString *userName = (NSString *)[viewController getIvarFromString_ONLY_USE_IN_UNIT_TEST:@"_userName"];
XCTAssertEqualObjects(userName, @"def", @"Sussess");
}
@end
</code></pre>
<p>3、测试UI变化</p>
<pre><code>#import "TestSettingPickerCell_UI.h"
#import "SettingPickerCell.h"
#import "NSObject+Properties.h"
@implementation TestSettingPickerCell_UI
{
SettingPickerCell *cell;
}
- (void)testsetCheckMarkYES
{
UIImageView *checkImageView = [cell getIvarFromString_ONLY_USE_IN_UNIT_TEST:@"_mCheckImageView"];
[cell setCheckMark:YES];
STAssertEquals(checkImageView.image, [UIImage imageNamed:@"selected_green.png"],@"");
}
- (void)testsetCheckMarNO
{
UIImageView *checkImageView = [cell getIvarFromString_ONLY_USE_IN_UNIT_TEST:@"_mCheckImageView"];
[cell setCheckMark:NO];
STAssertEquals(checkImageView.image, [UIImage imageNamed:@"CellUnchecked.png"],@"");
}
- (void)setUp
{
[super setUp];
cell = [[SettingPickerCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
- (void)tearDown
{
[super tearDown];
}
@end
</code></pre>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/01/30/ji-lei-de-jiao-ben/">积累的脚本</a></h1>
<p class="meta">
<time class='entry-date' datetime='2015-01-30T22:06:38+08:00'><span class='date'><span class='date-month'>Jan</span> <span class='date-day'>30</span><span class='date-suffix'>th</span>, <span class='date-year'>2015</span></span> <span class='time'>10:06 pm</span></time>
</p>
</header>
<div class="entry-content"><p>自己常用的一些脚本,批量查找、替换、删除。</p>
<p>1、复制一个文件夹下的文件到另外一个文件夹下</p>
<pre><code>cp -ri bar/ foo/
</code></pre>
<p>2、删除文件夹下的文件</p>
<pre><code>rm -rf /images/image*
</code></pre>
<p>3、在文件中查找关键字并替换</p>
<pre><code>grep -rl "2013 - 浙ICP备" * | xargs sed -i -e 's/2013 - 浙ICP备/2013-2014 - 浙ICP备/g'
</code></pre>
<p>4、查找文件夹下的文件是否包含某个关键字</p>
<pre><code>find src/ -name *.html |xargs grep -rn "火影"
find classes/ -name '*.xib' -o -name '*.mm' -o -name '*.m' |xargs grep -rl "bg_picker_white_cell"
find src/ -name *.js |xargs grep -rn "__dirty__"
</code></pre>
<p>5、批量替换的脚本</p>
<pre><code>#!/bin/sh
#批量替换文案
oldString='&lt;a target="_blank" href="http://yixin.im/contact.html"&gt;联系我们&lt;/a&gt;'
newString='&lt;a target="_blank" href="http://yixin.im/contact.html"&gt;联系我们&lt;/a&gt; - &lt;a target="_blank" href="http://yixin.im/contact.html"&gt;扫黄打非·净网2014&lt;/a&gt;'
grep -rl $oldString *.html | xargs sed -i -e "s#$oldString#$newString#g"
</code></pre>
<p>6、删除example文件所有包含test的行</p>
<pre><code>sed '/test/d' example
</code></pre>
<p>7、删除文件夹下文件中还有console.log的行,并且删除Sublime Text产生的-e结尾的文件</p>
<pre><code>find html/ -name *.html -o -name *.js | xargs sed -i -e '/console.log/d'
find javascript/ -name *.html -o -name *.js | xargs sed -i -e '/console.log/d'
find src/ -name "*.html-e" -o -name "*.js-e" | xargs rm -f
</code></pre>
<p>这个脚本的产生是有个缘由的,一个有奖(大奖有iPhone6 plus)的活动页面,查看页面源代码的时候,虽然js代码经过了压缩并混淆,但是文件中的console.log中写的中文都显示出来了,要命的是,console.log中把每一个步骤及逻辑都写在里面了。幸亏是在公司内测的时候发现的问题。</p>
<p><img src="/images/blog/console-log-1.jpg" alt="console log" /></p>
<p><img src="/images/blog/console-log-2.jpg" alt="console log" /></p>
<p>然后我就想怎么把所有的console.log干掉,然后再压缩混淆,于是就有了这个脚本。但是问题来了,console.log在本地调试的时候还是有用的(Native+Web的方式,<a href="https://www.npmjs.com/package/weinre">weinre</a>调试方便一点),总不能先运行脚本删掉,然后等打包发布完了,再revert回来吧,于是就想,能不能在打包脚本里面加上去掉console.log的功能呢,正好看了一下公司的打包脚本是<a href="https://github.com/mishoo/UglifyJS2">Uglifyjs2</a>,继续上网查找资料的时候,发现Uglifyjs2有去掉console.log的配置项,drop_console: true,然后联系了NEJ作者genify,大牛百忙中加上了这个<a href="https://github.com/genify/toolkit/commit/39e6faaae26e2f6efc744bfd79278a00e65bd729">配置</a>,工程在这里<a href="https://github.com/genify/toolkit">https://github.com/genify/toolkit</a>,顺利打包。</p>
<p>8、find的时候排除某个文件夹</p>
<p>文件结构:</p>
<pre><code>Demo
| ——— First
| ——— One
| ——— index.html
| ——— Second
| ——— index.html
| ——— index.html
</code></pre>
<p>1)、在Demo文件夹下查找index.html,排除Second文件夹</p>
<pre><code>find . -path './Second' -prune -o -name '*.html' -print
</code></pre>
<p>2)、在Demo文件夹下查找index.html,排除One文件夹</p>
<pre><code>find . -path './First/One' -prune -o -name '*.html' -print
</code></pre>
<p>3)、-prune对前面求值,-print不能少</p>
<p>这个脚本有什么具体的使用呢?这是我后来看到<a href="https://github.com/jeffhodnett/Unused">Unused</a>的时候想到的,这个项目有个问题,一是没有处理@3x的图片,作者很久都没有更新过了,另外一个就是跑出来的结果中,有个文件夹下面是我不想让它去检索的,比如存放Emoji表情的文件夹,那我就想,能不能也提供个输入框,用来选择要过滤的某个文件夹呢,然后一番尝试,还真成功了,哈哈,起作用的主要代码如下:</p>
<pre><code>// Create a find task
NSTask *task = [[[NSTask alloc] init] autorelease];
[task setLaunchPath: @"/usr/bin/find"];
// Search for all png files
NSArray *argvals = nil;
if (filterDirectoryPath.length &lt;= 0) {
argvals = [NSArray arrayWithObjects:directoryPath, @"-name", @"*.png", nil];
} else {
argvals = [NSArray arrayWithObjects:directoryPath,
@"-path", filterDirectoryPath, @"-prune", @"-o", @"-name", @"*.png", @"-print", nil];
}
[task setArguments: argvals];
</code></pre>
<p>这是过滤了一个文件夹,要是过滤好几个呢,那就是:</p>
<pre><code>find ./ \( -path './dir0*' -o -path './dir1*' \) -a -prune -o -name '*.png' -print
</code></pre>
<p>对这个开源项目改造了一下,试跑了一下易信的工程,运行时间大大减少,并且还找出了好几个没有用到的图片资源,减少了好几十个kb呢,也算是安慰了一下这颗折腾的心吧。</p>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/01/29/xctestdan-yuan-ce-shi/">XCTest单元测试</a></h1>
<p class="meta">
<time class='entry-date' datetime='2015-01-29T22:14:13+08:00'><span class='date'><span class='date-month'>Jan</span> <span class='date-day'>29</span><span class='date-suffix'>th</span>, <span class='date-year'>2015</span></span> <span class='time'>10:14 pm</span></time>
</p>
</header>
<div class="entry-content"><p>QA的同事说帮他们讲一下OC,我自己都是个搓鸟,还给别人讲,就当是玩笑拒绝了,那么多大牛都在,找我讲?后来又说让我讲,是认真的,那我想,反正他们也不会,忽悠一下他们还是OK的了,简单的写个Hello World还是不成问题的,于是就去讲了,讲到后来才明白他们想的最终需求是:单元测试。作为开发我从来没有了解过这一块,我说自己回去先写个Demo。于是上网各种搜索,还是有些收获的,昨天晚上到12:40,对UI和属性的测试有了点思路,完成一个简单的Demo,就当是单元测试的Hello World吧,今天上午又搞定了异步请求的单元测试,结合自己项目(方便写登录的请求)写了一个Demo,这也是列为今天todo list的第一个任务。现在记录一下。</p>
<p>1、新建一个测试的Target</p>
<p><img src="/images/blog/test-target.png" alt="Test Target" /></p>
<p>2、设置Search Paths
保持和项目的Target设置一致,不然在引用文件的时候,可能会提示有些文件找不到,比如:LoginManager.h:10:9: fatal error: &lsquo;biz/service/auth/auth_protocol.h&rsquo; file not found</p>
<p><img src="/images/blog/search-paths.png" alt="Search Paths" /></p>
<p>3、新建Test Class</p>
<p>见图一。</p>
<p>4、引入需要的文件开始测试
比如要写一个登录的单元测试,那要引入LoginManager.h之类的文件吧,我大概写一下,有些是项目的代码,不知道写在博文里面好不好。</p>
<pre><code>#import &lt;UIKit/UIKit.h&gt;
#import &lt;XCTest/XCTest.h&gt;
#import "LoginManager.h"
#import "LoginCallBack.h"
@interface MyTests : XCTestCase {
XCTestExpectation *_expectation;
}
@end
@implementation MyTests
- (void)setUp {
[super setUp];
[self addListenEvents];
}
- (void)tearDown {
[self removeListenEvents];
[super tearDown];
}
#pragma mark - 通知
- (void)addListenEvents {
extern NSString *kYIXINNotificationLoginResult;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onGetLoginResult:) name:kYIXINNotificationLoginResult object:nil];
}
- (void)removeListenEvents {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Test Methods
- (void)testUserLogin {
_expectation = [self expectationWithDescription:@"Login request"];
NSString *username = @"userID";
NSString *password = @"111111";
[LoginManager sharedManager].currentLoginData.userName = username;
[LoginManager sharedManager].currentLoginData.userPassword = [XXUtil encytePassword:password];
[LoginManager sharedManager].currentLoginData.type = kAccountYid;
[[LoginManager sharedManager] beginLogin];
[self waitForExpectationsWithTimeout:5
handler:^(NSError *error) {
// handler is called on _either_ success or failure
if (error != nil) {
XCTFail(@"timeout error: %@", error);
}
}];
}
#pragma mark - LoginResultProtocol
- (void)onGetLoginResult:(NSNotification*)aNotification {
extern NSString *kLoginStepKey;
extern NSString *kLoginResultKey;
NSDictionary *data = aNotification.userInfo;
NSInteger step = [[data objectForKey:kLoginStepKey] intValue];
NSInteger errorCode = [[data objectForKey:kLoginResultKey] intValue];
if (step == kLoginStepLogin) {
[_expectation fulfill];
if (errorCode == kResSuccess) {
XCTAssert(YES, @"Pass");
} else {
XCTAssert(NO, @"No Pass");
}
} else {
NSLog(@ "login result: step is %@, code is %@",@(step), @(errorCode));
}
}
@end
</code></pre>
<p>5、异步测试要点</p>
<pre><code>_expectation = [self expectationWithDescription:@"Login request”];
[_expectation fulfill];
XCTAssert(YES, @"Pass");
</code></pre>
<p>6、其他</p>
<p>其他的异步测试比如Block、Delegate的方式也都如此(采用XCTestExpectation)。</p>
<p>7、参考资料</p>
<ul>
<li><a href="http://objccn.io/issue-15-2/">http://objccn.io/issue-15-2/</a></li>
<li><a href="http://www.objc.io/issue-15/xctest.html">http://www.objc.io/issue-15/xctest.html</a></li>
<li><a href="http://www.bignerdranch.com/blog/asynchronous-testing-with-xcode-6/">http://www.bignerdranch.com/blog/asynchronous-testing-with-xcode-6/</a></li>
<li><a href="http://blog.dadabeatnik.com/2014/07/13/asynchronous-unit-testing-in-xcode-6/">http://blog.dadabeatnik.com/2014/07/13/asynchronous-unit-testing-in-xcode-6/</a></li>
<li><a href="http://www.pumpmybicep.com/2014/08/22/objective-c-http-stubbing-libraries/">http://www.pumpmybicep.com/2014/08/22/objective-c-http-stubbing-libraries/</a></li>
<li><a href="http://iosunittesting.com/asynchronous-tests-using-xctestexpectation/">http://iosunittesting.com/asynchronous-tests-using-xctestexpectation/</a></li>
<li><a href="http://www.pumpmybicep.com/2014/08/20/asynchronous-unit-testing-with-xctest/">http://www.pumpmybicep.com/2014/08/20/asynchronous-unit-testing-with-xctest/</a></li>
<li><a href="http://nshipster.com/xctestcase/">http://nshipster.com/xctestcase/</a></li>
<li><a href="http://ocmock.org">http://ocmock.org</a></li>
</ul>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2015/01/28/hello-to-myself/">Hello to Myself</a></h1>
<p class="meta">
<time class='entry-date' datetime='2015-01-28T15:17:17+08:00'><span class='date'><span class='date-month'>Jan</span> <span class='date-day'>28</span><span class='date-suffix'>th</span>, <span class='date-year'>2015</span></span> <span class='time'>3:17 pm</span></time>
</p>
</header>
<div class="entry-content"><p>Octopress听到它有些时间了,并且还配置好Run成功了,甚至有个Hello World的一个博文,我看了提交记录是Aug 14, 2013。从这之后就放着了,因为不知道写什么。再后来,看很多大牛写博客,然后就自己也想写,有个幼稚的想法是:大牛要写博客,写博客会成为大牛。</p>
<p>仔细想想写什么东西呢?写的东西给谁看呢?好累,现在想通了,我的博客定位就是自己记录一下自己学到的知识点,以前不知道的,现在知道了,记录下来,下次再想查的时候,方便一点儿,于是就下定决心想要写了。</p>
<p>这个时候已经换了一台电脑了,又要重新折腾Octopress,在一步一步配置的时候,出现问题了,没有Run成功,于是又放下了,第二天又尝试了一次,又没有成功,就又放下了,如此反复几次后,暗暗的下了一个决心:一定要Run起来,不然会对以后的修行有障碍(凡人修仙)。</p>
<p>花了一整天时间上网查找资料,功夫不负有心人,折腾到下午的时候,突然一个尝试了一个方法,结果成功了。心里特别开心,觉得只要下死功夫没有搞不定的。</p>
<p>环境搞好了,又陷入了脑海(脑子里的一片独特空间,这个地方不断有新想法冒出来)中,第一篇写点什么呢,写的不好怎么办呢?就又放了有一个月。</p>
<p>而后在反思自己为什么总有一些想法(养鱼、养花、打羽毛球,还有很多)冒出来,却都没有实施的时候,突然诊断出自己又一个到了晚期的心理病:拖延症,严重的拖延症。</p>
<p>症状表现是:总是在想,从来没有实施。自己的脑海里面有N个idea,每个idea都是那么的美好(老婆说很多都不切实际),当想实施的时候,发现有许许多多的条件不满足,即使做了,也达不到预期的美好效果,然后就放弃了,开始下一个胡思乱想。可能还有个原因就是,想做到最好,所以没有开始做。</p>
<p>这让我心里有些害怕,我从什么时候变成这样,或者一直是这样,一直没有发现么。</p>
<p>我想改变,哪怕是做一点很小的事情或者举动(比如一直在脑海里做俯卧撑,这个时候身体实施了一个俯卧撑动作),只要不是光想着的就行。</p>
<p>这样有了这一篇博文,我想写,我写了。</p>
</div>
</article>
<div class="pagination">
<a href="/blog/archives">Blog Archives</a>
</div>
</div>
<aside class="sidebar">
<section>
<h1>Recent Posts</h1>
<ul id="recent_posts">
<li class="post">
<a href="/blog/2015/02/06/yi-xin-nian-hui/">易信年会</a>
</li>
<li class="post">
<a href="/blog/2015/02/05/alassetslibraryxuan-ze-tu-pian-you-hua/">ALAssetsLibrary选择图片优化</a>
</li>
<li class="post">
<a href="/blog/2015/02/02/assetslibraryzi-ding-yi-tu-pian-xuan-ze-qi/">AssetsLibrary自定义图片选择器</a>
</li>
<li class="post">
<a href="/blog/2015/02/01/chang-yong-gong-ju/">常用工具</a>
</li>
<li class="post">
<a href="/blog/2015/01/31/xctestce-shi-shu-xing-bian-hua/">XCTest测试属性变化</a>
</li>
</ul>
</section>
</aside>
</div>
</div>
<footer role="contentinfo"><p>
Copyright &copy; 2015 - lingzuer -
<span class="credit">Powered by <a href="http://octopress.org">Octopress</a></span>
</p>
</footer>
</body>
</html>
Jump to Line
Something went wrong with that request. Please try again.