衢山岛旅游攻略

出行概要

活动时间:2018.6.15–2018.6.18

活动目的: 赏景海边露营

出发时间:2018.6.15

回程时间:2018.6.18

活动强度:强度一般

活动费用:含路费计划800左右

简介

衢山岛

浙江省舟山市岱山县衢山镇,面积59.94平方千米,人口7万人。古称大衢山、朐山或岣山.观音山为岛上风光最好的去处,海拔314米,是岱山县第一高峰,数峰连环如莲花,相传观音菩萨去普陀修道之前,曾在此山驻足3年。衢山岛有盐田3平方千米,为“岱盐”主要产地之一,观光旅客在岛中部沿海可以看到颇为壮观的大盐田。

Markdown preferences pane

地图

【临安-舟山】

高速(290KM,3.5h,高速费210)+ 轮渡(衢山岛 54KM,1.5h)

Markdown preferences pane

路程

Day1 临安-舟山市三江码头-衢山岛

6.15 周五 12:00(临安)临安开车出发预计时长3小时(13:00-4:00)到达舟山定海区入住酒店,预计5点到,外出吃晚饭。

临安天气:多云 19~31℃

舟山天气:多云转阴 20~27℃

Day2 舟山-三江码头-衢山岛

6.16 周六 7:00起床洗漱吃早餐,去菜市场时购买烧烤食材,定海区酒店8:20出发前往三江码头(预计开车30分钟),赶9:20的车渡船(1.5h)预计11:00到达衢山岛。

舟山天气:中雨转阴 22~23℃

衢山天气:阴 22~27℃(天气待定)

Markdown preferences pane

说明:车渡(包含驾驶员185)+人渡(成人60,小孩<1.2m免票,>1.2半价),两辆车都开上岛(待商议)。衢山岛较大可以玩的地方比较多,中午选择一家海鲜店或者农家乐吃吃饭,视具体情况情况而定,选择在衢山岛双龙沙滩,下午可以到海边游泳戏水抓鱼等,扎营。傍晚可以在海边看看日落,开始烧烤。具体看天气

Day3 衢山岛

6.17 周日 需要看海上日出(观音山)的清晨需要早起,不看日出的可以睡到自然醒,吃完早饭。

衢山天气:阵雨 23~24℃(天气待定)

说明:岛上可以去观音寺,盐田等地去参观游玩一下,逛逛小镇菜市场找个农家乐做晚饭或者中饭,也可自行活动,晚上可根据实际情况选择露营点露营(大风车平台露营基地可以洗漱等),具体看天气。

Day4 衢山岛-三江码头-临安

6.18 周一 睡到自然醒,上午可以出去逛逛,中午吃完海鲜大餐,准备下午轮渡会三江码头,开车会临安,预计下午5:00可以到。

衢山天气:阵雨 23~25℃(天气待定)

临安天气:阴转多云 20~28℃(天气待定)

费用参考

名称 费用 备注
住宿 1*80(住宿) = 80 带帐篷睡袋(具体看天气),薄被子
餐费 8 * 50 = 400 餐费包含水
景区门票 0 没有门票费
车费 (210 4 + 290 4 0.5 ) / 6 + 2 60 = 356 人均费用
其它费用 未知 包含个人消费等
总计 800 当前费用为大概统计,具体依据消费而定

行程清单

1.本次的露营,自备帐篷睡袋等,在外天气变化较大注意带保暖衣物。

2.雨伞,蚊虫叮咬等户外基础必备药物,海边游泳泳衣,当然还有美美的拍照衣服,防晒物品,手机防水设备。

3.烧烤相关(已备),游泳设备。

4.足够的电能(预计2天时间),适当的干粮可备,淡水。

参考

1.衢山岛——一个鲜为人知的“海上仙山”

2.衢山客运中心码头最新船班时刻表

3.衢山岛露营自驾,观东海赏风车品烧烤尝海鲜

4.美食攻略

5.开渔季 · 吃货福利

6.衢山岛自驾三天游

Android Studio 3.0 项目搭建及目录结构

Mac Android studio下载

Android studio是Google公司出的一款Android集成开发工具,类似 Eclipse ADT,提供了集成的Android开发工具用于开发调试。

  • 基于Gradle的构建支持
  • Android 专属的重构和快速修复
  • 提示工具以捕获性能、可用性、版本兼容性等问题
  • 支持ProGuard 和应用签名
  • 基于模板的向导来生成常用的 Android 应用设计和组件
  • 功能强大的布局编辑器,可以让你拖拉 UI 控件并进行效果预览

注意:下载需要翻墙VPN,在安装的过程中部分插件安装也需要翻墙,如果不翻墙可以使用国内的一些已经下载好的地址下载

创建项目

一. Android工程

1.创建一个Hello world的项目,按照默认提示操作即可,注意第一次创建项目会加载过程较长(需要下载一些插件内容VPN)耐心等待即可。

2.项目结构如下截图:

Markdown preferences pane

在项目没有配置正确的情况下,会出现右上角的运行等为灰色不可点击的情况,一旦Gradle自动化构建有问题则会出现这种情况,注意Gradle出现下载不完全等情况可以通过手动下载APN(地址:https://services.gradle.org/distributions/gradle-4.1-all.zip),同时修改配Android Studio ->Preferences->Gradle->选择 Use local Gradle Distribution(地址:Android Studio安装的位置显示包含内容,将刚刚手动下载的gradle替换)

3.Android工程于截图左侧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
manifests:AndroidManifest.xml:APP的配置信息
java:主要为源代码和测试代码
res:主要是资源目录,存储所有的项目资源
drawable:存储一些xml文件,-*dpi表示存储分辨率的图片,用于适配不同的屏幕。
-mdpi:320x480
-hdpi:480x800、480x854
-xhdpi:至少960x720
-xxhdpi:1280x720
layout:存储布局文件
mipmap:存储原声图片资源
values:存储app引用的一些值
- colors.xml:存储了一些color的样式
- dimens.xml:存储了一些公用的dp值
- strings.xml:存储了引用的string值
- styles.xml:存储了app需要用到的一些样式
Gradle Scripts:build.gradle为项目的gradle配置文件

参考

1.Android开发学习之路–Android Studio项目目录结构简介

2.Android 菜鸟教程

中华恐龙园

出行概要

活动时间:2017.8.26–2017.8.28

活动目的:游玩中华恐龙园

出发时间:2017.8.26

回程时间:2017.8.28

活动强度:一般

活动费用:不含路费计划800

简介

中华常州恐龙园

位于江苏省常州新区的现代旅游休闲 区内。占地面积600余亩,自2000年9月正式开园以来,中华恐龙园在主题公园经营上,创造性地提出了主题公园“5+2”发展模式,最终形成了中华恐龙园在汹涌的主题公园浪潮中傲然屹立的核心竞争力:科普性极强的旅游目的地 和游乐性极强的科普教育基地 。中华恐龙园陆续获得了“国家5A级景区”、“全国科普教育基地 ”、“中国文化产业示范基地”等多项“国”字号 殊荣,成为常州对外交流的一张名片。

地图

【临安-中华恐龙园】260KM,时间长3.3H

说明:途经杭长高速、沪宜高速

Markdown preferences

路程

Day1 临安 - 常州中华恐龙园 - 水世界

8.26 周六8:00(临安)临安开车出发预计时长3.5小时(8:00-11:30),到达酒店。午饭完毕预计下午1点左右进入中华恐龙园水世界园区游玩,可一直玩到晚上10:00出园。

临安天气:雷阵雨多云,26℃-34℃

常州天气:阴转多云,25℃-29℃,做好防晒准备

Day2 中华恐龙园

8.27 周日8:00 吃完早餐进入中华恐龙园园区,优化一整天。中途不要出园一旦出园,需要重新买票,晚上有恐龙园夜场秀。

常州天气:多云,26℃-33℃,做好防晒准备

Day3 暂定 - 常州 - 临安

8.28 周以8:00 吃完早餐,活动。下午吃完晚饭6:00左右回临安,预计晚上10:00到家。

常州天气:多云,25℃-36℃,做好防晒准备

临安天气:晴转多云,26℃-34℃

预计费用

名称 费用 备注
住宿 2 * 100 = 200 酒店
餐费 6 * 50 = 300 包含饮食
景区门票 水世界+恐龙园 + 其它 = 268 根据实际情况
车费 * 开车
其它费用 未知 包含个人消费等
总计 800 当前费用为大概统计,具体依据消费而定

注意事项

1.高温天气做好防晒准备,部分水上项目可能会淋湿衣服一次性雨衣或带足衣物

2.园区消费较高,建议携带部分能量补充物品,水+补给物。

3.进入园区一旦出园,重新进入园区需要重新购票。

4.关于28游玩项目暂定

参考

1.常州中华恐龙园旅游攻略

2.中华常州恐龙园

Xcode插件

在开发过程中,使用插件可以极大的提高工作的效率,下面简单的介绍一些常用的插件。

Alcatraz

一款简化了Xcode的插件的插件,安装了此插件可以极其方便的管理Xcode的插件。

在terminate中输入以下命令即可安装:

1
curl -fsSL https://raw.githubusercontent.com/supermarin/Alcatraz/deploy/Scripts/install.sh | sh

安装成功后,选择退出Xcode重新启动,会有提示选项,选择load bundle即可。在顶部选项卡里面,通过选择Window可以看到多出了一个Pagekage Manager选项,这个就是Alcatraz插件管理工具。选择后,可以搜索,安装,卸载相关的插件。

KSImageNamed

一款图片名称自动补全的图片插件,并且可以在coding的时候直接看到图片的缩略图极其方便。可以通过Alcatraz直接安装

InjectionPlugin

一款实时UI调测工具,不需要重复的run模拟器,节省UI调测时间。可以通过Alcatraz直接安装

通过Alcatraz安装后,重新启动Xcode,可以看到Product的菜单下多了一个Injection Plugin的选项。在当前项目中确定要使用改插件的功能需要选择“Patch Project for Injection”,选择后,会在项目pch文件插入相关代码如下:

1
2
3
4
5
#ifdef DEBUG
#define INJECTION_ENABLED

#import "/tmp/injectionforxcode/BundleInterface.h"
#endif

和main文件中插入如下代码:

1
2
3
4
5
6
7
8
9
// From here to end of file added by Injection Plugin //

#ifdef DEBUG
static char _inMainFilePath[] = __FILE__;
static const char *_inIPAddresses[] = {"192.168.1.159", "127.0.0.1", 0};

#define INJECTION_ENABLED
#import "/tmp/injectionforxcode/BundleInjection.h"
#endif

同时我们在当前项目目录下面可以看到一个iOSInjectionProject的文件夹,里面包含了当前项目的Inject配置等信息。如果我们想要移除Inject的所有操作,可以选择“Rever Injection’s Changes”。

模拟器

需要注意的是,在修改Xcode中的相关的代码后,我们再也不需要通过command+R来运行项目,而是选择control + 即可在当前页面实时查看到效果。

真机

需要注意是真机调测,由于需要证书支持。直接使用的话会出错,所以我们需要修改iOSInjectionProject中的相关机型下面identify.text文件中证书的配置,改为当前真机调测的证书即可。

ESJsonFormat-Xcode

将 JSON 格式化输出为模型的属性一款插件,可以直接通过Alcatraz安装,搜索ESJsonFormat。

Window-ESJsonFormat-输入Json-Enter OR 快捷键(Control+Shift+J)-输入JSON-Enter

getterMake-Xcode

批量生成属性的懒加载 getter方法。

[VVDocumenter-Xcode]

VVDocumenter-Xcode 是一个 Xcode 插件,可以帮你轻松的编写 Javadoc 风格的文档。

参考

1.iOS UI调试神器,插件injection for Xcode使用方法

2.IOS开发者常用的10个Xcode插件

3.Xcode插件:将 JSON 格式化输出为模型的属性 ESJsonFormat-Xcode

4.Xcode插件-开源软件库-开源中国社区

香格里拉大环线

出行概要

活动时间:2016.9.23–2016.10.8

活动目的:骑行赏景

出发时间:2016.9.23

回程时间:2016.10.8

活动强度:强度较大

总计里程:1100KM

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

联系人:Tweiwei497435786[微信]

地图

行者路书编号:

【丽江机场到泸沽湖 815060】
Markdown preferences

【泸沽湖到康定 815124】
Markdown preferences
说明需要注意的是:泸沽湖到康定路段中的泸沽湖到稻城路段走小路参考

路线

Day1 杭州 - 丽江 - 大理

9.23 周五06:00(杭州萧山机场B楼)集合乘坐杭州前往丽江飞机预计时长4小时(06:45-10:25),到达丽江后下午乘车去大理。

杭州天气:

丽江天气:

大理天气:

说明:下午乘车去大理游玩,大理到丽江班车2.5小时。

Day2 大理

9.23 周五 大理洱海等地游玩。

大理天气:

说明:晚上住宿大理。

Day3 大理 - 丽江

9.24 周五06:30 大理游玩,下午3:00左右乘坐大理至丽江车辆回丽江。

大理天气:

丽江天气:

说明:大理到丽江班车2.5小时

Day4 丽江 - 玉龙雪山(80KM)

9.25 周日06:30出发,骑行骑行丽江到泸沽湖线路,行至玉龙雪山附近。

丽江天气:

说明:玉龙雪山攻略

Day5 玉龙雪山 - 泸沽湖(130KM)

9.26 周一06:30 集合出发至泸沽湖,该路段较好相对轻松,预计下午4:00能够到达,找住宿地方修整,逛湖游玩。

丽江天气:

盐源天气:

说明:

Day6 泸沽湖 - 屋角乡(50KM)

9.27 周二 全程乱石路,需要解决住宿的问题。

盐源天气:

木里天气:

说明:全程乱石路,住宿在农家,需要自备睡袋。

Day7 屋角乡 - 瓦厂 - 915林场(88KM)

9.28 周三06:30 全程乱石路,915林场住宿。

木里天气:

说明:全程乱石路,915林场住宿,住宿在农家,需要自备睡袋。

Day8 915林场 - 拢撒牧场 - 子星 - 瓦托(90KM)

9.29 周四06:30 全程乱石路,瓦托住宿。

木里天气:

说明:全程乱石路,住宿在农家,需要自备睡袋。

Day9 瓦托 - 俄西 - 蒙自乡 - 香格里拉镇(50KM)

9.30 周五06:30 全程乱石路,瓦托住宿。

木里天气:

稻城天气:

说明:全程乱石路,住宿在农家,需要自备睡袋。

Day10 香格里拉镇 - 亚丁(5KM)

10.2 周日06:30 稻城亚丁游玩一天。

理塘县天气:

稻城县天气:

Day11 香格里拉镇 - 稻城县(75KM)

10.3 周一06:30 香格里拉镇前往稻城县。

稻城县天气:

说明:需要翻越一座海拔4500的高山,S216

Day12 稻城县 - 理塘县(150KM)

10.4 周二06:30 稻城县前往理塘县。

稻城县天气:

理塘县天气:

说明:全程高海拔,不需要翻大山,海拔在4000。该段路程可能会被分成两天S216,G318

Day13 理塘县 - 雅江县(130KM)

10.5 周三06:30 雅江县出发边走边玩到新都桥,骑行游玩新都桥,住宿新都桥。

理塘县天气:

雅江县天气:

说明:世界海拨最高的县城,整路不需要翻山,海拔在4000

Day14 新都桥 - 康定(70KM)

10.6 周四06:30 新都桥出发边走边玩到康定,骑行游玩康定情歌故里,住宿康定。

康定天气:

说明:将会翻越一座4000米的高山

Day15 康定 - 成都(乘车)

10.7 周五07:00 上午10:00左右乘坐康定到成都汽车,预计时长4小时,到达成都游玩,住宿成都东站附近。

康定天气:

成都天气:

说明:

Day16 成都 - 杭州(动车)

10.8 周六07:00 出发成都东D2224,7:36 - 22:24,晚上需要预定杭州东附近的旅馆。

杭州天气:

说明:

预计费用

名称 费用 备注
住宿 15*50 = 750 带睡袋
餐费 15 * 50 = 750 餐费包含水
景区门票 0 根据实际情况
车费 460 + 560 + 240 不包含自行车托运费用
其它费用 未知 包含个人消费等
总计 2800 当前费用为大概统计,具体依据消费而定

注意事项

1.在泸沽湖到香格里拉镇4天路段中,路况较差补给不足,注意带好备用轮胎,及相关补给。

2.部分高原地带,为避免高原反应需要备好相关药品。

3.需要带好保暖衣物,部分地方雪山天气。

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

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

免责声明

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

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

参考

1.寻找消失的地平线——香格里拉大环线骑行

2.香格里拉大环线

iOS Responder Chain

不知你是否有思考过,你在Touch一个application的某一点之后,iOS系统是怎么处理这些事件,并且如何准确响应的?那么接下来的知识,将会向你清楚的讲解结合demo关于iOS中事件的响应链:在我们点击屏幕的时候,iPhone OS获取到了用户进行操作行为,操作系统把包含这些点击事件的信息包装成UITouch和UIEvent形式的实例,然后找到当前运行的程序,逐级寻找能够响应这个事件的对象,直到没有响应者响应。

响应者

在iOS中,能够响应事件的都是UIResponder的子类对象,其相关的方法如下:

1
2
3
4
5
6
7
8
9
// Generally, all responders which do custom touch handling should override all four of these methods.
// Your responder will receive either touchesEnded:withEvent: or touchesCancelled:withEvent: for each
// touch it is handling (those touches it received in touchesBegan:withEvent:).
// *** You must handle cancelled touches to ensure correct behavior in your application. Failure to
// do so is very likely to lead to incorrect behavior or crashes.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;//touch开始
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;//touch移动
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;//touch结束
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;//取消touch事件

通过上面我们可以看到每一个响应者的委托方法中返回的有UITouch触发对象集合,以及UIEvent触发的事件类型。

  • UITouch点击对象

    表示单个点击,相关属性和方法如下:

    1
    2
    3
    4
    5
    	@property(nonatomic,readonly) UITouchPhase        phase;//包含touch的状态(UITouchPhaseBegan,UITouchPhaseMoved,UITouchPhaseStationary,UITouchPhaseEnded,UITouchPhaseCancelled)
    @property(nonatomic,readonly) UITouchType type
    - (CGPoint)locationInView:(nullable UIView *)view;//获取当前点击坐标点
    - (CGPoint)previousLocationInView:(nullable UIView *)view;//获取上个点击位置的坐标点
    ......
  • UIEvent点击事件
    iOS中使用UIEvent表示用户交互的事件对象,相关属性方法如下:

    1
    2
    3
    4
    5
    	@property(nonatomic,readonly) UIEventType     type NS_AVAILABLE_IOS(3_0);//包含事件的响应类型,分别有多点触控、摇一摇以及远程操作等
    @property(nonatomic,readonly) UIEventSubtype subtype NS_AVAILABLE_IOS(3_0);//subtype事件等
    @property(nonatomic,readonly) NSTimeInterval timestamp;//时间戳
    - (nullable NSSet <UITouch *> *)touchesForView:(UIView *)view;//可以通过event事件来获取相应view的UITouch的对象集合
    ......

需要注意的是UIView除了继承自UIResponder的touches事件外,还可以被绑定一些复杂的手势事件,如:UIGestureRecognizer,UITapGestureRecognizer等。在给UIView添加了UITapGestureRecognizer手势后,原本正常的touches事件流程touchesBegan,存在move(touchesMoved),touchesEnded。变成了touchesBegan,UITapGestureRecognizer的selector方法,然后是touchesCancelled。由此可说明手势的事件优先级高于touches的事件。

Hit-Testing 返回触摸事件发生的视图

iOS 使用hit-testing来找到事件发生的视图。 Hit-testing包括检查触摸事件是否发生在任何相关视图对象的范围内, 如果是,则递归地检查所有视图的子视图。在视图层次中的最底层视图,如果它包含了触摸点,那么它就是hit-test视图。等 iOS决定了hit-test视图之后,它把触摸事件传递给该视图以便处理。如下图:

General preferences pane

如果传递到hitTest:withEvent:方法的点不在视图的范围内,pointInside:withEvent:方法返回NO,点被忽视,并且hitTest:withEvent:返回nil. 如果一个子视图返回NO,则视图层次的整个分支都被忽视,因为如果触摸事件没有发生在那个子视图中,那么事件也不会在任何一个该子视图的子视图中发生。 这意味着在一个子视图中的任何点,如果它在其父视图的外面,那么它不能接收触摸事件,因为触摸点必须在父视图和子视图的范围内。

hit-test是第一处理触摸事件的视图。如果hit-test视图不能处理该事件,事件沿着视图的响应链传递直到系统找到可以处理该事件的视图,如果没有找到处理者则丢弃该事件。

响应链传递

响应链是一系列相连的响应者对象。 它由第一个响应者开始,以应用对象结束。 如果第一响应者不能处理该事件,它把事件传递给响应链中的下一个响应者。

在iOS系统检测到手指触摸后,会将其打包成UITouch和UIEvent加入当前活动的Application的事件队列。同时UIApplication会从事件队列中取出触摸事件并传递到keyWindow。

在UIView.h中存在如下定义的内容:

1
2
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;   // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds

hitTest:withEvent: 方法为给定的CGPoint 和 UIEvent返回hit test 视图。hitTest:withEvent:方法通过在自身调用pointInside:withEvent: 方法开始。 如果传递到方法hitTest:withEvent:内的点在视图的范围内,pointInside:withEvent:返回YES。同时将该响应者加入响应者栈,然后通过递归地给每个子视图调用hitTest:withEvent:方法,如果该防返回YES,则加入响应者栈。如下图(需要注意ViewController,AppDelegate,UIApplication并没有上面View的相关方法,但是由于其是UIResponder的相关类):

General preferences pane

很多类型的事件都在事件传递中依赖响应链。 响应链是一系列相连的响应者对象。 它由第一个响应者开始,以应用对象结束。 如果第一响应者不能处理该事件,它把事件传递给响应链中的下一个响应者。

响应者对象是可以响应并处理各种事件的对象。 UIResponder 类是所有响应者对象的基类,它为事件处理和通用响应者行为都定义了可编程接口。 UIApplication, UIViewController, 和UIView的实例都是响应者,就是说所有的视图和大多数主要对象都是响应者。 请注意Core 动画层不是响应者。

参考

1.事件传递:响应链

2.iOS-hitTest:withEvent与自定义hit-testing规则

3.iOS开发 - 事件传递响应链

3.下载demo

iOS中RunLoop

初识iOS RunLoop

iOS中应用程序app从开始运行一直处于等待的状态,接收到类似点击事件交互后执行相应的操作,完成后继续等待状态,直到我们将程序杀死。那么这个过程是怎么发生的呢?是如何通过相应的方式实现这种机制的呢?通过下面的代码探析:

1
2
3
4
5
6
7
- (void)loop {
BOOL message;
do {
message = [self getMessage];
[self processMessage];
} while (!message)
}

如上面的代码就可以达到这种机制。可以推测而出app从启动开始创建的主线程一定是在一个死循环中,没有任务的时候进行休眠,接收到任务后被激活执行任务。我们可以理解,这样一个管理线程执行任务就是RunLoop机制,线程在执行中的休眠与激活就是RunLoop对象进行管理的。

RunLoop与线程

RunLoop既然是用来管理线程的,那么他们直接有着怎样的关系。实际上,每一个线程中都有一个RunLoop对象,可以通过具体的方法获得。需要注意的是,除了主线程中RunLoop是默认创建并运行激活的,但并不是每一个线程中都有这个实例对象,如果我们不主动获取runloop,这个runloop就不存在,如果我们去获取不存在就去创建并返回。可以通过[NSRunLoop currentRunLoop]方法获取当前线程的runloop。

Cocoa中RunLoop

NSRunLoop是Cocoa框架中runloop的类非线程安全,关于NSRunLoop的方法如下:
1
2
3
4
+ (NSRunLoop *)currentRunLoop;//获取当前线程的runloop,没有则创建并返回
+ (NSRunLoop *)mainRunLoop NS_AVAILABLE(10_5, 2_0)//获取当前主线程的runloop

- (CFRunLoopRef)getCFRunLoop;//返回CFRunLoopRef
1
2
3
4
5
6
7
- (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode;//将定时器添加到runloop中

- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode;//添加输入源端口到runloop中
- (void)removePort:(NSPort *)aPort forMode:(NSString *)mode;//将某个输入源端口移除

- (nullable NSDate *)limitDateForMode:(NSString *)mode;//获取下个响应时间
- (void)acceptInputForMode:(NSString *)mode beforeDate:(NSDate *)limitDate;//在某个时间期限前接收响应
1
2
3
- (void)run; //开始运行
- (void)runUntilDate:(NSDate *)limitDate;//到某个时间点运行
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;//某个期限前运行

注意上面提供的方法:输入源被注册进Runloop中时会有方法进行remove,但是定时器却没有,但是定时器中的invalidate方法可以将其从runloop中移除,正如官方文档的说明:invalidate是重要也是唯一的可以将定时器从runloop的注销的方法,所以如果我们创建了定时器,就一定要在不使用时调用invalidate方法。

CoreFoundation中RunLoop

CFRunLoopRef是CoreFoundation框架中runloop的类线程安全,关于CoreFoundation中关于runloop的有一下类:
  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

Markdown preferences pane

CFRunLoopModeRef

一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响

CFRunLoopSourceRef事件产生的地方。分为Source0 和 Source1
  • Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
  • Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程
CFRunLoopTimerRef基于时间的触发器

当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。

CFRunLoopObserverRef

是观察者,每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化:

1
2
3
4
5
6
7
8
9
10
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),//即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1),//即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2),//即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5),// 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6),// 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7),// 即将退出Loop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
CFRunLoopModeRef

苹果公开提供的 Mode 有两个:kCFRunLoopDefaultMode (NSDefaultRunLoopMode)默认情况 和 UITrackingRunLoopMode追踪 ScrollView 滑动时的状态,你可以用这两个 Mode Name 来操作其对应的 Mode。

同时苹果还提供了一个操作 Common 标记的字符串:kCFRunLoopCommonModes (NSRunLoopCommonModes),你可以用这个字符串来操作 Common Item。需要注意的是一个 Mode 可以将自己标记为”Common”属性(通过将其 ModeName 添加到 RunLoop 的 “commonModes” 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 “Common” 标记的所有Mode里。
关于Mode更多点击这里

RunLoop 的内部逻辑

具体的流程如下图:

Markdown preferences pane

关于其中的逻辑讲解见参考博文中内容。

参考

1.iOS线下分享《RunLoop》

2.iOS中RunLoop机制浅探

3.深入理解RunLoop

渔山列岛露营

出行概要

活动时间:2016.8.19–2016.8.21

活动目的:露营,看海

出发时间:2016.8.19

回程时间:2016.8.21

活动强度:一般

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

联系人:Tweiwei497435786[微信]

地图

Markdown preferences pane

路线

Day1 杭州 - 宁波 - 象山

8.19 周五17:24(杭州火车东站)集合乘坐G589高铁到宁波,行程1小时,预计6:30到宁波,乘坐宁波到象山汽车。

Markdown preferences pane

杭州天气:

宁波天气:

说明:

(1) 杭州宁波高铁班次

(2) 宁波到象山的汽车时间表

Day2 象山 - 石浦 - 渔山列岛

9.20 周六6:00起床,乘车大巴从宁波到石浦(6:30汽车南站),一个半小时到达,预计6:30左右到达,乘坐6:50左右前往渔山列岛的班船,行程2:30小时,预计10:00登岛。

宁波天气:

说明:中餐在岛上酒店吃饭,晚餐干粮备足,晚上海边露营。特别需要注意的是’班船出行时间’

Day3 渔山列岛-象山石浦 - 宁波 - 杭州

9:30 周五睡到自然醒,选择上午9:00左右乘船回象山石浦,或者下午2:00左右回石浦到宁波,乘坐高铁回杭州。

宁波天气:

杭州天气:

说明

预计费用

名称 费用 备注
住宿 1 * 80 = 100 自带帐篷+宾馆,住宿两天
餐费 6 * 50 = 300 餐费包含水零食等
车费 71 2 + 41.5 2+300 = 500 高铁 大巴 船票
其它费用 未知 包含个人消费等
总计 900 当前费用为大概统计,具体依据消费而定

注意事项

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

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

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

免责声明

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

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

参考

1.淘宝渔山列岛船票预定

2.宁波渔山岛

Mac Tree命令

tree是一个能列出递归目录的命令,以图形显示驱动器或路径的文件夹结构,可以生成命令树。在linux和Windows下都有类似tree的命令来查看当前目录树结构的软件,mac上面却没有。当然办法总是会有的,下面几种教你几种方法安装方法。

alias

通过alias指定别名方法

1
alias tree="find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'"

打开bash文件,vim ~/.bash_profile,把上面命令拷贝进去就可以了。让然如果你安装了zsh的话,那就vim ~/.zshrc,把命令拷贝进去。但是这个命令只能在terminate中输入tree命令,无法做其它的扩展操作。

手动安装

tree下载文件,然后解压缩文件,打开文件中的makefile文件来匹配我们的环境osx,找到 # Uncomment for OS X:然后将下面的内容注释解开:

1
2
3
4
5
6
# Uncomment for OS X:
CC=cc
CFLAGS=-O2 -Wall -fomit-frame-pointer -no-cpp-precomp
LDFLAGS=
MANDIR=/usr/share/man/man1
OBJS+=strverscmp.o

表示在osx下使用,然后编译文件,并且将编译成功的文件放到系统的bin下面:

1
2
make  //编译
sudo cp tree /usr/local/bin/ //拷贝到bin下面

重启terminate,执行tree命令。此方法较为复杂,需要手动编译bin文件。

homebrew安装

我们可以通过brew search tree发现存在tree工具,当然前提是你需要安装homebrew,关于怎么安装请看之前的文章。在termiate中输入:

1
brew install tree

安装即可,此方式最为简单,不过需要安装brew。

参考

1.MAC OS X安装命令行工具TREE

2.mac系统中使用tree 命令展示目录树结构