Hi, this is Alice Home


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 公益404

EROS (一) 环境搭建

发表于 2018-09-05

EROS

环境

Node.js >= 9.x
npm 4+

1
2
3
4
5
6
7
# maple @ iMac in ~/workspace/eros [17:03:22]
$ node --version
v10.3.0

# maple @ iMac in ~/workspace/eros [17:03:29]
$ npm --version
6.1.0

安装

1
$ npm install -g eros-cli

安装出现问题:

1
2
3
4
5
6
7
$ npm install -g eros-cli
npm WARN deprecated gulp-util@3.0.7: gulp-util is deprecated - replace it, following the guidelines at https://medium.com/gulpjs/gulp-util-ca3b1f9f9ac5
npm WARN deprecated node-uuid@1.4.8: Use uuid module instead
npm ERR! Unexpected end of JSON input while parsing near '...anced-match":"^0.2.0"'

npm ERR! A complete log of this run can be found in:
npm ERR! /Users/maple/.npm/_logs/2018-09-05T09_49_30_162Z-debug.log

解决:

清除下缓存

1
2
npm cache clean --force
7281 npm install -g eros-cli

安装 cnpm

1
npm install -g cnpm --registry=https://registry.npm.taobao.org

安装 weex-toolkit

1
cnpm i -g weex-toolkit

需求预估时间提前阅读总结:

需求一: 有道广告优化

目前我看到的是在moment中有 youdao 类型的 cell 返回将这个cell出来,在代理方法中有对有道广告的配置,那么这个需求我们要在哪里进行修改???

iOS 项目中 1 倍图检索

发表于 2018-09-05

在项目开发的过程中,图片资源有些 1 倍图还存在,但是目前 iOS 开发的版本都支持 iOS 6 以上,甚至大部分只支持到 iOS 8 或者 iOS 9 , 所以这些 1 倍图资源可以直接删除了,可以通过下面的脚本大概检索一下项目中 1 倍图的数量,预估一下工作量

image delete py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/env python
# coding=utf-8
#!/usr/bin/env python

import os
import fnmatch
import argparse

__author__ = 'huidragonaijy@gmail.com'
__version__ = '0.0.1'
__copyright__ = 'MIT License'

BLUE = '\033[1;94m'
GREEN = '\033[1;92m'
ENDC = '\033[0m'


def iter_files(path, fnexp):
for root, dirs, files in os.walk(path):
for filename in fnmatch.filter(files, fnexp):
yield os.path.join(root, filename)


def extant_dir(x):
if not os.path.exists(x):
# Argparse uses the ArgumentTypeError to give a rejection message like:
# error: argument input: x does not exist
raise argparse.ArgumentTypeError("{0} does not exist".format(x))
elif not os.path.isdir(x):
raise argparse.ArgumentTypeError("{0} isn't a directory".format(x))
return x


def main(argv=None):
parser = argparse.ArgumentParser(description='find all 1x image in the specified directory.')
parser.add_argument('--version', action='version', version=__version__)
parser.add_argument('path', help='destination directory', type=extant_dir)
args = parser.parse_args(argv)

total = 0
for file_path in iter_files(args.path, '*@2x.png'):
file_name = os.path.basename(file_path)
(name, _) = file_name.split('@')
if not list(iter_files(args.path, name + '@3x.png')):
continue
imgs_1x = list(iter_files(args.path, name + '.png'))
if imgs_1x:
total += 1
print BLUE + '=> ' + name + ENDC
for img in imgs_1x:
print img
print u'\U0001F37B ' + GREEN + 'total: %d' % total + ENDC


if __name__ == '__main__':
main()

删除注意

  1. 删除图片的时候要查找每一张图片使用的情况
  2. imageName: 加载图片的时候,最好不要使用 .png 后缀,否则只加载了 1 倍图,那么如果将 1 倍图删除了,就会出现问题

终端浪一浪

发表于 2018-08-11

lolcat

效果:

亮骚色,哈哈

安装

1
gem install lolcat

至于 gem 是个啥?自行 Google !

使用:

1
ls -a | lolcat

开发的时候累了,来一个亮骚色的显示

移动 App 网络优化实践(一)

发表于 2018-08-09

iOS 学习记录篇

发表于 2018-08-09

记录博客或者第三方库或者开源项目。

作者 博客 备注
bang’s JSPatch 作者 移动 APP 网络优化概述

NSTimer 内存泄露

发表于 2018-08-08 | 分类于 iOS

NSTimer 引起 target 不能释放

NSTimer 是 RunLoop 的一种源,这就注定了其使用过程中与其他类的不同。

1
2
3
4
5
6
7
8
9
- (NSTimer * _Nonnull)addTimer {

return [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testOne) userInfo:nil repeats:YES];
}

- (void)viewDidLoad {
[super viewDidLoad];
[self addTimer];
}

上面这个代码是在控制器中加入了 Timer,然后执行 testOne 方法,repeat 为 YES。表现形式就是,当进入这个控制器之后,testOne 方法会在 1 秒间隔重复执行。如果没有其他 timer 有关的方法,那么当控制器 dimiss 之后,这个控制器不会被销毁,testOne 方法还是会一直被调用。

处理方式一, 调用 invalidate

出现上面的问题的原因是:

NSTimer 通过 scheduleTimerWithTimeInterval: target: selector: userInfo: repeats: 这个方法创建 timer 之后,这个 timer 会被添加到 runloop 的中,同时,runloop 会对 timer 做一次引用,而 target: 中的 self 又会被 timer 引用一次。runloop 是一直在内存中引用着 timer 的,所以 self 会被一直持有,所以就不会被释放了。

NSTimer 为我们提供了一个将 timer 从它的 runloop 中移除的方法:

1
2
3
4
5
- (void)invalidate;
Description
Stops the timer from ever firing again and requests its removal from its run loop.
This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes its strong reference to the timer, either just before the invalidate method returns or at some later point.
If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.

这里指出了 invalidate 是唯一一种移除的方式 … 所以一般的处理方式是在不用到 timer 的时候让其调用 invalidate 这个方法,比如上面的问题我们如下处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@implementation TimerVC {
NSTimer *_timer;
}

- (NSTimer * _Nonnull)addTimer {

return [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testOne) userInfo:nil repeats:YES];
}

- (void)viewDidLoad {
[super viewDidLoad];
_timer = [self addTimer];
}


- (void)viewWillDisappear:(BOOL)animated {

[_timer invalidate];
_timer = nil;
[super viewWillDisappear:animated];

}

- (void)testOne {
NSLog(@"HEllo world");
}

- (void)dealloc {

NSLog(@"dealloc ....");
}

step:

  1. 首先在当前类中添加全局变量,在 addTimer 的时候为其赋值
  2. 在该类不在使用该 timer 的时候,[_timer invalidate] 和 _timer = nil

这样就可以避免当前类不释放的问题了。但是 … 有没有觉得很不优雅,当前控制器需要在自己离开的时候照顾这个 runloop 家的宠儿的问题,而且为了它的消亡添加了一个全局变量

处理方式二:代理对象

其实,这里的问题就出在 runloop -> timer -> self 这个链中,因为 runloop 的不死,导致了 self 一直被引用。上面的做法是强行将 runloop -> timer 的应用切断。如果有其他方案的话,我们只能去想 timer -> self 这条链了,因为 runloop 是不能死掉的。

首先,为 timer 添加一个类目:

1
2
3
@interface NSTimer (Weak)
+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
@end

其次,是在这类目中添加一个 target 类

1
2
3
4
5
6
7
8
@interface NSTimerTarget:NSObject

@property (nonatomic, weak) id delegate;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *timer;
- (void)targetSelector;

@end

接下来看一下实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

- (void)targetSelector {

if (_delegate) {
[self.delegate performSelector:_selector withObject:nil];
}else {
[_timer invalidate];
_timer = nil;
}
}

- (void)dealloc {
NSLog(@"target is dealloc...");
}
@end

@implementation NSTimer (Weak)

+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo {

NSTimerTarget *target = [NSTimerTarget new];
target.delegate = aTarget;
target.selector = aSelector;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:ti target:target selector:@selector(targetSelector) userInfo:userInfo repeats:yesOrNo];
target.timer = timer;
return target.timer;
}
@end

在这里,通过一个中间类将外部调用 timer 的类换成了这个中间类,这样 timer 对象就不会持有调用了 NSTimer 方法的对象了,除此之外,这里 target 类还需要将外部类的 selector 方重新再 targetSelector 中调用。

targetSelector 这个方法在控制器(或者其他写了 NSTimer 启动方法的类)销毁之后其实还会继续调用,所以在内部判断移除 timer 的时机。

这样,整体上我们就不需要对外部调用 timer 的类做一些特殊的处理了。

总结:

其实,网上还有其他的解决方式,后续会继续看这个问题:

  1. 通过 GCD 实现 timer, NSWeakTimer
  2. 通过代理类 实现 timer,HWWeakTimer

第一种方式,是彻底将 timer 替换成了 gcd 中的 timer;第二种方式和目前的实现方式类似,但是是引入了新的 timer 类。

其实,这个问题应该是遇到很久了,大部分都是直接将 timer 移除的方法耦合到了使用的类中,一直没有去深究这问题,这次深究完了发现:

  1. NSRunloop 的理解加深
  2. 代码的设计上要多动脑
  3. 遇到问题多考虑集中解决方案然后再继续写代码
  4. 代码的优化方式永远不能止步

最后,代码传送带。

排序(二) 快速排序

发表于 2018-08-03 | 分类于 排序

快速排序

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据编程有序序列。

过程分析

一趟排序过程:

  1. 设置两个变量 i、j, 排序开始的时候: i = 0, j = N -1;
  2. 以第一个数组元素作为关键数据,赋值给 key:key = A[0];
  3. 从 j 开始向前搜索,即由后向前开始搜索(j –), 找到第一个小于 key 的值 A[j],将 A[j] 和 A[i] 交换;
  4. 从 i 开始向后搜索,即由前向后开始搜索(i ++),找到第一个大于 key 的 A[i], 然后将 A[j] 和 A[i] 交换;
  5. 重复 3、4 步骤,知道 i = j;

Code

复杂度分析

前言

不得不说冒泡排序相对来说比较复杂些,这篇博客也是经过了三四天才写完的,刚开始看别人写的算法实现,不太明白,然后看了下排序的动态图,还是模模糊糊。我觉得,怎么算是掌握了一个算法呢?首先,要知道每一步怎么去操作,比如给一串数字告诉你一个排序算法名字,你可以写出每一步骤怎么做,这是掌握;其次,要写出它的伪代码;最后,代码实现!很多公司会让你说这个算法题奥!

排序(一) 冒泡排序

发表于 2018-08-03 | 分类于 排序

代码链接

冒泡排序

轻者上浮,冒泡排序就是依次对比两个元素的大小,如果顺序错误就把它们进行交换。

过程

  • 取第一个元素和第二个元素进行比较,如果第一个大就交换;
  • 对第三个、第四个、… 执行相同操作,直到最后一个元素,这样结束后,最后一个元素最大
  • 重复上面的步骤,但是不除去最后一个元素
  • 重复上面的步骤,直到排序完成

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14

func bubbleSort(_ array: [Int]) -> [Int] {

var arr = array
for i in 0 ..< arr.count {

for j in 0 ..< arr.count - 1 - i {
if arr[j] > arr[j + 1]{
arr.swapAt(j, j + 1)
}
}
}
return arr
}

复杂度

  1. 时间复杂度: O(n²)
  2. 空间复杂度: O(1)

Data Structure (二) Tree

发表于 2018-07-31 | 分类于 Data Structure

数据结构 - 树

树结构可以帮助我们解决很多重要问题:

  • 代表对象之间有层次依赖的关系
  • 快速查找
  • 提供一种排序的列表数据
  • 文本中的前缀匹配

树种的术语

Root 根节点

树结构中第 0 层的节点称为 root 节点,这个节点作为树的入口

Node 节点

Node 是树结构中的基本单位,是树中分装数据的结构, root 也是一种 node

Leaf 叶子节点

最底层的 node 节点,没有孩子节点

Swift 实现树结构

React Native (一) 环境搭建

发表于 2018-07-31 | 分类于 React Native

安装步骤主要参考 官方文档

主要就是安装 node ,再者一些 watchdog 等一类的工具,然后就是 Xcode ,模拟器 安卓的 SDK JDK 一类的。这些工具因为之前做个 Node JS 以及 iOS 和安卓开发都已经安装好了,这里主要记录几个一开始遇到的问题:

问题 1: 无法运行 iOS 模拟器

1
2
3
4
5
6
7
react-native run-ios
Scanning folders for symlinks in /Users/maple/Developer/RN/AwesomeProject/node_modules (12ms)
Found Xcode project AwesomeProject.xcodeproj
xcrun: error: unable to find utility "instruments", not a developer tool or in PATH

Command failed: xcrun instruments -s
xcrun: error: unable to find utility "instruments", not a developer tool or in PATH

解决方案:

1
2
 sudo xcode-select -s /Applications/Xcode.app/Contents/Developer/
Password: 输入密码

重新运行即可

问题二: iOS 模拟器运行报错 ‘No bundle URL present.

针对这个问题是这样解决的,开启两个终端,Terminal1 和 Terminal2:

Terminal1 输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$react-native start
Scanning folders for symlinks in /Users/maple/Developer/RN/AwesomeProject/node_modules (15ms)
┌──────────────────────────────────────────────────────────────────────────────┐
│ │
│ Running Metro Bundler on port 8081. │
│ │
│ Keep Metro running while developing on any JS projects. Feel free to │
│ close this tab and run your own Metro instance if you prefer. │
│ │
│ https://github.com/facebook/react-native │
│ │
└──────────────────────────────────────────────────────────────────────────────┘

Looking for JS files in
/Users/maple/Developer/RN/AwesomeProject


Metro Bundler ready.

Loading dependency graph, done.

Terminal2 输入:

1
react-native run-ios

这样就可以运行起来了

初步接触 RN 中的 JS

根据官方文档中的说法,在开发的过程中主要面对的就是 App.js 文件,所以大体上看下这个文件内容,分成四部分:

  1. 引入文件包 import
  2. 格式化字符串 instructions ,内部通过 ios: 和 android: 做了显示内容的区分
  3. render() 通过内嵌 XML 文件显示文本
  4. style 文字显示样式的属性设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';

const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});

type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
1234…13
Alice

Alice

The Loneliest Whale in the World

121 日志
35 分类
30 标签
© 2021 Alice
由 Hexo 强力驱动
主题 - NexT.Pisces