Unix常用命令

在Mac的使用过程中,为了灵活的操作,开发人员经常需要使用命令方式进行操作,以下列举了一些经常使用的命令:

常用命令

1.删除当前文件夹及文件夹下所有的东西

1
2
rm -rf [source] 
eg:rm -rf xq_commonview //已经处于当前同级目录,如果不是则要写完整的路径

2.删除当前文件夹下的所有的文件(不包含文件夹)

1
2
rm [source]/* 
eg:rm xq_commonview/* //已经处于当前同级目录,如果不是则要写完整的路径

3.删除当前文件夹下的所有的文件(包含文件夹)

1
2
rm -rf [source]/* 
eg:rm -rf xq_commonview/* //已经处于当前同级目录,如果不是则要写完整的路径

4.复制某文件夹下的所有内容到另外文件夹下(包含文件夹)

1
2
cp -r [source]/* [targetSource]
eg:cp -r XQCommonView/* XQ_CommonView //已经处于当前同级目录,如果不是则要写完整的路径

5.复制某文件夹下的所有内容到另外文件夹下(不包含文件夹)

1
2
cp [source]/* [targetSource]
eg:cp XQCommonView/* XQ_CommonView //已经处于当前同级目录,如果不是则要写完整的路径

6.在当前目录下面创建文件夹命令

1
2
mkdir [name] 
eg: mkdir hexo //在当前文件夹下创建了一个hexo文件夹

7.在mac下面使用open命令快速打开文件或者软件,或者使用特定的软件打开特定的文件

1
2
3
4
open [fileName][appName]
open -a [appName] [fileName]
eg: open /Applications/kugou.app //"注意注意open之后需要空格再输入斜杠 / "
eg: open -a MacDown.app 查缺补漏.md //在当前目录下面使用MacDown.app 打开macdown文件

使用Cocoapods创建私有podspec

Spec Repo

什么是Spec Repo?其实它只是pods的一个索引,也就是一个容器,所有公开的pods都在这个里面,https://github.com/CocoaPods/Specs.git它实际是一个Git残酷remote端口,当你使用了Cocoapods后它会被默认的clone到本地~/.cocoapods/repos下的master文件夹下,这个就是官方的Spec Repo。注意在安装Cocoapods的时候,可能会在安装官方的Spec Repo文件会失败,但是并不会影响pod的使用,并且不会提示任何错误,但是在执行pod install或者search等命令会提示失败,这个会自动重新请求clone官方Spec Repo到本地。会出现如下:

1
Setting up CocoaPods master repo

这个过程时间会比较长。其文件结构如下,每个库都应的都有多个version,每个version下面有tag对应的.podspec文件:

1
2
3
          CocoaPods-version.yml      ......        
master -- Specs---------------------3DTouchHelper--VersionName---3DTouchHelper.podspec
README.md ......

CocoaPods

iOS 最新版 CocoaPods 的安装流程

1.移除现有Ruby默认源

$gem sources –remove https://rubygems.org/

2.使用新的源

$gem sources -a https://ruby.taobao.org/

3.验证新源是否替换成功

$gem sources -l

4.安装CocoaPods

(1) $sudo gem install cocoapods

备注:苹果系统升级 OS X EL Capitan 后改为 $sudo gem install -n /usr/local/bin cocoapods

(2) $pod setup

5.更新gem

$sudo gem update –system

  1. 新建工程,并在终端用cd指令到文件夹内

$pod search 第三方

问题

错误1:

Error fetching http://ruby.taobao.org/:

bad response Not Found 404 (http://ruby.taobao.org/specs.4.8.gz)

解决方案:把安装流程中 $gem sources -a http://ruby.taobao.org/ —改为—-> $gem sources -a https://ruby.taobao.org/

错误2:

ERROR: While executing gem … (Errno::EPERM)

Operation not permitted - /usr/bin/pod

解决方案:苹果系统升级OS X EL Capitan后会出现的插件错误,将安装流程 4.安装CocoaPods 的 (1)sudo gem install cocoapods ——>改为sudo gem install -n /usr/local/bin cocoapods

错误3:

[!] Unable to satisfy the following requirements: - AVOSCloud (~> 3.1.6.3) required by Podfile

Specs satisfying the AVOSCloud (~> 3.1.6.3) dependency were found, but they required a higher minimum deployment target.

解决方案:安装流程:Podfile文件 中 platform:ios, ‘6.0’ 后边的 6.0 是平台版本号 ,一定要加上

创建podfile

使用终端进入项目所在的目录,输入touch podfile命令,会看到该目录下面创建了一个podfile文件,修改podfile文件添加

1
2
platform:ios, '7.0'
pod 'AFNetworking'

在终端中输入pod install 命令,会看到到同目录下面创建了一个xcworkspace的工程。

创建私有Spec Repo

看了官方的Spec Repo后,我们需要创建一个类似的~/.cocoapods/repos的master的Spec Repo文件,这里我们可以fork官方,也可以自己创建。建议自己创建比较好,由于官方的东西实在是太多。执行后会生成TwwSpecs.podspec文件,需要在自己的github上创建一个repositories。然后修改项目文件TwwSpecs.podspec内容

1
pod repo add TwwSpecs https://github.com/Tweiwei497435786/TwwSpecs.git

创建成功后你会看到在~/.cocoapods/repos目录生成了一个TwwSpecs文件夹,那么这个就是我们私有的Spec Repo文件了。需要注意的就是如果项目中有其他同事共同开发的话,你还要给他这个Git仓库的权限。

创建pod库

创建pod库参考创建私有pod库。创建私有pod库成功后,会看到如下的目录内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
tree TwwTest -L 2
TwwTest
├── Example
│   ├── Podfile
│   ├── Podfile.lock
│   ├── Pods
│   ├── Tests
│   ├── TwwTest
│   ├── TwwTest.xcodeproj
│   └── TwwTest.xcworkspace
├── LICENSE
├── README.md
├── TwwTest
│   ├── Assets
│   └── Classes
├── TwwTest.podspec
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj

需要注意的是我们需要修改TwwTest.podspec文件的相关内容最低版本,包括真实的目录路径。

1
2
3
4
5
s.version          = '0.1.0'
s.ios.deployment_target = '8.0'
s.homepage = 'https://github.com/<GITHUB_USERNAME>/TwwTest'
s.source = { :git => 'https://github.com/<GITHUB_USERNAME>/TwwTest.git', :tag => s.version.to_s }
.....

关于TwwTest.podspec各项表示的意思,参考官网内容。在这里需要注意的是s.version默认了版本,在此时我们需要手动创建tag:

1
2
git tag -m "first add tag" "0.1.0" //创建tag,并提交信息 
git push --tags //推送tag到服务器

操作成功后,我们需要将创建好的私有pod库提交到我们私有Spec Repo中进行管理,在terminate中输入:

1
pod repo push TwwSpecs TwwTest.podspec --allow-warnings//将私有pod库TwwTest的TwwTest.podspec提交到私有repo中管理TwwSpecs,允许警告出现。

经过上面的操作那么,我们就将TwwTest的pod库的0.1.0的tag放到TwwSpecs的私有repo中进行管理。

维护私有podspec

需要注意的是,每次我们更改私有的pod库之后,在打上tag之前,我们需要修改私有pod库TwwTest的TwwTest.podspec文件,更改version信息版本:

1
s.version          = '0.1.0'

同时需要为私有pod库打上tag

1
2
git tag -m "first add tag" "0.1.1" //创建tag,并提交信息 
git push --tags //推送tag到服务器

然后需要push到specs中进行管理:

1
pod repo push TwwSpecs TwwTest.podspec --allow-warnings//将私有pod库TwwTest的TwwTest.podspec提交到私有repo中管理TwwSpecs,允许警告出现。

此时如果其他协同开发人员遇到,找不新的版本问题,需要其他开发人员本地操作:

1
pod update repo

项目引用

在我们的主要项目中的podfile文件加入以下内容:

1
2
3
4
5
6
source 'https://github.com/Tweiwei497435786/TwwSpecs.git'
source 'https://github.com/CocoaPods/Specs.git'

target "TwwTestProject" do
pod 'TwwTest', '~> 1.0.0'
end

问题

遇到pod repo布署更新的时候会遇到pod repo 不成功的情况,一般情况下会存在以下问题。

1.遇到警告导致repo不成功,此时我们可以使用以下–allow-warnings修饰.

1
pod repo push TwwSpecs TwwTest.podspec --allow-warnings --verbose //允许警告,并且打印出详细的verbose信息

2.由于使用了libiries的情况下我们需要在podspec里面添加相应的内容。

1
s.libraries = "c++","z" //表示要使用c++,libz的库

3.出现了以下问题的时候:

1
include of non-modular header inside framework module

在repo的时候需要加上–use-libraries修饰

1
pod repo push TwwSpecs TwwTest.podspec --allow-warnings --use-libraries --verbose

参考

1.使用Cocoapods创建私有podspec

2.cocoapods官网

国庆节杭州骑行胶东半岛计划

出行概要

活动时间:2016.9.28–2016.10.7

活动目的:骑游,赏景

道路状况:平坦路面为主

出发时间:2016.9.27

回程时间:2016.10.7

路程:1200KM+

活动强度:强

活动费用:含路费计划1500以内

联系人:Tweiwei497435786[微信]

地图

总行者路书编号:675752

Markdown preferences pane

分段路书编号:见下面每天行程

Markdown preferences pane

路线

Day1 杭州乘坐大巴到南通南通汽车客运东站(0KM)

9.28 周三16:30(杭州北站)集合出发乘坐大巴到南通,预计(晚上21:00左右达到南通,提前预定好宾馆住宿入住。

杭州天气:

南通天气:

说明:

(1)杭州 - 南通,选择当天最晚时间点班次的汽车,汽车费用(120不包含自行车托运),汽车预计耗费4个小时。

Day2 南通汽车客运东站-盐城(150KM)

9.29 周四6:30起床,南通汽车客运东站附近吃完早餐,预计下午6:30左右到达盐城市,选择住宾馆或者帐篷。

路况:S225,G228(路书编号:675795)

沿路风景名胜:

南通天气:

盐城天气:

说明:

Day3 盐城市-连云港(160KM)

9:30 周五6:30起床,收拾整理吃完早餐出发,预计下午7:00左右到达连云港连云区,选择在连云港港务局附近露营。

路况:G228(路书编号:675795)

沿路风景名胜:

盐城天气:

连云港天气:

说明:

Day4 连云港-日照市区(170KM)

10.1 周六6:30起床,收拾整理吃完早餐出发,预计下午7:30左右到达日照市区,选择在市区住宾馆或者海边露营。

路况:G228,G204,滨海沿路,碧海路(路书编号:570172)

沿路风景名胜:

连云港天气:

日照市天气:

说明:

Day5 日照市-青岛市(135KM)

10.2 周日6:30起床,收拾整理吃完早餐出发,预计下午4:00左右到达青岛市(崂山国家森林公园),选择在海边露营。

路况:G204,S293,S329,薛家岛港,S214(路书编号:570181)

沿路风景名胜:

日照市天气:

青岛市天气:

说明:路程相对较少,青岛有许多好玩的地方,可以多晚一些地方,选择海边露营。

Day6 青岛-乳山市(140KM)

10.3 周一6:30起床,收拾整理吃完早餐出发,预计下午5:00左右到达青岛市(崂山国家森林公园),选择住宿宾馆。

路况:S293,S202(路书编号:570189)

沿路风景名胜:

青岛市天气:

乳山市天气:

说明:

Day7 乳山市-威海市(215KM)

10.4 周二6:30起床,收拾整理吃完早餐出发,预计下午5:00左右到达青岛市(崂山国家森林公园),选择住宿宾馆。

路况:S293,S202(路书编号:570199)

沿路风景名胜:

乳山市天气:

威海市天气:

说明:该段路程规划的沿海骑行,可能路线是这几天中最重的路线。(该段路线较差或者沿路风景一般)可考虑走直线距离直接到达威海市。

Day8 威海市-烟台市 - 蓬莱市(150KM)

10.5 周三6:30起床,收拾整理吃完早餐出发,预计下午6:00左右到达蓬莱市,选择露营或者住宿宾馆。

路况:S302,S264,G206(路书编号:570222)

沿路风景名胜:

威海市天气:

烟台市天气:

蓬莱市天气:

说明:

Day9 蓬莱市-潍坊市(195KM)

10.6 周四6:30起床,收拾整理吃完早餐出发,预计下午8:00左右到达潍坊市,选择住宿宾馆。

路况:G206(路书编号:570225)

沿路风景名胜:

蓬莱市天气

潍坊天气:

说明:

Day10 潍坊市- 杭州(高铁)

10.7 周五9:30起床,收拾整理吃完早餐出发,下午15:00到达潍坊火车站,乘坐下午15:48的高铁准备回杭州。

路况:

沿路风景名胜:

潍坊天气:

杭州天气:

说明:

(1)乘坐G256次高铁回杭州,时长6个小时左右,费用450,预计晚上21:40到达火车东站。

预计费用

名称 费用 备注
住宿 4 * 80 = 320 自带帐篷+宾馆,总共出行9天,按照一半住宿宾馆,费用80/天
餐费 9 * 50 = 450 餐费包含水
自行车托运 70 + 120 = 210 去大巴直接随车,回程办理火车托运非随车
车费 120 + 450 = 570 大巴,高铁
其它费用 未知 包含个人消费等
总计 1550 当前费用为大概统计,具体依据消费而定

注意事项

1.由于时间关系,本计划只是大概的行程计划,本次出行时间不低于8天,后期可考虑裁剪路线或者增加出行日程,大家注意时间安排。

2.关于行程中的景点,天气会在出行之前不断完善,期望大家多出主意,可以尽快联系我。

2.期望大家能够在出游之前提出更多的建议,将会根据相关建议做出修改。让大家都能有一个开心,快乐美好的骑行游玩旅程。

3.戴好头盔手套与能量补给;需要带基本衣物,手电,防雨装备,防晒,基本药物等。

4.本次出行采取露营方式,需要自备帐篷。

5.本次活动强度较大骑行9天,需参与人员能够有150KM/天的骑行经验。

免责声明

1.本活动是自发的,有一定的危险性,参加者须对自己的安全负责。活动中当由于意外事故或急性疾病等不可预测因素造成身体损伤时,团队的其他成员会尽全力救助,但不承担任何法律和经济责任,望能理解。

2.任何参与本活动的人均视为同意此免责声明。

参考

1.上海骑行青岛路上拍照

房屋装修之装修步骤

装修的步骤一般包含如下的基本21个步骤:

1.前期设计 – 2.主体拆改 – 3.水电改造 – 4.木工 – 5.贴砖 – 6.刷墙面漆 – 7.热水器安装 – 8.厨卫吊顶 – 9.橱柜安装– 10.烟机灶安装 – 11.木门安装 – 12.地板安装 – 13.铺贴壁纸 – 14. 开关插座安装 – 15.灯具安装 – 16.五金洁具安装 – 17.窗帘杆安装 – 18.拓荒保洁 – 19.家具进场 – 20.家电安装 – 21.家居配饰

接下来将会对21环节拆分讲解及需要注意的事项:

一 前期设计

在前期设计前,需要对房子做一个详细的测量,测量需要包括(下面是原平面图):原设计图,原设计图线路图,墙体厚度,原开关线路的位置等,原来门窗的宽高度,原墙面的宽高度,原开关的位置,不可动墙体部分等具体的位置坐标。

Markdown preferences pane

做完以上的原设计图等内容,是方便重新设计出符合自己装修的图纸,在原来的基础上适当的改拆,进行二次设计。

选定装修风格,开始利用sweethome工具进行初期设计,在原设计图上改拆等具体的都要标注清楚。

1.设计好装修图之后,需要设计好家居的摆放位置,进行渲染后看效果图,确定好所有的家居,灯,家电,五金,等设备摆放位置后,可以根据设计图进行电路图的设计,开关位置,开关数量都需要确定(数量,坐标位置)将这些信息标注都在设计图上面标注清楚。

2.根据墙面等大小,摆放各种家具装置,确定大小位置高度,后期方便家具购买。(注意各家具的大小都需要具体的坐标大小)

参考

1.装修房子的过程

2.史上最详细的房屋装修流程,看懂了再装!不收藏可惜!!!

3.Sweet Home 3D 使用者指南

4.昵图网素材

读《影响力》

简介

这本书是一本社会心理学类的书籍,作为销售人员或者管理者及其从事相关行业人员,这本书是一本难得的好书,从心理学角度如何有技巧的去说服别人。当然如果你不是以上人员也是非常有必要去读一读,书中关于使用相关心里原则如何辨别是真善还是作为获利手段都有相关方法,同时对于如果其他人对你使用相关手段获利,那么你如何防范及从心理层面拒绝都有行之有效的对策,总之这是一本难得的好书。

本书作者罗比特·西奥迪尼,全球最致命的说服术与影响力亚牛权威,分别于北卡罗来纳大学,哥伦比亚大学取得博士与博士后学位,投入说服与顺从行为研究多年。目前是亚利桑那州立大学心理学系教授。

Markdown preferences pane

知识框架

本书内容围绕六大原则展开,分别是互惠,承诺一致,社会认同,喜好,权威,稀缺的社会功能来讨论,以及顺从专家如何将各项原则整合到购买,捐赠,让步,选举,赞成等请求中,使之发挥巨大的力量。本书的首页便有一张类似xmind的思维导图,非常方便读者能够快速的掌握书中的知识结构。

Markdown preferences pane

iOS多线程理解之NSOperation

上一节有讲到过关于NSThread的方式实现多线程,本章将会采用NSOperation实现多线程。使用NSOperation去操作,相对较为简单,只需要将NSOperation放到NSOperationQueue这个队列中,线程就会依次的启动,NSOperationQueue负责管理,执行所有的NSOperation,可以更加方便容易的管理线程总的数量和线程之间的优先级关系。有效的解决NSThread中存在的问题。

在NSOperation中有两个常用子类用于创建线程的操作:

  • NSInvocationOperation
  • NSBlockOperation(block形式组织代码)

NSOperation

可以将其看做线程管理器,对线程进行管理操作。下面看一下其属性和方法

1
2
3
4
5
- (void)addOperation:(NSOperation *)op;//将创建的Operation放入操作队列,`添加到操作队列后,队列会开启一个新的线程执行此操作`
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);//采用block的形式加入操作队列
@property NSInteger maxConcurrentOperationCount;////限制队列同时只运行线程的最大数量
- (void)cancelAllOperations;//取消当前队列的所有操作
.....

NSInvocationOperation

相对于使用NSThread代码减少,不需要管理其线程。

1
2
3
4
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImage:) object:model];
//创建完NSInvocationOperation对象并不会调用,它由一个start方法启动操作,但是注意如果直接调用start方法,则此操作会在主线程中调用,一般不会这么操作,而是添加到NSOperationQueue中
//[invocationOperation start];
[self.operationQueue addOperation:invocationOperation];//注意添加到操作队后,队列会开启一个线程执行此操作

NSBlockOperation

相对于NSInvocationOperation使用更加简单,直接使用block块。可以不必单独定义新的方法,直接在block中写代码即可。可以避免参数传递限制的问题。

1
2
3
4
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{//简洁,避免传递参数的问题
[self loadImage:model];
}];
[self.operationQueue addOperation:blockOperation];
使用NSOperation进行多线程开发可以设置最大并发线程,有效的对线程进行了控制。

线程执行顺序

在之前使用NSThread很难控制线程的执行顺序,但是在NSOperation中就相对比较容易控制,每一个NSOperation可以设置依赖线程,NSOperation提供了addDependency方法可以有效的控制当前queue中的线程之间的依赖关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//创建多个线程用于填充图片
for (int i=0; i<count-1; ++i) {
//方法1:创建操作块添加到队列
//创建多线程操作
NSBlockOperation *blockOperation=[NSBlockOperation blockOperationWithBlock:^{
[self loadImage:[NSNumber numberWithInt:i]];
}];
//设置依赖操作为最后一张图片加载操作
[blockOperation addDependency:lastBlockOperation];

[operationQueue addOperation:blockOperation];

}
//将最后一个图片的加载操作加入线程队列
[operationQueue addOperation:lastBlockOperation];

需要注意的是不可以设置为循环依赖,否则不会被执行。

xcodebuild xcrun simctl

在iOS中打包是一件繁琐且重复耗时的工作,而且手动操作还会遇到一些操作错误的问题。而且随着iOS的渠道增多更加会浪费额外的时间,在前面的文中有讲到过使用jenkins或者fastlane打包的工具,那么本篇将会讲解如果通过xcode提供的的shell编译工具xcodebuild,xcrun,xcrun simtil等进行相应的打包自动化操作。下面会简单的列举一些操作,更多请查看对应的–help。

xcodebuild

xcodebuild负责将工程源文件编译成xxx.app

在terminate中输入以下命令xcodebuild –help:

1
tianww@tortoise-MacBook-Pro xcodebuild --help

可以查看的到xcodebuild的相关的可用命令:

显示xcodebuildversion:xcodebuild –version

显示当前系统安装的sdk:xcodebuild –showsdks

显示当前目录下project Information:xcodebuild –list

需要注意以上命令是需要在当前的工程.xcodeproj下执行

清理iphoneos下的.app文件xcodebuild clean

1
$xcodebuild clean -workspace BeiBeiAPP.xcworkspace -scheme beibei -configuration release -sdk iphoneos SYMROOT=`pwd`/build_yueyu

以下是通过xcodebuild生成workspace类型的.app文件,注意如果是xcodeproj类型打包的话就选择-project,注意此时需要设置好项目的codesign选择自动即可。

1
$xcodebuild -workspace BeiBeiAPP.xcworkspace -scheme beibei -configuration release -sdk iphoneos build SYMROOT=`pwd`/build_yueyu

xcrun

xcrun负责给xxx.app(签名)并打包成xxx.ipa

在terminate中输入以下命令xcrun –help,可以查看到xcrun的相关所有的命令:

1
tianww@tortoise-MacBook-Pro xcrun --help

以下通过.app文件生成ipa文件,并且签名的操作

1
/usr/bin/xcrun -sdk iphoneos PackageApplication -v "$distDir/$appName.app" -o "$distDir/$channelId.ipa" --sign "09E378E966D47195A553BE0EF9F0790D4997A9B5"

其中,参数 v 就是 build 结果中最后一行 app 文件的路径,参数 o 是生成的 ipa 文件的路径。

xcrun simctl

通过xcrun simtil相关命令可以调度模拟器的操作,包括安装卸载启动等。

在terminate中输入以下命令xcrun simctl –help,可以查看到xcrun simctl的相关所有的命令:

1
tianww@tortoise-MacBook-Pro xcrun simctl --help

如下简单的写了一个自动启动模拟器,并安装相关APP,并且自动启动的shell脚本:

1
2
3
4
5
6
7
8
echo "ouput xcrun instuments list-->"
xcrun instruments -s #列举当前机器的所有的instruments列表
echo "select instruments device and run--->"
xcrun instruments -w "iPhone 6s (9.3)#[DEB8527E-87D4-4BCE-BDE3-12CAB9463669] (Simulator)" #根据列举的instruments,调用对应的设备
echo "install app-->"
xcrun simctl install booted ~/Desktop/BridgeLabiPhone.app #安装对应地址下的.app文件到上面选择的设备
echo "run app-->"
xcrun simctl launch booted "com.redcat.shandiangou.daily" #启动模拟器,需要传入对应的bundle identifier

参考

1.iOS自动化的打渠道包解决方案

2.xcodebuild 使用手册

iOS 多线程理解之NSThread

关于iOS中多线程知识已经有无数前辈总结过,文章的好坏参差不齐,多线程的问题似乎是成为一个高手必修炼的内功。如果说在面试过程中面试官必出的一道题,那么非多线程内容不可,iOS中的多线程开发真的有那么难么?下面是本人的一些总结,当然也参考了前人比较好的文章,只有去实践才能深刻的理解,下面会分三篇去讲解。

多线程

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。在一个程序中,独立运行的程序片段叫做“线程”(Thread)。每个正在系统上运行的程序都是一个进程,每个进程包含一到多个线程。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行,线程基本上是轻量级的进程,它负责在单个程序里执行多个任务。通常由操作系统负责多个线程的调度和执行。线程是程序中一个单一的循序控制流程,在单个程序中同时运行多个线程完成不同的工作,称为多线程。

在单一的线程中,一件事情没有处理完林外一件事情就不能开始,这样则会影响用户体验。在单核处理器时期就有多线程,这个时候多线程更多的用于解决线程阻塞造成的用户等待(通常是操作完UI后用户不再干涉,其它的线程在等待队列中,CPU一旦空闲就继续执行,不影响用户其它UI操作),其处理能力并没有变化。如今无论移动还是PC,服务器都是多核处理,于是并行运算就更多的提及。一件事情我们可以划分为多个步骤,在没有顺序要求的情况下使用多线程技能解决线程阻塞又能充分利用多核处理器运行能力。

下图反映了一个包含8个操作的任务在一个有两核心的CPU中创建四个线程运行的情况。假设每个核心有两个线程,那么每个CPU中两个线程会交替执行,两个CPU之间的操作会并行运算。单就一个CPU而言两个线程可以解决线程阻塞造成的不流畅问题,其本身运行效率并没有提高,多CPU的并行运算才真正解决了运行效率问题,这也正是并发和并行的区别。当然,不管是多核还是单核开发人员不用过多的担心,因为任务具体分配给几个CPU运算是由系统调度的,开发人员不用过多关心系统有几个CPU。开发人员需要关心的是线程之间的依赖关系,因为有些操作必须在某个操作完成完才能执行,如果不能保证这个顺序势必会造成程序问题。

General preferences

优点

1.使用线程可以把占据时间长的程序中的任务放到后台去处理

2.程序的运行速度可能加快(多核更突出)

……

缺点

1.大量的的线程,会影响性能,因为操作系统需要在他们之间来回切换

2.更多的线程需要更多的内存空间

3.多线程的情况下可能会带来线程操作不当的bug,需要考虑线程之间的资源共享,线程死锁的情况。

iOS多线程

在iOS中每个应用程序(进程)启动后都会建立主线程(UI线程),这个线程被称作其它线程的父线程。如下代码中展示,在main函数中打印发现主线程已经创建:

1
2
3
4
5
6
int main(int argc, char * argv[]) {
NSLog(@"应用程序启动的时候在main函数中创建了主线程.......:%@",[NSThread currentThread]);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

由于在iOS中除了主线程,其它子线程是独立于Cocoa Touch的,所以只有主线程可以更新UI界面(同时UI的操作是非线程安全的,如果苹果大量UI部分UIKit设计为线程安全的,将会是非常耗性能的,同时会造成资源共享的bug等。当然新版本iOS中,部分UIFont,UIColor绘图等UI操作已经被设计为线程安全的,但是还是建议UI的操作保证在主线程中执行)。iOS中使用多线程并不复杂,关键是如何控制好各个线程的执行的循序,处理好资源竞争问题。常用的多线程开发有三种方式:

  • NSThread
  • NSOperation
  • GCD

当然使用各多线程的方式需要考虑当前具体的需求场景,甚至在项目中需要结合多种方式使用。具体的在了解完各自的具体使用后,具体的分析各自的优缺点及使用场景。

NSThread

NSThread是轻量级的多线程开发,使用起来并不复杂,但是需要管理线程的生命周期(线程的创建create,启动start,调度cancle等),无法有效控制线程数量。可以通过两种方式来创建线程,实例方法,类方法创建如下:

通过实例方法调用(需要管理其生命周期):

1
2
3
//使用alloc的方式创建一个新的线程,不会阻塞当前的ui主线程的操作
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImage:) object:nil];
[myThread start];//启动线程

通过类方法直接创建调用:

1
2
//通过类方法直接隐式创建,每次都会创建一条新的线程
[NSThread detachNewThreadSelector:@selector(loadImage:) toTarget:self withObject:nil];

通过NSThread可以解决线程阻塞的问题,在通过其它线程进行图片等资源下载过程中,保证主线程UI操作不会阻塞。其它线程操作完成,通过调用主线程的方式操作UI。

NSObject (NSThreadPerformAdditions)

为了简单操作,苹果提供了NSOject分类扩展方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface NSObject (NSThreadPerformAdditions)

//在主线程上执行一个方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes

//在指定的线程上执行一个方法,需要用户创建一个线程对象

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
// equivalent to the first method with kCFRunLoopCommonModes

//在后台执行一个操作,本质就是重新创建一个线程执行当前方法
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
@end
多个线程并发(Concurrent)

使用NSThread创建多个线程,下载图片并且在相应的UIImageview中展示,通过以下场景来了解并发:

1
2
3
4
5
6
7
8
- (void)changeImage:(MultiModel *)model
{
NSLog(@"index....:%ld",model.index);
NSData *data = [self requestData:model.imagUrl];
model.imageData = data;
//更新的操作放到主线程中操作
[_multiThreadView performSelector:@selector(updateImageView:) onThread:[NSThread mainThread] withObject:model waitUntilDone:YES];
}

1.当前图片请求操作都在主线程执行,当前主线程阻塞,在点击加载按钮后图片没有请求完成的情况下无法,点击顶部的返回操作,所有图片全部请求完成后一起显示。

1
2
3
//当前主线程阻塞,在点击加载按钮后图片没有请求完成的情况下无法,点击顶部的返回操作
//所有图片全部请求完成后一起显示
[self changeImage:model];

2.每一个图片的展示使用一个NSThread线程进行请求图片的操作。由于selector不能传递多个参数,可以将数据进行对象封装处理。当点击加载后,可以点击返回到主页,不会阻塞当前的主线程的任何操作(返回首页后,子线程依旧输出内容)。虽然图片是按循序请求的,但是图片的显示顺序不是有序的,由于网络的原因每个请求线程时间都不一致。

1
2
3
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(changeImage:) object:model];
thread.name = [NSString stringWithFormat:@"线程%ld",index];
[thread start];

3.为了能够控制线程执行顺序,通过控制最后一张图片最后展示在最后展示,可以通过当前为1张图片的线程操作的时候,使得图片1的线程(线程0)休眠一段时间达到最后展示,实际测试多次发现并非图片1每一都是最后展示。

1
2
3
4
5
6
7
8
9
10
11
12
- (void)delay:(MultiModel *)model
{
NSLog(@"index....:%ld",model.index);
if (model.index == 0) {//控制第一张图片,最后展示
NSLog(@"[NSThread currentThread].....:%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:20];
}
NSData *data = [self requestData:model.imagUrl];
model.imageData = data;
//更新的操作放到主线程中操作
[_multiThreadView performSelector:@selector(updateImageView:) onThread:[NSThread mainThread] withObject:model waitUntilDone:YES];
}

4.当然我们也可以通过修改线程的优先级来控制,图片展示的顺序。线程优先级范围为0~1,值越大优先级越高,每个线程的优先级默认为0.5。修改图片下载方法如下,改变最后一张图片加载的优先级,这样可以提高它被优先加载的几率,但是它也未必就第一个加载:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)priority:(MultiModel *)model
{
NSLog(@"index....:%ld",model.index);
if (model.index == 8) {//控制最后一个最先加载,设置优先级最高
NSLog(@"[NSThread currentThread].....:%@",[NSThread currentThread]);
[NSThread setThreadPriority:1.0];
}
NSData *data = [self requestData:model.imagUrl];
model.imageData = data;
//更新的操作放到主线程中操作
[_multiThreadView performSelector:@selector(updateImageView:) onThread:[NSThread mainThread] withObject:model waitUntilDone:YES];
}

说明NSThread的并发操作,并不能有效的保证执行顺序,由于图片加载的顺序以及网络的原因。

线程的状态

线程的状态有三种如下,都是只读操作:

1
2
3
@property (readonly, getter=isExecuting) BOOL executing NS_AVAILABLE(10_5, 2_0);//判断是否在执行
@property (readonly, getter=isFinished) BOOL finished NS_AVAILABLE(10_5, 2_0);//判断执行是否完成
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);//判断执行是否取消
线程的生命周期

在线程操作过程中可以让某个线程休眠等待,优先执行其他线程操作,而且在这个过程中还可以修改某个线程的状态或者终止某个指定线程。比喻上面并发过程中讲到的线程睡眠,优先级的操作。有下面方法可以控制线程的生命周期,包含类方法和实例方法:

1
2
3
4
5
6
7
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

+ (void)exit;

+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
1
2
3
4
5
6
7
@property (readonly, getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5, 2_0);

- (void)cancel NS_AVAILABLE(10_5, 2_0);

- (void)start NS_AVAILABLE(10_5, 2_0);

- (void)main NS_AVAILABLE(10_5, 2_0); // thread body method

在用户并发加载了图片操作,立马取消所有线程操作。通过线程的状态,改变线程的生命周期。

1
2
3
4
5
6
7
8
9
10
- (void)exitAllThread
{
NSLog(@"取消所有的线程");
//注意如果仅仅是在这里cancel,实际上是不能阻止子线程继续执行,还需要在线程加载数据的时候,取消掉exit
[self.threadArray enumerateObjectsUsingBlock:^(NSThread * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![obj isFinished]) {
[obj cancel];
}
}];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
{
NSLog(@"index....:%ld",model.index);
if (model.index > 6) {
[NSThread sleepForTimeInterval:2];//等待主线程中取消后2s再判断,7,8线程是否取消,取消后则退出当前线程,配合断点不怎好控制
if ([[NSThread currentThread] isCancelled]) {//当前在主线程中已经被cancle了
[NSThread exit];//取消掉当前的线程
}
}
NSData *data = [self requestData:model.imagUrl];
model.imageData = data;
//更新的操作放到主线程中操作
[_multiThreadView performSelector:@selector(updateImageView:) onThread:[NSThread mainThread] withObject:model waitUntilDone:YES];
}

使用NSThread在进行多线程开发过程中操作比较简单,但是要控制线程执行顺序并不容易(前面万不得已采用了休眠的方法),另外在这个过程中如果打印线程会发现循环几次就创建了几个线程,这在实际开发过程中是不得不考虑的问题,因为每个线程的创建也是相当占用系统开销的。

参考

1.本篇文章详细demomultiThreadingTest

2.多线程

3.iOS开发系列–并行开发其实很容易

4.超详细!iOS 并发编程之 Operation Queues

5.浅谈并发与并行

6.并发和并行的区别

房屋装修设计基础

房子总算是买了,房屋装修设计可也是一大麻烦事,纠结于全包还是半包。全包担心最后结果不是自己想要的,质量上可能会打折扣;半包太麻烦,没有那么多时间。有人说“房屋设计装修一直到完工所需的知识不亚于高考”,总之综合各种考虑之后,我选择了半包,基本的设计图纸,装修搭配,材料购买全部自己来负责。想想之后完工的效果,应该是很有成就感吧!

知识储备

1.你需要看大量的各种设计效果图,确定选择自己喜欢的风格。搜罗各种装修技巧,前人遇到的各种坑。那么这些你一定要注意了。

2.电脑操作技巧等。

3.水电设计(大量的参考博文)

4.审美(继续参考)

设计工具

关于房屋平面设计你是不是一无所知,什么CAD等各种各样的设计软件,是不是觉得很迷茫,总之是无从下手。下面介绍的工具等内容,可以让你轻松的实现自己设计效果,并且可以根据自己的喜好实时做出修改。

Sweet Home 3D这款工具可以帮你轻松搞定装修设计图,效果图,操作简单实用。注意这里是Mac OXS版本的,Windows请自行度娘。

由于Sweet Home 3D自带的素材(家居,文理)相对来说较少,不怕万能的淘宝能帮助你解决这个问题,2块钱即可买到1千多款素材,你绝对值得拥有,见地址.

如果各位没有找到满意的模型库,可以在网上搜索模型库,你会找到大量的模型库,但是格式可能是MAX的需要转换为sweethome的格式,关于怎么转换格式把MAX格式的3D模型转换到Sweet Home 3D支持格式的步骤

实践

Sweet Home 3D使用起来还是相当简单的,拖拖拽拽一些控件就可以实时看到3D效果。把所有东西都操作一遍基本一上午就可学会了,很简单吧。我不会告诉你,我对装修设计可是一窍不通。

下面附上我花了一天实践设计品面图,和3D效果图,还是很有成就感的呢,后面会根据情况实际做修改调整:

Markdown preferences pane

Markdown preferences pane

Mac OSX包管理工具

工欲善其事,必先利其器。在window上面安装工具,直接下载一个安装即可。由于Mac OSX是基于unix架构,在linux上面我们多习惯用命令来安装工具。接下来一些强大的Mac管理工具,作为Mac开发人员你不可不知。

Homebrew

Homebrew简称为brew,是Mac OSX上的软件包管理工具,能在Mac中方便的安装软件或者卸载软件, 只需要一个命令, 非常方便。

安装brew只需要在终端输入如下命令:

1
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

搜索软件:brew search 软件名,如brew search wget

安装软件:brew install 软件名,如brew install wget

卸载软件:brew remove 软件名,如brew remove wget

示例:上一文中又讲到安装jenkins使用的就是brew方式安装的:
brew install jenkins

NPM

NPM是一个Node包管理和分发工具,已经成为了非官方的发布node模块(包)的标准。有了npm,可以很快的找到特定服务要使用的包,进行下载、安装以及管理已经安装的包,前端开发者的必备利器。

在这里我们同样先使用brew安装Npm,Node:

1
brew install node

示例:在首篇文章中搭建hexo的时候就是使用npm安装hexo:npm install -g (安装hexo的命令)

Gem

由于在CocoaPods类库管理工具中需要使用,Gem是Ruby(一种跨平台的脚本语言)的环境下的命令,在Mac OXS系统中,已经自动安装了Xcode,Xcode已经安装ruby的环境。