0%

仓鼠小课堂系列

寄存器

2017年01月13日 讲解人:骑神


简单的计算机内部结构。

那么我们的代码在执行的时候会发生什么事情呢?通常来说,我们的APP存储的位置在存储硬盘

先说一个基础知识,上面的结构图当中越往上执行速度越高

在启动应用之后,CPU会到硬盘中寻址找到应用的存储位置,把app的内存加载到内存当中

然后在程序运行期间,基本上就是CPU不断的在寄存器和内存之后交换数据,当然还有其他虚拟内存的东西,这里就不讲了,因为我也不太懂

寄存器是什么呢?在CPU核心周围分布着一堆的存储元件,这些元件的存储容量很小,一半来说基本都是K为单位的

这些元件被称为寄存器,寄存器会因为自身的功能被分为多种类型,例如寻址寄存器、浮点数寄存器、堆栈等等

作为开发者,我们一般关心的只有通用寄存器,因为你从汇编中看到的代码基本上也是在操作这些寄存器

通用寄存器采用的结构是栈式结构,这种结构有什么好处呢?


如右边的递归代码,在调用的时候会把函数的地址一个个推入寄存器栈中

在我们连续调用一系列的方法的时候,为了能让函数执行完毕之后回到原来的代码位置,系统会把当前调用的代码处的地址推入调用栈中

除此之外,函数调用需要用到的变量会在其他寄存器也进行入栈处理

函数调用完毕之后出栈获取调用处的地址,然后返回继续执行指令

接着就是昨天🐹所说的为什么递归无返回或者递归次数达到一定次数的时候会发生崩溃,原因就是寄存器的尺寸是非常有限的

所以递归次数达到一定数量的时候,寄存器就会容纳不下

delegate和block

2017年01月13日 讲解人:骑神

先来看看delegate跟block的代码

delegate的是NSURLConnectDataDelegate的方法列表,大家可以看到在NSURLConnection的请求过程中,代理会有多次的回调,分别对应图中注释的情况

block图上有两个block,上面是一个日常开发常见的网络请求的处理,下面是NSURLSession创建一个请求任务的代码

NSURLSession除了通过这种方式创建请求任务和传入请求结果处理之外。它自身存在多个类似NSURLConnectionDelegate的回调方法列表。

delegate跟block在设计上的理念就是不同的

因为如果block不是面向结果的设计,那么block的代码应该会把请求过程中的所有状态都囊括起来

上面是从设计理念来说,接下来聊聊两者的运行效率

大家都知道OC是一门消息发送语言,简单来说,就是[a add: b]; 在消息发送语言中被称作
向接收者a发送了一个名为add:的消息,参数是b

基于runtime的OC在执行这段代码的时候会把它转换成用来发送消息的objc_msgSend函数执行代码。这时候就会变成 objc_msgSend(a, @selector(add:), b);

由于昨天的🐹小课堂里已经聊过objc_msgSend过程中会发生什么,这里就不再重复阐述。但是大家要注意这个过程中会依次 遍历消息缓存列表、本类的消息列表、父类的消息列表来直到找到对应的执行代码为止,因此即便苹果做成了汇编的调用,消耗还是有的

那么block的调用消耗呢?

在runtime里面,提供了imp跟block的相互转换。imp是一种用来把代码表示成变量的类型,简单来说跟block的性质是一样的。在objc_msgSend的过程中,目的是为了找到@selector()对应的IMP代码,然后执行

因此block的调用等同于直接调用IMP,省去了查找IMP的过程,从效率上来说胜于代理

是runtime提供了imp跟block的转换方法。实际上block跟imp都等同于指向某段代码的地址,所以效率更高

主要是block->invoke()的调用效率确实太高。至于block内部的结构实现,这个暂且不说

从使用难度上来说,这个包括debug难度跟使用难度,先从debug难度说。因为要我去写一段崩溃的delegate跟block总觉的蛋疼,所以没图

上面也说了,delegate的回调过程中因为涉及到了objc_msgSend的过程,如果在这个过程中发生了异常崩溃,那么在控制台会输出消息发送错误的异常信息,可以比较容易的定位到错误位置

其二,delegate可以通过command+左键的方式跳转到代理方处,所以也能快速的查找到代理双方。从debug的角度来说,难度较低

至于block,通过上面我发的结构图和调用的流程来说,不存在消息发送的过程。因为block是内部结构直接存在函数的指针,直接调用,另一方面是会引用外部对象。所以相比delegate,一旦发生崩溃,难以定位到崩溃处,崩溃原因也基本是野指针异常

因此从debug难度来说,block弱于delegate

那么接下来从使用的角度来说

上面是一个需求,点击商品会弹出商品信息,要用户输入商品的数量,点击确定后商品对应的单元格会高亮
因此代码的逻辑是:
点击单元格->弹窗->输入数量点击确定->单元格高亮

下面放上block跟delegate的处理代码


首先是两个相关的view的头文件


这个是逻辑代码

block的做法会把处理的逻辑写在一处位置,这样子结构清晰。


然后是delegate的处理,就要声明两个协议


这时候弹窗和高亮的逻辑会被分到两个代理事件中处理,在代码量大的时候,连贯性差
而且必要的时候还要清空过渡变量

上面对比的个人总结是:
在事件回调的处理上,delegate比不上block。原因不仅限于block的灵活性更强(当参数传递),功能性(适用于大多数的场景),甚至可以说block是iOS回调手段中最强大的

那么就有一个问题:既然block这么强大,为什么我们还要使用delegate?

这种是代理优于block的场景之一,因为这些代理的事件第一是没有太多的关联性,第二是类型多,事件状态多

另外其实很多人对delegate纠结的一点是:只要提到了协议,很多人的第一反应就是代理

协议可以提供一套共同的行为,比如动物都能喝水,汽车都要加油.
是一种OC实现多继承的方式

好了,最后是协议除了delegate还能做什么?

NS_DESIGNATED _ INITIALIZER的正确使用

2017年02月08日09:54:04

我们随便点开一个系统类的 .h,会发现有些 init 方法后面跟了这个标记:NS_DESIGNATED_INITIALIZER,比如 NSURL :

UIView:

有的 init 方法后面有,有的没有。这个标记是什么意思呢?这种设计有什么好处呢?~

NS_DESIGNATED _ INITIALIZER的正确使用

有这个标记的方法称为指定构造器,与之相对的称为便捷构造器。地铁上就打中文了,大家明白意思就好

可以说,指定构造器是 核心 的init方法,里面应该做这个类初始化时所有 必须 做的事情。

而便捷构造器是搭便车的方法,它不能独立存在,需要调用指定构造器。

举个例子,UIView里面有两个指定构造器,分别是initWithFrame 和 initWithCoder,代表了这个类两种截然不同的初始化方式,它们互相独立且缺一不可

比如仓鼠喜欢为固定尺寸的UIView写一个便捷init方法叫 initWithDefaultFrame,里面调用initWithFrame后面传预设好的参数,这样它的使用者构造它的时候就不用再设尺寸了

我写的这个就是一个典型的便捷构造器

有一条原则就是子类的指定构造器必须调用父类的指定构造器,也就不难理解了

这样的设计有什么好处呢?我能想到的一点是,当你为一个类添加一个必须初始化的属性时,你只需在指定构造器里对它初始化,就可以保证所有的init方法,包括这个类的所有子类,都能正确初始化这个属性

而不是说,我有10个init方法,就得改10个地方

或者担心子类会绕过我的关键初始化

对这个机制还有兴趣进一步了解的同学,可以参考下swift的文档

TheSwiftProgrammingLanguage–语言指南–构造过程

禅与 Objective-C 编程艺术读书笔记

条件语句

条件语句体应该总是被大括号包围。尽管有时候你可以不使用大括号(比如,条件语句体只有一行内容),但是这样做会带来问题隐患。比如,增加一行代码时,你可能会误以为它是 if 语句体里面的。此外,更危险的是,如果把 if 后面的那行代码注释掉,之后的一行代码会成为 if 语句里的代码。

写条件语言的时候要用大括号

尤达表达式

不要使用尤达表达式。尤达表达式是指,拿一个常量去和变量比较而不是拿变量去和常量比较。

例子:

1
2
3
4
//推荐
if ([myValue isEqual:@42]){···}
//不推荐
if ([@42isEqual: myValue]){···}

nil和BOOL检查

有些框架中检查nil的时候会这样写:

if (nil == myValue){···}

这种写法是像尤达表达式,拿常量和变量去比较。

这样写的原因是为了方便排查错误。我们正常的写法是这样写:

if (myValue == nil){···}

但是有的程序员会丢掉一个=号,像这样:

if (myValue = nil){···}

这样就相当于把nil赋值给了myValue,这样的错误比较难调试。如果反过来写的话nil是不能被赋值的。这样比较好调试。

为了避免这类错误,不要用这种方法去判断是否为真。使用感叹号来进行判断。可以这样去判断:

1
if (!myValue){···}

黄金大道

在使用条件语句编程时,代码的左边距应该是一条“黄金”或者“快乐”的大道。 也就是说,不要嵌套 if 语句。使用多个 return 可以避免增加循环的复杂度,并提高代码的可读性。因为方法的重要部分没有嵌套在分支里面,并且你可以很清楚地找到相关的代码。

推荐:

1
2
3
4
5
6
7
- (void)someMethod {
if (![someOther boolValue]) {
return;
}

//Do something important
}

不推荐:

1
2
3
4
5
- (void)someMethod {
if ([someOther boolValue]) {
//Do something important
}
}

意思就是不要把重要的逻辑代码放在条件语句中,如果重要的逻辑代码中还有条件语句,那这样就会造成循环的复杂。这样的语法格式参考swift中的guard语法。参考我的这篇文章

复杂的表达式

当你有一个复杂的 if 子句的时候,你应该把它们提取出来赋给一个 BOOL 变量,这样可以让逻辑更清楚,而且让每个子句的意义体现出来。

1
2
3
4
5
6
7
BOOL nameContainsSwift  = [sessionName containsString:@"Swift"];
BOOL isCurrentYear = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession = nameContainsSwift && isCurrentYear;

if (isSwiftSession) {
// Do something very cool
}

三元运算符

三元运算符 ? 应该只用在它能让代码更加清楚的地方。 一个条件语句的所有的变量应该是已经被求值了的。类似 if 语句,计算多个条件子句通常会让语句更加难以理解。或者可以把它们重构到实例变量里面。

推荐:

result = a > b ? x : y;

不推荐:

result = a > b ? x = c > d ? c : d : y;

推荐:

result = object ? : [self createObject];

不推荐:

result = object ? object : [self createObject];

使用三元运算符是为了让代码更加易读,而不是为了使用它而用它。

错误处理

有些方法通通过参数返回 error 的引用,使用这样的方法时应当检查方法的返回值,而非 error 的引用。

推荐:

1
2
3
4
NSError *error = nil;
if (![self trySomethingWithError:&error]) {
// Handle Error
}

Case语句

除非编译器强制要求,括号在case语句里面是不必要的。但是当一个case包括多行语句的时候,需要加上括号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
switch (condition) {
case 1:
// ...
break;
case 2: {
// ...
// Multi-line example using braces
break;
}
case 3:
// ...
break;
default:
// ...
break;
}

有时候可以使用 fall-through 在不同的 case 里面执行同一段代码。一个 fall-through 是指移除 case 语句的 “break” 然后让下面的 case 继续执行。

1
2
3
4
5
6
7
8
9
switch (condition) {
case 1:
case 2:
// code executed for values 1 and 2
break;
default:
// ...
break;
}

当在 switch 语句里面使用一个可枚举的变量的时候,default 是不必要的。比如:

1
2
3
4
5
6
7
8
9
10
11
switch (menuType) {
case ZOCEnumNone:
// ...
break;
case ZOCEnumValue1:
// ...
break;
case ZOCEnumValue2:
// ...
break;
}

此外,为了避免使用默认的 case,如果新的值加入到 enum,程序员会马上收到一个 warning 通知

枚举类型

当使用 enum 的时候,建议使用新的固定的基础类型定义,因它有更强大的的类型检查和代码补全。 SDK 现在有一个 宏来鼓励和促进使用固定类型定义 - NS_ENUM()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//推荐
typedef NS_ENUM(NSUInteger, ZOCMachineState) {
ZOCMachineStateNone,
ZOCMachineStateIdle,
ZOCMachineStateRunning,
ZOCMachineStatePaused
};
//不推荐
typedef enum{
ZOCMachineStateNone,
ZOCMachineStateIdle,
ZOCMachineStateRunning,
ZOCMachineStatePaused
}ZOCMachineState;

命名

尽可能的遵守Apple的命名约定,尤其是和 内存管理规则 (NARC) 相关的地方。推荐使用长的、描述性的方法和变量名。

例如:

1
2
3
4
//推荐
UIButton *settingsButton;
//不推荐
UIButton *setBut;

Constants常量

常量应该以驼峰法命名,并以相关类名作为前缀。

例如:

1
2
3
4
//推荐
static const NSTimeInterval ZOCSignInViewControllerFadeOutAnimationDuration = 0.4;
//不推荐
static const NSTimeInterval fadeOutTime = 0.4;

推荐使用常量来代替字符串字面值和数字,这样能够方便复用,而且可以快速修改而不需要查找和替换。常量应该用 static 声明为静态常量,而不要用 #define,除非它明确的作为一个宏来使用。

1
2
3
4
5
6
//推荐
static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static const CGFloat ZOCImageThumbnailHeight = 50.0f;
//不推荐
#define CompanyName @"Apple Inc."
#define magicNumber 42

常量应该在头文件中以这样的形式暴露给外部:

1
extern NSString *const ZOCCacheControllerDidClearCacheNotification;

并在实现文件中为它赋值。

只有公有的常量才需要添加命名空间作为前缀。尽管实现文件中私有常量的命名可以遵循另外一种模式,你仍旧可以遵循这个规则。

方法

方法名与方法类型 (-/+ 符号)之间应该以空格间隔。方法段之间也应该以空格间隔(以符合 Apple 风格)。参数前应该总是有一个描述性的关键词。尽可能少用 “and” 这个词。它不应该用来阐明有多个参数。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
//推荐
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;

//不推荐
- (void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height; // Never do this.

字面值

多用字面量语法,少用与之等价的方法。

例如:

1
2
3
4
5
6
7
8
9
10
//推荐
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
//不推荐
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];

如果要用到这些类的可变副本,我们推荐使用 NSMutableArray, NSMutableString 这样的类。
应该避免下面这样:

NSMutableArray *aMutableArray = [@[] mutableCopy];

这种写法效率和可读性都有问题。

效率方面,一个不必要的不可变对象被创建后立马被废弃了;虽然这并不会让你的 App 变慢(除非这个方法被频繁调用),但是确实没必要为了少打几个字而这样做。

可读性方面,存在两个问题:第一个问题是当你浏览代码并看见 @[] 的时候,你首先联想到的是 NSArray 实例,但是在这种情形下你需要停下来深思熟虑的检查;

另一个问题是,一些新手以他的水平看到你的代码后可能会对这是一个可变对象还是一个不可变对象产生分歧。他/她可能不熟悉可变拷贝构造的含义(这并不是说这个知识不重要)。

当然,不存在绝对的错误,我们只是讨论代码的可用性(包括可读性)。

类名

类名应该以三个大写字母作为前缀(双字母前缀为 Apple 的类预留)。尽管这个规范看起来有些古怪,但是这样做可以减少 Objective-c 没有命名空间所带来的问题。

一些开发者在定义模型对象时并不遵循这个规范(对于 Core Data 对象,我们更应该遵循这个规范)。我们建议在定义 Core Data 对象时严格遵循这个约定,因为最终你可能需要把你的 Managed Object Model(托管对象模型)与其他(第三方库)的 MOMs(Managed Object Model)合并。

你可能注意到了,这本书里类的前缀(不仅仅是类,也包括公开的常量、Protocol 等的前缀)是ZOC。

另一个好的类的命名规范:当你创建一个子类的时候,你应该把说明性的部分放在前缀和父类名的在中间。

举个例子:如果你有一个 ZOCNetworkClient 类,子类的名字会是ZOCTwitterNetworkClient (注意 “Twitter” 在 “ZOC” 和 “NetworkClient“ 之间); 按照这个约定, 一个UIViewController 的子类会是 ZOCTimelineViewController.

Initializer 和 dealloc 初始化

Linux crontab定时任务

通过crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script脚本。这个命令非常适合周期性的日志分析或数据备份等工作。

命令格式和参数

crontab [-u user] { -e | -l | -r }

  • -u user :用来设定某个用户的crontab服务
  • file:将这个文件作为crontab的任务列表文件并载入crontab

crontab文件格式

m h dom mon dow command

从第一列开始分别代表

  • 分钟 0~59
  • 小时 0~23
  • 日 0~31
  • 月 1~12
  • 星期 0~7
  • 要执行的的命令

crontab常用命令

列出crontab文件

1
crontab -l

编辑crontab文件

1
crontab -e

删除crontab文件

1
crontab -r

crontab实例

实例1:每分钟执行一次Command

* * * * * Command

实例2:每小时第3分钟执行一次Command

3 * * * * Command

实例3:每隔两天的0点0分执行Command

0 0 */2 * * Command

实例4:每天0点0分执行Command

0 0 * * * Command

实例5:每天删除指定日期前的文件

0 2 * * * find /farm/upload_tmp/ -type f -mtime +1 -print0 | xargs -0 rm

解释:每天2点执行后面的命令。命令是查找一天以前的文件并删除

shell脚本一键生成icon

在iOS开发中icon需要很多尺寸。具体需要哪些在这篇文章中有介绍。
所以有的时候需要我们去处理些图片。当然我们可以直接使用sketch生成相应的icon,但是因为需要的种类比较多,这样不够方便。所以打算写一个脚本,给一个icon可以生成我要需要的icon。

首先分析我们的需求只要简单的图片大小的改变和给图片重命名就可以完成我们的需求。搜索后查到mac上有sips可以处理,也可以用ImageMagicks。ImageMagick支持linux。

Mac下使用sips

安装

Mac下自带这个sips不需要安装

脚本

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
#!/bin/bash
fileName=$1
echo "开始生成图标······"
sips -z 20 20 $fileName --out icon20@1x.png
sips -z 40 40 $fileName --out icon20@2x.png
sips -z 60 60 $fileName --out icon20@3x.png
echo "20pt图标生成······"
sips -z 29 29 $fileName --out icon29@1x.png
sips -z 58 58 $fileName --out icon29@2x.png
sips -z 87 87 $fileName --out icon29@3x.png
echo "29pt图标生成······"
sips -z 40 40 $fileName --out icon40@1x.png
sips -z 80 80 $fileName --out icon40@2x.png
sips -z 120 120 $fileName --out icon40@3x.png
echo "40pt图标生成······"
sips -z 50 50 $fileName --out icon50@1x.png
sips -z 100 100 $fileName --out icon50@2x.png
sips -z 150 150 $fileName --out icon50@3x.png
echo "50pt图标生成······"
sips -z 57 57 $fileName --out icon57@1x.png
sips -z 114 114 $fileName --out icon57@2x.png
sips -z 171 171 $fileName --out icon57@3x.png
echo "57pt图标生成······"
sips -z 60 60 $fileName --out icon60@1x.png
sips -z 120 120 $fileName --out icon60@2x.png
sips -z 180 180 $fileName --out icon60@3x.png
echo "60pt图标生成······"
sips -z 72 72 $fileName --out icon72@1x.png
sips -z 144 144 $fileName --out icon72@2x.png
sips -z 216 216 $fileName --out icon72@3x.png
echo "72pt图标生成······"
sips -z 76 76 $fileName --out icon76@1x.png
sips -z 152 152 $fileName --out icon76@2x.png
sips -z 228 228 $fileName --out icon76@3x.png
echo "76pt图标生成······"
sips -z 167 167 $fileName --out icon83.5@2x.png
echo "83.5pt图标生成······"
echo "全部图标生成······"

解释

sips -z 167 167 $fileName --out icon83.5@2x.png

-z 后面是宽度和高度 $filName 是你要处理的图片 –out是保存处理后的图片

关于sips的其他命令可以执行man sips或者sips --help查看。
这里方便Xcode识别所以用@1x、@2x、@3x来命名。

使用

在终端打开直接执行这个脚本后面跟上要处理的图片就可以了。图片分辨率不能很低于228x228并且保证比例为1:1。例如:

./ios_icon_shell_sips.sh icon500.png

这里就是处理的icon500.png这个图片,在当前目录下生成了所以需要的图片。

使用ImageMagick

安装

ImageMagic的安装请参考这篇文章

脚本

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
#!/bin/bash

fileName=$1
echo "开始生成图标······"
convert -resize 20x20 $fileName icon20@1x.png
convert -resize 40x40 $fileName icon20@2x.png
convert -resize 60x60 $fileName icon20@3x.png
echo "20pt图标生成······"
convert -resize 29x29 $fileName icon29@1x.png
convert -resize 58x58 $fileName icon29@2x.png
convert -resize 87x87 $fileName icon29@3x.png
echo "29pt图标生成······"
convert -resize 40x40 $fileName icon40@1x.png
convert -resize 80x80 $fileName icon40@2x.png
convert -resize 120x120 $fileName icon40@3x.png
echo "40pt图标生成······"
convert -resize 50x50 $fileName icon50@1x.png
convert -resize 100x100 $fileName icon50@2x.png
convert -resize 150x150 $fileName icon50@3x.png
echo "50pt图标生成······"
convert -resize 57x57 $fileName icon57@1x.png
convert -resize 114x114 $fileName icon57@2x.png
convert -resize 171x171 $fileName icon57@3x.png
echo "57pt图标生成······"
convert -resize 60x60 $fileName icon60@1x.png
convert -resize 120x120 $fileName icon60@2x.png
convert -resize 180x180 $fileName icon60@3x.png
echo "60pt图标生成······"
convert -resize 72x72 $fileName icon72@1x.png
convert -resize 144x144 $fileName icon72@2x.png
convert -resize 216x216 $fileName icon72@3x.png
echo "72pt图标生成······"
convert -resize 76x76 $fileName icon76@1x.png
convert -resize 152x152 $fileName icon76@2x.png
convert -resize 228x228 $fileName icon76@3x.png
echo "76pt图标生成······"
convert -resize 167x167 $fileName icon83.5@2x.png
echo "83.5pt图标生成······"
echo "全部图标生成······"

解释

convert -resize 167x167 $fileName icon83.5@2x.png

-resize 后面跟分辨率宽度和高度 $fileName为要处理的图片
后面跟的是处理后的图片

注意

目前在最新版的macOS sierra(10.12)上不支持。在linux和老版本macOS上可以使用。

目前这个脚本还比较简单,只是实现了最基本的功能。并没有对输入文件进行格式判断,与分辨率判断。后面有时间继续完善。

ImageMagick安装与使用

ubuntu下安装ImageMagick

直接使用apt-get安装就可以了

sudo apt-get install imagemagick

安装成功后使用

convert -version

如果看到版本信息,说明安装成功。

Mac下安装ImageMagick

首先,目前macOS Sierra(10.12)上不能用。而且通过homebrew安装失败,通过Macports安装也不成功。通过源码编译安装后,安装成功但是不能使用。在最新的系统上还不支持,之前的系统应该是支持的。这里记录一下安装过程。具体也可以直接查看官方文档。

首先分析一下安装遇到的问题:

使用homebrew安装的时候使用brew install imagemagick安装。执行时会提示下面的错

首先是警告:目前没有发布版本给最新的10.12系统。可能会编译错误。
后面还有一个错误是下载依赖包失败。打开错误地址没法访问。应该是地址换过了。更新下homebrew再安装应该可以执行。但是我这里更新homebrew时卡住不动。所以我选择了使用源码编译安装。

源码编译安装:

下载地址:官网地址

可以看到官网只有10.11版本的没有兼容最新版的10.12。
下载下来后,把压缩包拷贝到主目录下。

打开终端:

cd $HOME

解压:

tar xvzf ImageMagick-x86_64-apple-darwin15.2.0.tar.gz

设置MAGICK_HOME环境变量设置为你解压后的目录

export MAGICK_HOME="$HOME/ImageMagick-7.0.4"

如果解压包的bin目录不在可执行的搜索路径中,要将它添加到PATH环境变量。

export PATH="$MAGICK_HOME/bin:$PATH"

设置DYLD_LIBRARY_PATH环境变量:

export DYLD_LIBRARY_PATH="$MAGICK_HOME/lib/"

macOS Sierra下安装MacPorts

应为需要想要安装ImageMagick,ImageMagick是一个图片处理库。搜索了安装教程后发现可以使用。homebrew安装,Macport安装,还有就是下载后编译安装。

因为使用homebrew安装失败,而且官网的安装说明是使用的MacPorts,因为没有使用过MacPorts所以打算尝试使用一波。

  1. 首先要安装Xcode,直接在AppStore中下载安装就可以。
  2. 安装MacPorts。

官方推荐了两种方法。一是直接使用pkg包安装,但是我使用这种方法安装,会一直卡在Running package scripts这一步。具体原因不知道。所以我使用的是手动编译安装。下面是手动安装步骤:

  • 下载源码
1
2
$ curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.3.5.tar.bz2
$ tar xf MacPorts-2.3.5.tar.bz2
  • 编译安装
1
2
3
4
$ cd MacPorts-2.3.5/
$ ./configure
$ make
$ sudo make install
  • 设置系统变量
1
2
3
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
export MANPATH=/opt/local/share/man:$MANPATH
export DISPLAY=:0.0

iOS基础系列之App生命周期

应用程序的执行状态

在任何特定的时刻,你的应用都会处于一种状态下。而且系统会根据系统中发生的操作将你的应用从一个状态移动到另一个状态。


State (状态) Description(描述)
Not running(未运行) 应用并未启动,或者正在运行但是被系统终止
Inactive(非活动) 应用正在前台运行,但是当前没有接收事件。一个应用通常只会短暂处于这种状态,它可能正过渡到其他状态
Active (活动) 应用正在前台运行,并正在接收事件。这是前台应用程序的正常模式
Background (后台) 应用在后台运行并执行代码,大多数应用进入后台后不久就会进入到暂停状态。但是应用可以保持一段时间在后台
Suspended (暂停) 应用程序在后台,但是不执行代码。系统会自动将应用程序调整到这种状态。处于这种状态的时候应用会保存在内存中,但是不执行代码。当打开程序多的时候,系统会自动清除内存中已经暂停的应用为前台应用腾出更多空间

代理方法

大多数的状态转换都伴随调用你的应用的代理方法。这些方法是你处理状态改变的合适时机。

  • application:willFinishLaunchingWithOptions:应用将要进入初始化操作开始启动应用
  • application:didFinishLaunchingWithOptions:应用完成了初始化操作,开始运行
  • applicationDidBecomeActive:应用进入活跃状态,接收事件
  • applicationWillResignActive:应用将要进入非活跃状态。比如电话来了,或者双击home键
  • applicationDidEnterBackground:应用处于后台状态
  • applicationWillEnterForeground:应用将要进入前台执行
  • applicationWillTerminate:应用退出前执行

应用开始运行按顺序执行了

  • willFinishLaunchingWithOptions
  • didFinishLaunchingWithOptions
  • applicationDidBecomeActive

点击home键后执行了

  • applicationWillResignActive
  • applicationDidEnterBackground

双击home键重新进入app

  • applicationWillEnterForeground
  • applicationDidBecomeActive

双击home键 (不做操作的话一直处于这个状态)

  • applicationWillResignActive

关闭应用

  • applicationDidEnterBackground
  • applicationWillTerminate

iOS开发中AppIcon设置

名词解释

PX(Pixel):像素点,电子屏幕上组成图像的最基本单位,相对大小。

PT(Point):点,印刷行业常用单位,是一个标准长度,绝对大小。1pt=1/72英寸。在iOS开发中是基准单位

PPI(Pixel Per Inch): 图像分辨率,每英寸包含的像素数。

DPI(Dots Pre Inch):打印分辨率,像素密度,每英寸包含的打印点数。

开发中需要的Icon的尺寸

打开Assets.xcassets打开AppIcon可以看到里面需要的图片尺寸。
这里用通用设备举例,包含iPhone和iPad中的Icon。

例如iPhone Notification iOS7-10 20pt需要2x和3x的图片。意思就是需要分辨率为40x40(单位是px)的图片,和60x60的图片。我使用的是Sketch。直接设置成20x20的图片然后导出2x和3x的图片。
从截图中可以看到有20、29、40、50、57、60、72、76、83.5九种格式的Icon。并且需要的倍率也很清楚。按照相应的要求在Sketch中导出需要倍率的Icon就可以了。

Mac下配置免密码登录到阿里云

本机创建公钥

打开终端输入ls ~/.ssh

如果有公钥就不需要再去生成公钥,如果没有的话需要现在本地先生成公钥使用ssh-keygen -t rsa命令生成公钥。执行成功后会生成两个文件,没有后缀名的为私钥,后缀名为.pub的为公钥。

复制公钥到服务器

简单方法

Mac上可以借助ssh-copy-id工具。

首先使用homebrew安装ssh-copy-id终端下执行brew install ssh-copy-id

然后执行ssh-copy-id username@hostname #将username和hostname替换为你的ssh服务器用户名和IP
会提示你输入密码。

通用方法

手动复制公钥内容到服务器

可以使用cat命令查看公钥中的内容。
添加到.ssh/authorized_keys文件中

也可以先把公钥拷贝到服务器上在复制公钥内容到authorized_keys文件中。
scp ~/.ssh/id_rsa.pub username@hostname:~/
将公钥文件复制至ssh服务器

ssh username@hostname
使用用户名和密码方式登录至ssh服务器

cat id_rsa.pub >> .ssh/authorized_keys
将公钥文件id_rsa.pub文件内容追加到authorized_keys中

ssh免密码的登录

使用ssh命令登录到服务器不在需要密码
ssh username@hostname

LeetCode算法笔记

1. Two Sum

Two Sum题目地址

Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,

return [0, 1].

题目意思:

给一个Int数组nums,一个Int值target,查找数组中是否存在两个元素相加等于target的值,存在的话返回两个值在数组中的下标。

解题思路:

  1. 循环遍历
  2. hash

循环遍历
循环嵌套,复杂度为O(n*n)

hash
遍历一遍数组,存到map中,再遍历一遍找到符合的值。复杂度O(n)

循环遍历代码实现:(swift实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// - Parameters:
/// - nums: 输入的Int数组
/// - target: 相加得到的值
/// - Returns: 返回结果为[Int]
func TwoSum(_ nums: [Int], _ target: Int) -> [Int] {
for i in 0 ..< nums.count {
for j in i+1 ..< nums.count{
if nums[j] == target - nums[i]{
let result = [i,j]
print(result)
return result
}
}
}
return []
}

hash代码实现:

1
//todo