<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title><![CDATA[纳兰若水]]></title>
  
  <link href="/atom.xml" rel="self"/>
  <link href="http://yoursite.com/"/>
  <updated>2024-12-09T14:52:00.013Z</updated>
  <id>http://yoursite.com/</id>
  
  <author>
    <name><![CDATA[纳兰若水]]></name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title><![CDATA[南疆之旅]]></title>
    <link href="http://yoursite.com/2024/10/13/%E5%8D%97%E7%96%86%E4%B9%8B%E6%97%85/"/>
    <id>http://yoursite.com/2024/10/13/南疆之旅/</id>
    <published>2024-10-13T10:11:19.000Z</published>
    <updated>2024-12-09T14:52:00.013Z</updated>
    <content type="html"><![CDATA[<p>出行日期：2024/09/22 - 2024/10/08<br>游玩方式：自驾、6人<br>出行目的地：南疆<br>出行必备：墨镜、防晒霜、帽子、冲锋衣（羽绒服）等防晒保暖用品。最最重要的是需要办理边防证（可以把新疆全域都写上）。</p>
<p>本次行程定制、出行方式、机酒预订本人都没有参与，还是熟悉的人员配方，还是一键跟随模式，主打一个听安排。以下是行程路线图，仅供参考：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng.jpg" alt=""></p>
<p>国庆高峰出行，这个行程看似简单，但实际上完美的避开了高峰期，没有遇到堵车，封路，风雪等恶劣天气。全靠队友给力，赞~  我只能写写流水账+风景贴图+自恋贴图，收集一波回忆素材。</p>
<a id="more"></a>
<h4 id="DAY1-上海-喀什"><a href="#DAY1-上海-喀什" class="headerlink" title="DAY1 - 上海-喀什"></a>DAY1 - 上海-喀什</h4><p>因为是下午的飞机，上午还是要上班了，紧赶慢赶的结束了工作，收拾好心情和行李，出发！历时六个半小时的飞行，到达喀什机场，入住酒店，已然是凌晨12点多了。好在这边日出日落都晚，早餐提供一般从8点半才开始（大概9点才天亮），所以虽然不能睡到自然醒，但是不太需要调整生物钟。按理说能保证睡眠时长~ 不过换了个地方又错过了入睡时间，根本没睡着多久！😴<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/day1.jpg" alt=""></p>
<h4 id="DAY2-喀什-塔县"><a href="#DAY2-喀什-塔县" class="headerlink" title="DAY2 - 喀什-塔县"></a>DAY2 - 喀什-塔县</h4><p>今日自驾行程路线图：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng2.jpg" alt=""></p>
<p>第二天一早在酒店吃早餐，早餐比较简单，用完早餐，9点左右行李顺利装车，说到行李是因为有6个人，7坐车（别克GL8），行李不能太多，行前合计后，严谨的队友们算出3个24寸、3个20寸刚刚好。整装出发，行至一个村（不记得啥村）买了烤包子和烤馕做为补给，价格实惠，味道很好。</p>
<p>2个小时后，导航抵达第一个地点，克州冰川公园西门入口，停车在一个很大的平坦碎石路面上（感觉是个野景点），就能近距离看到山上的红色石头，红色的峡谷搭配雅丹地貌也是很出片，拍到了第一组人生照片，哈哈哈。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/rensheng1.jpg" alt=""></p>
<p>入口的峡谷，也美的很大气。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/rukou.jpg" alt=""></p>
<p>不过我们没有计划特别深入，就在附近游玩了一下，不过fufu领队说真正计划的景点-奥依塔克红石神秘大峡谷再里面，所以又开车沿着左侧的路继续开了几公里，到了一个奥依塔克红石神秘大峡谷度假村的大门下，不过大家看着入口陷入了沉思，因为这个景点没什么人，走进去问了下门口的疑似工作人员，说是门票10元，要自己走进去大概两公里就能到红石神秘大峡谷，算了下，这一来一回可能要一个多小时呀，看了门口宣传图，感觉和刚才的『野景点』可能差不多，感觉还是算了，就没有进去（后来看了小红薯，其实里面的是由于流水侵蚀而成的圆洞型特殊地貌，像是一个个气泡，还是蛮有特色的，网图）。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/daxiagu.jpg" alt=""></p>
<p>说不进就不进，咱们继续前行，路上被南疆大气的风貌吸引，大家兴致高涨，一路瞎聊，从牛马生活讲到了玄幻小说，回想这一刻，不再是牛马，是肆意逍遥海阔天空的主角。<br>一路上眺望远处，随着海拔的上升，能看到的雪山越来越多，对眼睛非常友好。中午一点半左右就抵达了白沙湖-南岸，门票10元?购买门票后可以开车进入停车场，此时太阳还不错，湖面真的很美，此时我们还没意识到高原的温度多变，穿着单薄的衣服就下去美美的拍照了，沉迷美景和拍照的我还在旋转跳跃（危险行为，会高反），拍了很多照片。湖边的风越来越大，还下起了雪，一下子就感觉到非常的寒冷，鼻腔和脑门都开始难受了，赶紧去车上穿上了冲锋衣，带上了帽子和围巾，才感觉瞬间暖和了许多。接着回到湖边，就有当地人牵着牦牛问你要不要拍照，我们想了想，来都来了是吧，拍吧，30元一个人（后来才知道可以讲价，甚至有人讲到10块钱一个人）。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/baishahu1.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/baishahu2.jpg" alt=""></p>
<p>拍完正巧遇到队友们都拍好了，那肯定少不了来个大合照。在这个美丽冻人的湖边玩儿的差不多了，赶紧回到车上，买了一些牛肉串、羊肉串吃上，味道真的不错，不算贵(7块钱一串)。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/baishahu3.JPG" alt=""></p>
<p>最后附上一张航拍<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/baishahu.JPG" alt=""></p>
<p>刚才在白沙湖也是冷风吹了很久，我感觉开始有些缺氧了，此时海拔也是三千多了（帕米尔高原），坐在车上头有些疼。而且后面的路上还是越来越冷，一路上都是雪山的景色，不过好在很快到了喀拉库勒湖，我们没有深入走到湖边（感觉有点难受，走不远），就再边上停车场附近打卡，其实边上也能看到全貌了，太阳这个时候也很不错，雪山加蓝天的景色很美，可惜无人机拍的美照和合照后面不小心遗失了，略感遗憾。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/kashenkulehu.JPG" alt=""></p>
<p>因为行程时间比较随意，后面就直接前往酒店，中途没有停留，本来还有个湿地的景点，但是因为时间原因没有去。因为如果过去，到酒店就会很晚，而且我和一个同伴出现了一些高反，出现了缺氧头疼症状，到酒店九点左右了，就在酒店的餐厅吃了火锅，没想到味道真的挺不错的，好吃的，吃完都感觉大大减少了高反。因为第二天还是再这个酒店，所以也不需要收拾行李之类，节省了很多时间，可以多睡一会儿。不过头疼症状还在，还是挺难受。</p>
<p><img 晚餐图.jpg=""> </p>
<h4 id="DAY3-塔县-塔县"><a href="#DAY3-塔县-塔县" class="headerlink" title="DAY3 - 塔县-塔县"></a>DAY3 - 塔县-塔县</h4><p>今日行程路线：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng3.jpg" alt=""></p>
<p>今天的行程是从塔县出发，到塔县结束的一个环形路线，今天全程海拔比较高，温度是10℃左右，前一天真的冷怕了，大家都搞上了相对保暖的装备，主打一个扛冻。这天首先要去的是盘龙古道，因为这段路程是持续盘山公路上海拔然后再下来，大家又有点高反，这个行程差点被pass，不过最终大家决议还是去一趟，因为盘龙古道有几百道弯路，所以说『走过这些弯路，人生尽是坦途』。试问这谁能不动心，咱高低都得去走一走，必然是这段旅程中非常难忘的一个点了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/panlong.jpg" alt=""></p>
<p>起始点打卡后，大家充满了雄心壮志的踏上这段弯路。一路盘山向上，云雾越来越大，中途在进入云雾弥漫之前，下车看了下我们来时走过的弯道，真的是盘龙了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/panlong7.jpg" alt=""></p>
<p>随后又一路行驶进云雾中，直到走进了全是积雪的山顶。山顶有个休息点，因为这时候海拔是最高了，其实是有点缺氧的，我一路上昏昏沉沉的，只有男士们下车买了热腾腾的咖啡和罐罐奶茶。得到能量的补充，然后继续前行就是下山了，冲出云雾后，能看到对面的云彩下的雪山和接下来像龙一样的盘山路。看到一个适合观景的停车点停车后，看到了对面雪山，和对面山上的云雾，景色很美。这个时候海拔下来了，大家状态好了很多，手机相机无人机都整起来~~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/panlong5.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/panlong3.JPG" alt=""><br>远处的云雾中的群山真的很美~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/panlong4.jpg" alt=""></p>
<p>中间遇到了两个人一起自驾的旅友，开着很酷的橙红色甲壳虫，很热情的建议我们可以借用他们的车做背景拍照，果然非常的出片，出的还是人生照片啊，哈哈哈。遇到这两个很酷的旅友，感觉非常有趣，多年以后，回想起来，肯定还是很难忘。他们说两个人一起旅行，走久了会有点无聊，羡慕我们人多会比较有趣，想想确实有一起的旅游搭子真的是不错，尤其是搭子还特别靠谱，本废物点赞~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/panlong1.jpg" alt=""></p>
<p>缘分旅友照，真的，他们热情又酷!（侵删~）<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/panlong6.JPG" alt=""></p>
<p>下盘龙古道后，就有个休息区，在这里稍事休整，简单的休息吃了个午饭，这个时候高反症状基本缓解很多了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/panlong2.jpg" alt=""></p>
<p>班迪尔蓝湖真的是很出片的一个湖，因为含有丰富的矿石资源，这里的水颜色非常漂亮，会随着光影，有不同的颜色。<br>远看湖面和群山，非常美，才华有限，只能说 wow ~太美啦。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/bandier2.jpg" alt=""></p>
<p>哦豁~ 来几张连拍<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/bandier1.jpg" alt=""></p>
<p>收获超绝美壁纸一张<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/bandier3.jpeg" alt=""></p>
<p>随后我们一路沿着塔身库尔干河一路前行，绵延的山脉下蓝色的河水流淌，行至一处峡谷，感觉两处的山拔地而起，气势恢宏，非常震撼，我们马上决定下车，自由行程就是这么随性~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiagu1.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiagu2.jpg" alt=""></p>
<p>心满意足的离开，最后一个景点是在塔县的酒店附近的石头城，虽然已经7点，但是大家兴致不减（bushi~来都来了），买了点零食充饥，坐景区的车到石头城，石头城不算大，很快走完了，不过石头城下面就是金草滩，看看新疆的湿地，景色还是不错，但是其实还是有点累了，微微的有些高反中。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/jincaotan1.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/jincaotan2.jpg" alt=""></p>
<p>9点多回到酒店，大家不想吃饭，打算点外卖，外卖味道一般，没有酒店的火锅和炒菜好吃。但是点都点了，点的还有点多了，吃了一些。此时还一切正常，只是半夜感觉心脏很累，醒了几次。本次行程第一个因为吃多了些难受的人出现了 o(╥﹏╥)o。</p>
<h4 id="DAY4-塔县-喀什"><a href="#DAY4-塔县-喀什" class="headerlink" title="DAY4 - 塔县-喀什"></a>DAY4 - 塔县-喀什</h4><p>今日行程路线：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng4.jpg" alt=""></p>
<p>一早起来比较难受，哇哇吐了好几次，高反的症状，早上也吃不下什么，吃了点粥。大家收拾行李出发。中间到了一个金草滩观景台，大家买了几个冰箱贴啥的，不过处于高反状态下的我还是没什么精神，早上吃的东西也是哇哇哇就吐了，不过胃里没东西反而好受些。<br>一路向高海拔的冰川行驶，到了慕士塔格冰川脚下景点已经是满目白雪，真的很冷。 12点的时候，景区说可以去冰川了，我们算是第一批上去了，我们这情况，肯定是要骑马上去了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/bingchuan1.JPG" alt=""><br>不骑马真的走不动一点~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/bingchuan3.jpg" alt=""></p>
<p>冰川真的很冷，又缺氧，又没有太阳。下马走到冰川观景台上都费劲，整个人就很眩晕，赶紧狂吸了一罐氧气才好些。冰川下真的非常冷，景色是很美，但是低温下高反下真的非常难受。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/bingchaun.JPG" alt=""></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/bingchuan2.JPG" alt=""></p>
<p>赶紧在牵马的工作人员再次接人上来后，马上下去才好些，在上面估计就呆了半小时不到。大家在景点吃了点东西，我是不敢吃了，不过也是不饿的状态。大家都调整好状态后，开车一路向下，海拔低了之后，整个人的状态就开始慢慢好起来了~</p>
<p>这次行程还是回到喀什，最后会路过白沙湖，我们上次只去了南岸，这次正好去下北岸，这次大家穿的足够，在栈道上走了很远。今天的天气可见度也好一些但是还是很冷，远处的雪山上环绕着一层层云雾，也是非常美。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/baishahu5.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/baishahu6.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/baishahu4.jpg" alt=""></p>
<h4 id="DAY5-喀什-莎车"><a href="#DAY5-喀什-莎车" class="headerlink" title="DAY5 - 喀什 - 莎车"></a>DAY5 - 喀什 - 莎车</h4><p>今日行程路线：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng5.jpg" alt=""></p>
<p>今日的行程是感受新疆的风土人文，首先去到了土陶村，虽然已经国庆了，但是游客还是比较少，刚进村就有村民卖自家种的葡萄和提子，买了几斤很实惠，还会帮忙洗好，葡萄和提子都非常非常甜，好吃停不下来。<br>村里有制作土陶的体验馆，看到有游客在体验，做完烧好可以寄回去。不过我们行程安排上有点来不及，就在村里到处逛了下，整个土陶村的颜色都很漂亮，墙壁和门框的花纹和色彩都很丰富绚丽，真的是热爱生活呀~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/tutao1.jpg" alt=""></p>
<p>打卡收集了各种颜色的门框~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/tutao2.jpg" alt=""></p>
<p>感觉土陶村应该没之前热闹，很多沿街的商铺都没开，附近的小刀村也没看到什么人，中途就没再多逛，中午时分就抵达了塔县。找了一家评价还不错的餐厅，吃了这边的特色烤鸽子和鸽子汤。我之前高反不敢吃太多战斗力不行~ 都给虫虫吃了。</p>
<p>吃饱喝足在路边还买了水果和切糕~ 入住的酒店还行，有健身房和洗衣机和烘干机，可以洗一波衣服。本来打算在酒店休息一会就去王宫，结果发现王宫一直在维修中不开放，莎车这边也都在搞基建，难怪很多地方都在动工。最后决定去王陵，就在王宫对面。而且上海身份证游客就可以免费进入~ 啊哈，就虫虫买了一个票和讲解票。有个漂亮的小姐姐讲解带我们走了一圈（不是很大，感觉十分钟能走完），讲了王陵的历史，十二木卡姆乐曲保存下来的故事，当地人对陵墓不忌讳，可以随意参观，而且周围很近的地方就是居民区。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/taxian.jpg" alt=""><br>不到半小时参观完王陵，王宫不开门，只能在王宫门口打了个卡，冲着王宫来的，可以提前看下是否开放哦~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/taxian1.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/taxian2.JPG" alt=""><br>不过这个时候虫虫开始肚子不太舒服了，之前还喝了两杯咖啡、还吃了水果、当地的一种饮料，感觉是一下子吃的有点多了，消化功能紊乱了。我们只能回酒店休息了~ 后面他实在太疼了，买了药到晚上吃药了躺着才好一点点。</p>
<h4 id="DAY6-莎车-喀什"><a href="#DAY6-莎车-喀什" class="headerlink" title="DAY6 - 莎车 - 喀什"></a>DAY6 - 莎车 - 喀什</h4><p>今日原计划行程路线：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng6.jpg" alt=""></p>
<p>今天的行程本来是去塔克拉玛干沙漠N39景区，结果虫虫实在是肚子疼的厉害，在景区门口说不行了，站不起来了，吓坏了，马上查医院，然后紧急开车我们回到了刚才经过的刀郎画乡的县医院，去急诊，先挂了一会水，然后还得去抽血、B超做了是胃肠炎，才开药挂水，经过4个多小时才好。</p>
<p>匆匆离开景区的门口~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/n39.jpg" alt=""></p>
<p>县城的医院，医护人员都比较少，人都很不错，医保也可以直接用，压针带上还画了个小人儿，有趣！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/yiyuan.jpg" alt=""></p>
<p>折腾了小伙伴半天，放弃了今日的景点（感谢小伙伴们、齐心协力帮我们克服困难），错过了一些景点，但是今天也会是难忘的记忆。<br>不管怎么兵荒马乱大家总算是在9点到了喀什的酒店，点了粥吃了一点，中间外卖员还吐槽说我们住的酒店不行，离景区远又不便宜。其实我们第二天也不在喀什，要去乌恰县，所以其实还好，哈哈，还挺好心热心。不过这几天真的身体备受摧残，还认真讨论了是不是要放弃后面的行程，直接回了，回去的机票还便宜。后面想了想，后面可能不太会再有重新走这段路线的机会了，而且明天还要去中国的最西边，这种「最」真的是让人无法拒绝。</p>
<p>晚上我们两吃不下，就喝了点粥，其他小伙伴去外面吃了。回来的时候买了一个大哈密瓜，非常甜。我吃了两块不敢多吃，但是因为这个哈密瓜太大了，fufu为了不浪费吃了很多。结果这趟因为多吃了导致不舒服的第三位受害者出现了~ </p>
<h4 id="DAY7-喀什-乌恰县-斯木哈纳村"><a href="#DAY7-喀什-乌恰县-斯木哈纳村" class="headerlink" title="DAY7 - 喀什 - 乌恰县~斯木哈纳村"></a>DAY7 - 喀什 - 乌恰县~斯木哈纳村</h4><p>今日行程路线：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng7.jpg" alt=""></p>
<p>这天还是照常9点多出发，也有很多人赶一赶可以喀什到西极后回到喀什，所以我们不当天来回时间还是很充裕的。第一站五彩滩很快就到了，有意思的是观景处的几个大石头涂了五彩的漆，可以看到远处的群山丹霞地貌~ 不过因为是路边停车，交警会来管制，不是特别严会让你待一会。今天的照片是酷酷的~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/wucaitan.jpg" alt=""></p>
<p>到了昆仑山天山交汇处，因为两座山的地貌有比较大的差异而形成了独特的风景，非常的大气磅礴。<br>这是算是一个比较正式的景点了，有停车场和小卖部之类的，在这里喝了杯咖啡、吃了泡面简单解决了午饭。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/shaungshan.jpg" alt=""></p>
<p>因为fufu领队吃坏了肚子，后面司机的任务交给了罗罗，还好一行人有三个老司机，非常的保险。去斯木哈纳村的路上会路过贝壳山，我们很开心很自信的去贝壳山企图挖几块曾经万年前沧海中的贝壳，但是还是挺难找的，这么些年很多人来过都挖完了。连滚带爬的下山（洞洞鞋真不适合爬这个山），找了山下的阿姨花十块钱买了几个贝壳化石，也是不虚此行了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/beikeshan.jpg" alt=""></p>
<p>先到了斯木哈纳村的酒店，入住后还早，修整两小时，还去了酒店附近的草场，看看牛马，看看小羊啥的，心里暖暖的，想去 rua 小羊屁屁，但是草场太大，追不上，可恶还是太弱了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiji.jpg" alt=""></p>
<p>休息的差不多，轻装出发去追中国的最后一缕阳光咯，驱车半小时到达了西极碑脚下，上去还花了十几分钟，云层很厚，太阳也快落山了，特别冷。看到有人举起国旗，什么花10块钱租的？那我们爱国 DNA 觉醒，必须得租啊，风再大天再冷也要举起大旗谁都挡不住我们的热情~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiji2.jpg" alt=""></p>
<p>尽兴而回，晚上吃个热乎的牛肉火锅~ 感觉又可以了！</p>
<h4 id="DAY8-塔县-喀什"><a href="#DAY8-塔县-喀什" class="headerlink" title="DAY8 - 塔县 - 喀什"></a>DAY8 - 塔县 - 喀什</h4><p>今日行程路线：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng8.jpg" alt=""></p>
<p>今天行程很简单，就是回到喀什，所以在下午两点前就到达了喀什古城，这次我们住的是古城东区内的民宿，大小和风格我还是挺满意的，入住后还是有很长的时间可以逛下古城。我们讨论下各自活动，在香妃茶馆喝了花茶（挺好喝，有点甜了），吃了新疆凉皮，凉皮真的很好吃。接着慢悠悠逛了东区古城的帽子一条街、小吃一条街、酒吧一条街、玉石小刀一条街，很热闹，中间还经过了买买提的老房子，买买提就坐在门口（感觉一直坐着被拍照会不会有点累）。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/chaguan.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/kashen2.jpg" alt=""></p>
<p>晚上就在小吃街吃了羊棒（感觉一般，调料有点奇怪吃不太习惯），后面再美食广场又吃了一份凉皮，个人感觉凉皮真的很好吃，广场上还有演出，可以边吃边看。古城的夜景也是很不错的，晚上到很晚都很热闹~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/kashen5.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/kashen6.jpg" alt=""></p>
<h4 id="DAY9-喀什"><a href="#DAY9-喀什" class="headerlink" title="DAY9 - 喀什"></a>DAY9 - 喀什</h4><p>今日就是休闲的逛逛景区，上午有个重点项目：喀什异域风情拍摄，我们住在东区的民宿，不过定的写真馆和拍摄都在西区，感觉西区更出片，西区的小巷和色彩更丰富。<br>拍摄都很快，妆造还行吧，在东区看到一些姐妹拍的时候卡粉很厉害，还好我们的还行！40分钟妆造，半小时拍摄，转一个小圈拍摄三十多张就结束了，很有流水线的感觉了。应该自己穿着衣服多拍一些，自己只拍了一点。<br>今日也是流水线公主！！！放图，非常多⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄~</p>
<p>全身的效果：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiezhen2.jpg" alt=""></p>
<p>半身的效果：中间两张是写真馆精修的（只修四张），修的很一般只能说，完全不像本人。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiezhen3.jpg" alt=""></p>
<p>这个小巷子很多人排队打卡拍照、确实挺出片<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiezhen1.jpg" alt=""></p>
<p>自己相机拍摄的一些也很不错，其实感觉和摄影师拍的差不多，而且会更自然一些，不过摄影师会教你摆一些出片的动作。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiezhen4.jpg" alt=""><br>不过不能穿着这套衣服逛街，如果要穿类似衣服逛街需要另外租。不过我没有去租其他衣服的打算，换自己的衣服逛逛也不错哒，在东区的拍照点打卡~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xiezhen5.jpg" alt=""></p>
<p>下午就是自己在喀什古城到处逛一逛了，我们还是在东区这片闲逛。名宿附近的corner coffee店很多人拍照打卡，在这里喝着咖啡能躺一天。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/kashen4.jpg" alt=""></p>
<p>还去了铁业公社，没想到铁器也可以很漂亮，还有一些地毯商店~逛下来大饱眼福。这个古城的色彩真的是很丰富、很热情~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/kashen1.jpg" alt=""></p>
<h4 id="DAY10-喀什-重庆"><a href="#DAY10-喀什-重庆" class="headerlink" title="DAY10 - 喀什 - 重庆"></a>DAY10 - 喀什 - 重庆</h4><p>行程前看机票和路线，最后定了会来可以绕道重庆，性价比还行是个不错的选择，今日的主要任务就是去重庆，因为是下午比较晚的飞机，上午再喀什接着逛了下。去了下清真寺🕌，门票三十，内部可以游览的地方很少很少，去的时候做礼拜也结束了有点遗憾。<br>有点无聊又绕着古城东区不太热闹的一边走了一圈，路上碰到的游客很少，这边应该是居民区了，路上还碰到一群小孩卖爆米花，满满一袋只要5块，小孩们坚持要装满满一袋子，好吃不腻，真的很nice~<br>接着就在香妃茶馆消磨时间，喝了一壶茶，最后我点的凉皮也没上真的很气，主要是凉皮真好吃，到现在还恋恋不忘。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/kashen8.jpg" alt=""></p>
<p>南疆的行程总是会结束，飞往重庆的飞机还是会起飞~ 到重庆酒店就夜里12点了，不过酒店环境真不错，舒服好评。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/chongqing6.jpg" alt=""></p>
<h4 id="DAY11-重庆"><a href="#DAY11-重庆" class="headerlink" title="DAY11 - 重庆"></a>DAY11 - 重庆</h4><p>今日就是随心逛，睡到自然醒然后酒店慢慢享用早餐，早餐很丰富，吃的很满足。酒店楼下就是个大商场，喝个咖啡奶茶啥的很方便。上午没出去，中午楼下吃了一碗牛肚粉，真的很好吃，辣的过瘾。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/chongqing1.jpg" alt=""></p>
<p>下午实在无聊，酒店看到对面有个庙。七拐八拐上台阶下台阶走过去，发现是关岳庙（原关帝庙），很清净，还看到一只慵懒的小猫在三清殿义工登记处窝着，原来还是只网红猫，叫元宝。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/chongqing.jpg" alt=""></p>
<p>正好遇到道长在教形意拳的公开课，马上加入练了半个多小时，做下来还是有点累的。道长还说可以报名，一般很多人会跟着学习一周，外地的提供住宿，留了联系方式，说不定哪天想去了呢。本来想吃下斋饭，不过错过饭点，斋饭不提供了。据说有俩尊财神可以拜拜很灵，好像没看到，亏了~ 不过还是很有缘分，闹中取静的好去处。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/chongqing4.jpg" alt=""></p>
<p>傍晚和小伙伴们在解放碑附近集合，正愁不知道去哪里吃饭，有个自称志愿者穿着黄马甲的人说可以推荐我们一些好吃的苍蝇馆子，还可以报个晚上的观光团从解放碑出发饶一圈到洪崖洞，一人只要198。还好心的开车带我们去旅行社等我们买完票再带我们去苍蝇馆子，只收10块钱油费！！从人心纯朴的新疆回来的我们，觉得这真是大好人（🖤🖤🖤）。 等我们吃上了点好的烤鱼，感觉不对劲，真的很难吃，就是把鱼弄熟，放了一堆蔬菜，没有味道。一看点评上的评价，果然都是被拉过来，然后觉得又贵又不好吃的。。。。。。吃完根据之前『志愿者』的指点的方向想找观光团集合点，结果发现和地图方向相反，好吧，还是跟着地图走了，又七拐八拐跟着地图到达了集合点。发现是美食广场，闻起来还挺香。但是来不及吃了，大巴到了。<br>坐着大巴开过彩虹立交，然后上去看了下，接着去江边看了一会儿夜景，随后去了洪崖洞江对面的一个小水池，说可以拍洪崖洞的倒影。然后跨过跨江大桥到了观光的终点站，洪崖洞附近的天台131，观光票包含天台131高空观景台门票，先坐电梯再走楼梯上去，上面人很少，因为是最后一波人了，活动和商场都关门了，没什么意思，结果最佳观景位置还有人在旅拍，不让过去，好好好，那我们只能随便看看赶紧下去了。被观光团硬控的3小时结束，走到洪崖洞随意看了看，也十点多了，赶紧回酒店休息吧~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/chongqing2.jpg" alt=""></p>
<h4 id="DAY12-重庆-上海"><a href="#DAY12-重庆-上海" class="headerlink" title="DAY12 - 重庆 - 上海"></a>DAY12 - 重庆 - 上海</h4><p>回程的飞机是下午三点，时间充裕，依然睡到自然醒，在酒店慢悠悠吃完早餐。之前就看到酒店餐厅附近有健身房游戏房啥的，最重要的是还有一个自助DIY的美甲台，哦豁~ 做个美甲，美美哒，开心回家！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/chongqing3.jpg" alt=""></p>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>这次行程虽然一开始以为就是吃吃喝喝美食，快乐看美景，结果没有预想中吃到那么多美食，一半是因为高原会教训吃太多的人，一半是踩到了山城的小坑。两个美食出名的地方，没吃过瘾有点小遗憾。<br>But 美景是真的~</p>
<p>费用人均：1W左右，12天，真的非常划算了。</p>
<p>完美，撒花~~</p>
<h5 id=""><a href="#" class="headerlink" title=" "></a> </h5><h5 id="-1"><a href="#-1" class="headerlink" title=" "></a> </h5><h5 id="-2"><a href="#-2" class="headerlink" title=" "></a> </h5><h5 id="-3"><a href="#-3" class="headerlink" title=" "></a> </h5><h5 id="-4"><a href="#-4" class="headerlink" title=" "></a> </h5><h5 id="-5"><a href="#-5" class="headerlink" title=" "></a> </h5><h5 id="-6"><a href="#-6" class="headerlink" title=" "></a> </h5><h5 id="-7"><a href="#-7" class="headerlink" title=" "></a> </h5><h5 id="-8"><a href="#-8" class="headerlink" title=" "></a> </h5><h5 id="-9"><a href="#-9" class="headerlink" title=" "></a> </h5><h5 id="-10"><a href="#-10" class="headerlink" title=" "></a> </h5><h5 id="-11"><a href="#-11" class="headerlink" title=" "></a> </h5><h5 id="-12"><a href="#-12" class="headerlink" title=" "></a> </h5><h5 id="-13"><a href="#-13" class="headerlink" title=" "></a> </h5><h5 id="-14"><a href="#-14" class="headerlink" title=" "></a> </h5><h5 id="-15"><a href="#-15" class="headerlink" title=" "></a> </h5><h5 id="-16"><a href="#-16" class="headerlink" title=" "></a> </h5><h5 id="-17"><a href="#-17" class="headerlink" title=" "></a> </h5><h5 id="-18"><a href="#-18" class="headerlink" title=" "></a> </h5><h5 id="-19"><a href="#-19" class="headerlink" title=" "></a> </h5><h5 id="-20"><a href="#-20" class="headerlink" title=" "></a> </h5><h5 id="-21"><a href="#-21" class="headerlink" title=" "></a> </h5><h5 id="-22"><a href="#-22" class="headerlink" title=" "></a> </h5><h5 id="-23"><a href="#-23" class="headerlink" title=" "></a> </h5><h5 id="-24"><a href="#-24" class="headerlink" title=" "></a> </h5><h5 id="-25"><a href="#-25" class="headerlink" title=" "></a> </h5><h5 id="-26"><a href="#-26" class="headerlink" title=" "></a> </h5><h5 id="-27"><a href="#-27" class="headerlink" title=" "></a> </h5><h5 id="-28"><a href="#-28" class="headerlink" title=" "></a> </h5><h5 id="-29"><a href="#-29" class="headerlink" title=" "></a> </h5><h5 id="-30"><a href="#-30" class="headerlink" title=" "></a> </h5><h5 id="-31"><a href="#-31" class="headerlink" title=" "></a> </h5><h5 id="-32"><a href="#-32" class="headerlink" title=" "></a> </h5><h5 id="-33"><a href="#-33" class="headerlink" title=" "></a> </h5><h5 id="-34"><a href="#-34" class="headerlink" title=" "></a> </h5><p>发现彩蛋：</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan1.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan2.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan3.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan4.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan5.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan6.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan7.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan8.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/caidan9.JPG" alt=""></p>
<p>今天又是开心的一天呢~</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>出行日期：2024/09/22 - 2024/10/08<br>游玩方式：自驾、6人<br>出行目的地：南疆<br>出行必备：墨镜、防晒霜、帽子、冲锋衣（羽绒服）等防晒保暖用品。最最重要的是需要办理边防证（可以把新疆全域都写上）。</p>
<p>本次行程定制、出行方式、机酒预订本人都没有参与，还是熟悉的人员配方，还是一键跟随模式，主打一个听安排。以下是行程路线图，仅供参考：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%8D%97%E7%96%86/xingcheng.jpg" alt=""></p>
<p>国庆高峰出行，这个行程看似简单，但实际上完美的避开了高峰期，没有遇到堵车，封路，风雪等恶劣天气。全靠队友给力，赞~  我只能写写流水账+风景贴图+自恋贴图，收集一波回忆素材。</p>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[东京休闲六日游]]></title>
    <link href="http://yoursite.com/2024/05/15/%E4%B8%9C%E4%BA%AC%E4%BC%91%E9%97%B2%E5%85%AD%E6%97%A5%E6%B8%B8/"/>
    <id>http://yoursite.com/2024/05/15/东京休闲六日游/</id>
    <published>2024-05-14T19:27:41.000Z</published>
    <updated>2024-05-23T06:19:09.397Z</updated>
    <content type="html"><![CDATA[<p>五一假期将近，计划出游的时间到了，合计了下，日本签证还没过期，汇率也还不错，想着正适合去东京。而且去年也去过关西，经验还在，遂做了个大致计划主打休闲不累为主，定好机酒出发。这次计划依然家属负责，我主打一键跟随，不带脑子。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/plan.jpg" alt=""></p>
<p>出行日期：2024/04/29 - 2024/05/04<br>道具准备：</p>
<ul>
<li>电子设备： mba，手机，充电：type-c ，watch，iphone，相机充电器。</li>
<li>相机选择：A7M4+70200GM2 , 富士 x100vi</li>
<li>日本 sim 卡、Suica（西瓜卡）、日元现金 4w（都是刷卡，真准备太多了）</li>
</ul>
<p>冰箱贴 +3  ^_^<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dongjing.jpg" alt=""></p>
<a id="more"></a>
<p>相关游记：<br><a href="https://www.xuyanlan.com/2023/07/01/%E6%97%A5%E6%9C%AC%E5%85%B3%E8%A5%BF%E4%B9%8B%E6%97%85/" target="_blank" rel="noopener">日本关西の旅</a>、<a href="https://www.xuyanlan.com/2018/09/12/%E5%86%B2%E7%BB%B3%E5%9B%9B%E6%97%A5%E4%B8%89%E6%99%9A%E6%B8%B8%E8%AE%B0/" target="_blank" rel="noopener">冲绳游记</a></p>
<h4 id="DAY1-上海-东京，逛上野公园"><a href="#DAY1-上海-东京，逛上野公园" class="headerlink" title="DAY1 上海 - 东京，逛上野公园"></a>DAY1 上海 - 东京，逛上野公园</h4><p>坐早上7点50的上海出发的飞机直飞，东京时间12点半准时抵达~<br>抵达成田机场，人还是挺多的，边填写海关申报，随着人潮走电子申报通道出关后，找一找KS标识，发现直接下B1就能看到蓝色的购票窗口，看了下最近的车次时间正好，人工那边人也挺少，所以直接在人工买票刷西瓜卡+ 1300 的 Skyline 票，直接进站后车很快就到了，车程45分钟直达酒店所在的上野站，上野站出来发现人还是不少的，商店餐馆也很多，还有北京烤鸭~<br>乘车直达上野可以参考小红书： <a href="http://xhslink.com/Vh4NyH" target="_blank" rel="noopener">http://xhslink.com/Vh4NyH</a></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/skyline.jpg" alt=""></p>
<p>到了酒店，工作人员引导在酒店的自助机器上进行checkin，之前选了熊猫主题房间，其实就是楼层有些熊猫元素，哎哟，熊猫跟我们拿了同款相机。进房间发现还有个熊猫玩偶，不得不说，房间真小。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/jiudian.jpg" alt=""></p>
<p>中午随便在酒店附近的小店吃了，再走一小段路就是上野公园，公园其实不大。正值春天，公园绿意盎然，整体还是很舒服的，小风吹着，风景也不错，但是美术馆动物园什么的没开放，就只随便走走逛逛清水观音寺、花园神社⛩，在不忍池边上有些小吃摊，闻起来不错，点了一个烤蟹肉棒，咬一口，真不好吃 o(╥﹏╥)o </p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/gongyuan2.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/gongyuan1.jpg" alt=""></p>
<p>逛了几个小时差不多了，查到上野附近有个比较大的上野中华街，其实逛完上野公园出来时就经过了，晚上决定就去这了，到了发现人还挺多的，很多日本年轻人在一起非常开心的喝酒聊天。吃的类型还是非常多的，找了一个海鲜店，有一些人排队，排了不久就排到了，自助扫二维码点餐，这个时候发现，微信的翻译还挺好用，拍照发到微信聊天，一键翻译，不用担心看不懂菜单，哈哈，又get 一个小 tip。点了烤蟹腿和玉子烧，感觉还蛮好吃的~不赖！<br>尝试了下自带氛围滤镜的相机拍夜景，还可以哟~ ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄<br>今天就在上野站圈子附近活动了，Easy 模式，不累。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/wancan1.jpg" alt=""></p>
<h4 id="DAY2-东京迪士尼-Sea"><a href="#DAY2-东京迪士尼-Sea" class="headerlink" title="DAY2 东京迪士尼 Sea"></a>DAY2 东京迪士尼 Sea</h4><p>第二天按计划去东京迪斯尼 Sea，东迪分 Sea 和 Land，因为说 Land 其实没有多少好玩的项目，而且Sea 不是在水上乐园的意思，是不湿身的，可能是能看到海的原因吧，最终当然选择了去 Sea，提前买好了门票，下载好了app，直接跟着 GoogleMap 公共交通去就行了。</p>
<p>在八点半左右到达迪士尼，还是熟悉的音乐，氛围一下就起来了，入口就看到了一个巨大的水上地球雕塑，排队等了半小时，发现东迪的动物是不怕人的，应该是游客会喂食所以不怕人。9点开始入园，因为在五一前一天来的，所以人其实没有想象中的多，据说后面五一期间人非常多。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dongdi1.jpg" alt=""></p>
<p>游玩项目简介:<br>1）Tower of Terror ， 惊魂古堡<br>2）Indiana Jones Adventrue: Temple of the Crystal Skull , 印第安琼斯冒险<br>3）Soaring: Fantastic Fight , 翱翔：梦幻奇航<br>4）Journey to the center of the Earth,  地心探险<br>5）Raging Spirits， 忿怒双神<br>6）DisneySea Transit Steamer Line， 迪士尼海洋渡轮。<br>7）Big Band Beat， 米奇米妮演出。 </p>
<p>入园后马上就看到一个湖和一个火山造型的建筑，看比较热门的项目是飞跃地平线、惊魂古堡、地心探险，翱翔梦幻奇航据说和上海迪斯尼的飞跃地平线没有啥区别（结尾的区别），所以第一时间冲了惊魂古堡，惊魂古堡其实就是跳楼机，中间会营造一些恐怖的氛围，应该是最刺激的一个项目了，搞笑的是第一个项目就和另一个中国人撞了玲娜贝儿的包，哈哈~ 第一个项目玩完就看了下排队情况，体力还行就去排了 40 分钟的愤怒双神，360旋转的过山车，其实也还好，有个360度的小旋转。出来正好碰到了迪士尼海洋渡轮表演，迪士尼人物会在火山前的湖上坐船出场表演，虽然是湖上表演，但是正对火山的位置应该是比较好的观赏位置了。入园时就在APP上买好了地心冒险和烟花位置，看时间正好，就去玩了地心冒险，坐着就行都不用系安全带的，全程会有地心奇景的布局装饰，冒险氛围有了，最后会从之前提到的火山造型的地方喷出来（看过小说的猜到了，早有心理准备，就这一下还比较刺激），和加勒比海盗有点像。</p>
<p>注意上午入园后就可以再 App上领取快通，我们选择了印第安琼斯冒险的快通（上午时间段）、预约了 Big Band Beat 米奇米妮5点半的演出，提前预约的好处是看演出时会分配固定坐位，所以不用担心跟别人抢位置。</p>
<p>地心探险出来拍拍照啥的就到吃饭时间了，这时候感觉快累趴了，我们就选择在地心探险这边的中式餐厅（火山餐厅）吃饭休息了。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dongdi2.jpg" alt=""></p>
<p>休息好了就快到了印第安琼斯冒险快通的时间，正好开始继续下午的项目。下午排队的人明显少了，除了翱翔梦幻奇航人还多些，其他的都是50分钟以下，相比上海已经是人很少了，所以体验了印第安琼斯冒险 - 坐着吉普车在黑暗的环境下一路前进，路上会突然出现大蛇（假的）、喷火、突然吊下个人，惊吓连连。之后看到愤怒双神可以领取快通了，就又去玩了一次，360度旋转过山车而已拿下。出来后发现阳光很好，完全没有天气预报的阴天，非常适合拍照。一路拍拍照，看看项目，发现地心探险排队只有二十分钟，震惊，就又去玩了一次，不然上午的钱感觉花的不值，平衡一下，哈哈😂。后面又去了海底两万里体验了下，就是在一个类似潜艇的装置里，透过玻璃看外面模拟的水下场景，为了体现水深压力大装置里弄的比较热，整体一般般吧，不过只要排队十分钟（好像拿了快通），无所谓。还去体验了下旋转木马，根本没人呀，在上海迪士尼体验不到的项目（排队太久）</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dongdi3.jpg" alt=""></p>
<p>一路玩玩拍拍照也是很累了，撑到了 Big Band Beat米奇米妮演出的时间，到了检票时间就进去了，位置是之间区域，正对舞台，不远不近，挺好的，主要是舞蹈演出，演员很有激情，观众也很配合打拍子互动，感觉日本的民众还挺喜欢这种的，配合的挺 high 的，不过我可太累了，算是坐着休息了半小时吧~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dongdi4.jpg" alt=""></p>
<p>晚饭后就去了花钱选的烟花秀的位置，其实没有固定位置，就是能在湖边的位置坐地上看，因为怕下雨带了一次性雨衣，正好可以坐地上，很多有经验的，带了野餐垫去坐着。八点开始烟花秀，烟花秀是围绕着湖进行的，延伸到了湖边我们所在位置后面的建筑群的墙面上，以及火上的墙面上，所以范围还是挺广的，看不过来了，和上海迪斯尼城堡光影秀+烟花比，烟花没那么多，但是有真人扮演者在湖面坐着船进行表演，配合迪斯尼IP的一些动画进行播放，视觉和互动都有了，体验感很好~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dongdi5.jpg" alt=""></p>
<p>看完烟花秀就跟着人流散场了，一天下来还是比较累的，整体排队比想象中的少很多，节前去真是明智的选择，本来以为是阴天有雨，实际上还幸运的有半天大晴天。nice day ~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dongdi6.jpg" alt=""></p>
<h4 id="DAY3-东京购物-银座、秋叶原"><a href="#DAY3-东京购物-银座、秋叶原" class="headerlink" title="DAY3 东京购物 银座、秋叶原"></a>DAY3 东京购物 银座、秋叶原</h4><p>五一这天计划时看大概率下雨，所以安排的购物，果然下了一天的雨，天气稍冷。也不着急，就睡到自然醒，酒店吃完早点就直接去银座，到那边看到三宅一生排队了很多了，还需要预约才能进店，只能放弃。看 LV 的店，已经排队很多人了，而且是在雨里排队，不想排，也放弃。看了下 SKII、SYL 美妆，价格也没有很实惠，也没有心情选购。去 Dior 看了之前种草的包包，结果说五月二号才卖，比国内便宜4千多rmb吧，还行，店员说可能附近另一家 Dior 今天会卖，不过贵600，想了想去看了下，结果试下来没那么喜欢，果断放弃了。结果去优衣库逛了很久，买了4件T恤，1件防晒衣，加起来3百多rmb吧。逛街太累了，真的。找了家寿喜烧补充体力，味道不错！</p>
<p>撤的时候发现三宅一生可以不需要预约进去，就去看了看，结果看中的包包都是 Display Only，无语，本来店里就没几个包，一堆人排队。不懂这操作~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/gouwu1.jpg" alt=""></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/gouwu2.jpg" alt=""></p>
<p>转战去秋叶原，有很多手办和动漫周边，我是不太感兴趣了，逛街太累了，只想回酒店，结果啥也没买成。今日花费300多买衣服，吃饭花了700多！</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/gouwu3.jpg" alt=""></p>
<h4 id="DAY4-大室山、热海花火大会"><a href="#DAY4-大室山、热海花火大会" class="headerlink" title="DAY4 大室山、热海花火大会"></a>DAY4 大室山、热海花火大会</h4><p>去伊豆大室山就必须得开车无法走公共交通，而且预计路上应该会有点堵车，因为五一日本也放假大家都出来玩儿了，而且他们放9天！最后决定报了一个十几人的一日游团，不过旅游公司都是不能超过10小时工作时间，因为要看8点多的花火大会，所以只能是十一点以后出发，我们选择了相对来说比较适合的，十一点出发的，十一点过几分钟，大家都集合完毕，都是中国人，司机和导游也是。一路上最开始还好，但是还是堵了一个小时也是有心理准备了，一路上导游特意选了沿海的高速，还在风景不错的休息区停车休息了一下，不过午餐只能简单的在车上吃自己准备的食物了。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dashishan.jpg" alt=""></p>
<p>三点一刻的时候才到了大室山（《你的名字》取景地），天气看起来不太好，能见度不太行，云层黑厚，而且人很多，大家一起下车排队买票坐缆车，因为要保护环境，只能缆车上去。中间吃了下这边的绝品小吃，吃起来还不错！</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dashishan1.jpg" alt=""></p>
<p>等买好票的时候已经过了一个小时，不过非常幸运的是，坐缆车准备上去的时候，天气突然变得很好，太阳出来了，整个景色变得生机勃勃，整片绿色。运气真的很好~感觉排队也很值！ 上得山顶，就看到一个很大的火山坑，真的和《你的名字》场景很像了，有种时空相撞的错觉。 围着火山坑走一圈下来能够看到山下大片居民区和远处的海，坑里有两个红色的小房子，配合着好天气真的很舒服，很出片。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dashishan2.jpg" alt=""></p>
<p>山顶收获一波美照~ 哈哈 自恋！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dashishan3.jpg" alt=""></p>
<p>不过因为导游定的行程5点要下去，结果我们念念不舍下去，然后导游又说很多人没下来不然等到5点半吧（因为5点下就真的挺赶），ε=(´ο｀*)))唉，不早说咯，只能在山脚下再拍会儿照片，买点纪念品等其他人。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dashishan4.jpg" alt=""></p>
<p>5点半导游和大家商量不去第二个景点 ~ 城琦海岸，因为时间上比较赶，而且到了都六点多，没有很好的光线就是个普通的海岸，大多数人都同意了，我们也同意了，因为感觉不想太累太赶，而且对我们没什么特别的吸引力，应该没有冲绳万座毛那边的海岸美。所以省去一个行程，直接去热海，到热海还不到7点，还可以在看花火之前去逛下商业街，吃个东西。不过商业街吃的地方人都不少，便利店也是大排长龙。最后找到一个可以外带的西餐店，点了一个青花鱼汉堡🍔，味道还不错，比想象中的好吃。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/rehai1.jpg" alt=""></p>
<p>八点二十，热海花火大会如约而绽放，热海的花火大会会持续好几个月，在夏日祭的时候都会燃放，最初为了纪念被海淹没的村子，逐渐形成今日的仪式。花火大会整个过程二十分钟，今天非常多人一起在海岸边一起看烟火燃放，本地人感觉很有激情，看到绽放的好的还会一起鼓掌，感觉他们仪式感更强些，这个气氛一下子就上来了感觉很好。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/rehai.jpg" alt=""></p>
<p>因为导游提前预约了停车场，并让我们看完马上往车上赶，趁着人潮和车潮没有开始堵塞，赶紧开车往外走，我们旅游车出去算是非常早了，一路上一点没耽误，2个半小时就到了早上出发的地方。到酒店也没有很晚，看后面的消息称很多人十一二点还在那边堵着，感觉这天的行程包车还是超值的~</p>
<h4 id="DAY5-涩谷、富士山"><a href="#DAY5-涩谷、富士山" class="headerlink" title="DAY5 涩谷、富士山"></a>DAY5 涩谷、富士山</h4><p>谁家去著名景点之前还去购物啊！也只有我们了，因为五一那天没买到什么动漫周边，查了半天决定去涩谷的任天堂店，然后看人家商场是十一点开门，于是十一点赶到，发现其实任天堂那层十点就开门了，没事，毕竟多睡了一小时，开心。结果过去人巨多，还要去领取预约进场的时间劵。只能领取20分钟后的了，到了时间买了很多塞尔达的周边，把现金基本上花了。现金+刷卡大概花了 rmb 2千多，然后家属还拉着我去迪斯尼Store逛了一圈，真是爱逛街的男人！累~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/segu.jpg" alt=""></p>
<p>买完再去车站已经十二点多了，刷西瓜卡+买了特急的高铁车次（无座，不过如果黄红色指示灯可以坐，黄色是下站有人，红色是无人购买座位），等车的时候，在站台买了牛舌自热饭，抽掉一根绳子加热五分钟就行，感觉还挺好吃，就是菜太少，四片牛舌两根辣椒，饭根本吃不完啊。一路上还行，基本上都是坐着的。 去河口湖又要转乘电车？还是火车？人挺多的，所以自由席车厢也有点拥挤，我们在最后面半坐着，也还行。快到河口湖的时候，基本上就能看到富士山了，今天的能见度很高。幸运~</p>
<p>三点多才到了河口湖车站，很多人直奔往河口湖的公交去了，不过我们本来想去另一个打卡点 ~ 本栖湖（富士山芝樱祭），但是售票员一直跟我们解释，过去和回来都大堵车，看我们想去非常着急，说我们过去可能就玩不到20分钟，但是要花很久的时间来回。听人劝，我们没去，就在河口湖车站的村子里面溜达了一大圈，然后再罗森打开点拍照了，然后还走到了居民区很深的地方去了，人还挺少，可能本地人五一假期也都出去玩了吧~</p>
<p>罗森打开点，人车很多，勉强打了几张能看的。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/fushishan1.jpg" alt=""></p>
<p>村子里看到一些很舒服的房子，住里面应该非常惬意吧<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/fushishan2.jpg" alt=""></p>
<p>村里菜地很多，种的菜很漂亮<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/fushishan5.jpg" alt=""></p>
<p>找的奇奇怪怪的机位<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/fushishan4.jpg" alt=""></p>
<p>本来想去的本栖湖 - 小红书图片，下次去！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/fushishan3.jpg" alt=""></p>
<h4 id="DAY6-逛成田机场、回上海"><a href="#DAY6-逛成田机场、回上海" class="headerlink" title="DAY6 逛成田机场、回上海"></a>DAY6 逛成田机场、回上海</h4><p>因为没买到包包，所以提前了一个小时去机场，结果 T2 没有 LV 也没有 Dior，更没有三宅一生。看到了 Chanel 没人排队，就进去看了下，不太了解，之前想看的几个热门的包包没有，有个带钻的 trendy cc 还挺适合, 价格比国内便宜1W左右，不过也要4.8W 了，想了下没什么场合背，就溜了，有点后悔o(╥﹏╥)o。出来的时候看到已经排队很多人了，去看了下其他的东西，也没啥买的兴趣。飞机起飞后大概17、8分钟的时候路过了富士山，特意选了能看到富士山的一侧。没想象中的震撼，应该是雪化了太多吧，有点秃秃的。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/fushishan6.jpg" alt=""></p>
<p>下午三点准时到达上海，行程结束<br>总计花费：2人吃住行大概2W左右吧，机票比较贵9K。购物3K左右~</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>五一假期将近，计划出游的时间到了，合计了下，日本签证还没过期，汇率也还不错，想着正适合去东京。而且去年也去过关西，经验还在，遂做了个大致计划主打休闲不累为主，定好机酒出发。这次计划依然家属负责，我主打一键跟随，不带脑子。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/plan.jpg" alt=""></p>
<p>出行日期：2024/04/29 - 2024/05/04<br>道具准备：</p>
<ul>
<li>电子设备： mba，手机，充电：type-c ，watch，iphone，相机充电器。</li>
<li>相机选择：A7M4+70200GM2 , 富士 x100vi</li>
<li>日本 sim 卡、Suica（西瓜卡）、日元现金 4w（都是刷卡，真准备太多了）</li>
</ul>
<p>冰箱贴 +3  ^_^<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com//dongjing/dongjing.jpg" alt=""></p>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[马尔代夫-瓦卡鲁岛游记]]></title>
    <link href="http://yoursite.com/2023/10/08/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB-%E7%93%A6%E5%8D%A1%E9%B2%81%E5%B2%9B%E6%B8%B8%E8%AE%B0/"/>
    <id>http://yoursite.com/2023/10/08/马尔代夫-瓦卡鲁岛游记/</id>
    <published>2023-10-08T15:14:50.000Z</published>
    <updated>2023-10-16T07:38:06.320Z</updated>
    <content type="html"><![CDATA[<h4 id="行程日期-amp-目的地"><a href="#行程日期-amp-目的地" class="headerlink" title="行程日期 &amp; 目的地"></a>行程日期 &amp; 目的地</h4><p>日期：2023/09/26 - 2023/10/02<br>目的地：马尔代夫-瓦卡鲁岛，这次旅程是这么多出行旅游中最轻松的一次了，出发前休息了几天，回来后又休息了几天。不过因为我全程没有操心行程安排、机票酒店啥的、所以我应该是最轻松的了(✧∇✧)。</p>
<p>整个行程基本上都是和朋友们在瓦卡鲁岛上待着，吹吹海风看看美景，换个地方躺着的假期。很蓝，很惬意~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/wakalu2.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/wakalu1.jpg" alt=""><br><a id="more"></a></p>
<p>这次是度假风的出行，基本上在酒店岛 - 瓦卡鲁岛上呆着，是在马代实验室上定的行程，会有客服小姐姐负责对接和安排去马尔代夫的行程，到了马累机场也会有专门的人负责接送到岛上。在岛上也安排了管家全程对接。会负责接送去岛上的任何地方。马累和北京的时差有三小时，不过瓦卡鲁岛和北京的时差是两小时。整体行程安排是26号从上海飞机八小时去马累，然后27号早上坐水飞机去岛上，一直在岛上带到1号下午。再回到上海就是2号早上了。</p>
<h4 id="衣"><a href="#衣" class="headerlink" title="衣"></a>衣</h4><p>这个时间马尔代夫的天气比较舒服、穿短袖不冷不热，不过比较是在海边，所以可以准备多一些的衣服，可以多拍些照片。朋友还自己准备了浮潜的衣服和设备，岛上也会免费提供浮潜的装备和救生衣，我们浮潜就穿泳衣加短袖长裤，感觉也没什么不方便。<br>经过我一个月的各种买裙子，后来发现，我真的没有挑裙子的眼光。锻炼了一个月，也没瘦几斤。不过出去玩嘛，最重要的是开心咯。<br>在岛上的第二天，天气很好，阳光很好，岛上感觉更美了，拍了挺久的照片，小伙伴们都晒伤了，不过我没有，我可能晒习惯了，也可能皮厚，^<em>^ O(∩</em>∩)O ~。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/paizhao01.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/paizhao02.jpg" alt=""></p>
<h4 id="食"><a href="#食" class="headerlink" title="食"></a>食</h4><p>在去马尔代夫之前，就听说这边的酒店岛的饮料比较贵，而且我们定的是包含早餐和晚餐的行程，午饭我们考虑本身那边食物我们也吃不惯，就从国内带了一些可乐喝方便面之类的过去，反正行李箱位置也挺多，哈哈。<br><img src="/2023/10/08/马尔代夫-瓦卡鲁岛游记/wufan.jpg"><br><!-- ![](https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/wufan.jpg) --><br>到达马累后，看了下附近的吃的，也没看懂，就在住的酒店吃的自助餐，15$一个人，吃的选择很少，感觉体验很一般，不符合我们的饮食习惯。<br>到了岛上后，第二天中午大概当地时间十一点就上岛了，所以送了我们一个午餐，在岛上的第一餐是在意大利餐厅，后面的早餐都是在一个地方，早餐很丰富，面包水果饮料咖啡都有，味道都很不错，需要热的食物需要点餐，不限量。我们点的比较多的早餐是鸡肉面条汤（大多数时候没有面条，哈哈），鸡蛋欧姆蕾，煎蛋烤肠培根之类的，因为午饭我们自己用泡面解决，而且一天也没有什么着急的行程，所以早上吃的都比较晚，可以慢慢的享受早餐。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/zaocan.jpg" alt=""><br>餐厅外的景色也是绝美~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/zaocan01.jpg" alt=""></p>
<p>岛上有5个餐厅，我们每天选择的餐厅不一样，意大利餐厅、亚洲餐厅、海鲜餐厅、日式餐厅都去过，都是前菜主菜甜品可以各选一个菜品，除了日式餐厅，其他餐厅量是挺多的，如果有特别吃不了的他们会问，你可以换一个~ see see 我们吃的是啥吧~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/canting01.jpg" alt=""><br>餐厅当然也都是在海边啦~ 意大利餐厅晚上会有两个人在现场唱歌，唱的还是很好听的~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/canting02.jpg" alt=""></p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/yidali.mp4"><br></video>

<h4 id="住"><a href="#住" class="headerlink" title="住"></a>住</h4><p>到马累需要坐飞机8小时，到了之后，水飞机场关门了，所以在马累需要先住一晚，说实话，马累住宿的那个酒店比较简单也比较小，属于将就过渡一下的住宿点。不过在外面看了一圈，感觉本身马累觉也没有什么比较豪华的酒店。<br>第二天中午大概当地时间十一点就上岛了。在岛上的前两天是住的水屋，后面两天住的是沙屋。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/fangjian.jpg" alt=""></p>
<p>这边的房间都很大，卫生间衣帽间都很大，水屋可以躺着看海，水不深，可以直接从水屋下去可以浮潜，这边不深的水都是能看清下方的白色海底，是白色的砂石和珊瑚，所以整个海水是蒂芙尼蓝。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/shuiwu05.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/shuiwu04.jpg" alt=""></p>
<p>我们第一天下午用无人机拍了下，但是后面收到酒店的提醒，说隐私问题不能拍的。这个之前也是确实不知道~ 后面就没办法航拍了~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/shuiwu01.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/shuiwu02.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/shuiwu03.jpg" alt=""></p>
<p>刚去岛上的第一天上午下大雨，一整天也没有什么大太阳，但是刚上岛也是感觉不虚此行，第二天一早6点多就醒了，天气很舒服，在阳台看着天一点点变亮，很惬意。</p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/shuiwu02.mov"><br></video>

<p>沙屋其实也挺不错，就在沙滩边上，还带着个小泳池，但是感觉这个泳池并不能游泳，而且比水屋还更贵些。不过我个人还是更喜欢水屋，能躺着看海，吹海风。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/shawu.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/shawu02.jpg" alt=""></p>
<h4 id="行"><a href="#行" class="headerlink" title="行"></a>行</h4><p>从到马累机场后，一路上的行程交通都有人帮忙接送。不过本身马累也挺小的，第一天从机场去马累的酒店很快，第二天去水飞的机场也很快，不过一直有雨，第一次起飞突然雨变得很大，只能回来等了半小时再飞。<br>水飞也花费了将近一个小时了，在飞机上能看到一个个沉没的小岛，那些蒂芙尼蓝的小环就是了~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/huodong02.jpg" alt=""></p>
<p>整个岛就是一个酒店，里面只有游客和酒店人员，还有健身房、清晨瑜伽、spa馆、羽毛球篮球网球沙滩排球啥的户外游戏区域，海上项目啥的像船🚣🏻，帆板滑水这种是可以随便去的，需要出海的就看行程中有没有带了，没带的要去的话还是挺贵的，也有室内的游戏房可以玩一些简单的桌游啥的，也有台球乒乓球桌上足球这些。在岛上，有一个全程的管家会帮忙接送我们去想去的地方，这是我们的ebi~~在等我们~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/ebi.jpg" alt=""></p>
<p>去体验过清晨瑜伽，动作不会太难，一早练完还是挺舒服的，不过属于要提前一天预定的。室内的游戏房也去过，因为我们在十一前几天去的，这些地方人都挺少的，体验很好。<br>预定的行程中需要乘船出海的活动只能选择一个，我们选择了海龟浮潜。另外还有环礁浮潜、海钓、水上摩托可以选。像追海豚、深潜这些项目都挺贵的，考虑到性价比不高，没去体验，去体验了spa，做spa过程中可以看到海，做完还可以去汗蒸之类的~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/huodong01.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/jianshengfang.jpeg" alt=""><br>浮潜时带着手机下去拍的~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/fuqian.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/end04.jpg" alt=""></p>
<h4 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h4><p>整个行程两个人大概5w吧，另外我们两个人准备了250$，正好付小费和做spa用完了，在机场吃麦当劳和买一些纪念品花了大概50$吧。总共花费就是这些了。<br>需要注意的是换的钱有些折痕磨损，他们不收，不过后面在国内换成了人民币。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/end02.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/end01.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/end03.jpg" alt=""></p>
]]></content>
    <summary type="html">
    <![CDATA[<h4 id="行程日期-amp-目的地"><a href="#行程日期-amp-目的地" class="headerlink" title="行程日期 &amp; 目的地"></a>行程日期 &amp; 目的地</h4><p>日期：2023/09/26 - 2023/10/02<br>目的地：马尔代夫-瓦卡鲁岛，这次旅程是这么多出行旅游中最轻松的一次了，出发前休息了几天，回来后又休息了几天。不过因为我全程没有操心行程安排、机票酒店啥的、所以我应该是最轻松的了(✧∇✧)。</p>
<p>整个行程基本上都是和朋友们在瓦卡鲁岛上待着，吹吹海风看看美景，换个地方躺着的假期。很蓝，很惬意~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/wakalu2.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E9%A9%AC%E5%B0%94%E4%BB%A3%E5%A4%AB/wakalu1.jpg" alt=""><br></p>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[日本关西の旅]]></title>
    <link href="http://yoursite.com/2023/07/01/%E6%97%A5%E6%9C%AC%E5%85%B3%E8%A5%BF%E4%B9%8B%E6%97%85/"/>
    <id>http://yoursite.com/2023/07/01/日本关西之旅/</id>
    <published>2023-06-30T19:11:11.000Z</published>
    <updated>2024-10-16T12:07:41.952Z</updated>
    <content type="html"><![CDATA[<p>日本4月底放开，机票价格挺美丽，就计划腾个假期去日本关西玩玩，最后定了端午去，不过等正式定行程的时候，机票贵了挺多，不过相比四月份还是便宜很多的。最初我们想定私人跟团游，但是日本的包车真的很贵，遂放弃了。最终行程和机酒路线都是自己定制，交通大部分选择公共交通。不过相比国内的交通，日本交通复杂很多，同一个车站有不同目的地，同一个车不同车厢还有可能去的地方不一样。总之，跟着google map的提示来就行，好在很多地点是中文字，细心一些不会错。</p>
<ul>
<li>结果很美丽，收获超多开心和回忆~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E6%97%A5%E6%9C%AC%E5%85%B3%E8%A5%BF%E8%A1%8C/jijin.jpg" alt="">  </li>
</ul>
<a id="more"></a>
<h4 id="行程日期-amp-路线"><a href="#行程日期-amp-路线" class="headerlink" title="行程日期 &amp; 路线"></a>行程日期 &amp; 路线</h4><p>日期：2023/06/17 - 2023/06/23<br>准备：交通卡 - Suica（西瓜卡），苹果手机可以在钱包内直接添加~ 便利店和公交电车都可以刷<br>计划行程，实际还是和计划有一些差别</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/xingcheng.jpg" alt=""></p>
<h4 id="day1-杭州-大阪关西机场-京东"><a href="#day1-杭州-大阪关西机场-京东" class="headerlink" title="day1 - 杭州-大阪关西机场-京东"></a>day1 - 杭州-大阪关西机场-京东</h4><p>下午从杭州出发，国内出境需要健康申报，日本入境提前进行海关申报、入境审查啥的。不过出发时航班延迟了一个小时，到关西机场已经7点半，入境办理出机场已经8点30了，我们出机场去高铁站购买去京都的高铁，虽然提前在携程订了票，但是一定要有纸质的车票，排队折腾选时间，幸好提前做了攻略最后顺利搞定，终于订到了倒数第二班的高铁，点赞，但是来不及进行选座，选座需要用车票再次在机器上操作，不得不感叹好麻烦，没选座整个过程加排队也超过半小时了。<br>冲进高铁站，等了十分钟左右高铁到了，居然是HelloKitty车列，正好排到了无需选座的车厢，很顺利，座位还挺舒服，到了京都，转坐公交车，几站路就到了酒店附近。终于在东京时间11点多入住了酒店休息。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/gaotie.JPG" alt=""></p>
<h4 id="day2-京都-清水寺、伏见稻荷大社"><a href="#day2-京都-清水寺、伏见稻荷大社" class="headerlink" title="day2 京都 - 清水寺、伏见稻荷大社"></a>day2 京都 - 清水寺、伏见稻荷大社</h4><p>日本的京都，是有名的寺庙之城，来京都肯定少不了去清水寺和伏见稻荷大社，去了发现日本的寺庙文化是深入民众的，这些寺庙都有很多学生成群结队的进行参观，学习。<br>去清水寺可以顺便去二年坂三年坂，感觉是个成熟的商业街，上下坡走的有些累。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/ernianban.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/qingshuisi1.jpg" alt=""><br>清水寺也没有深入玩，就打了下卡，感觉那边寺庙风格比较有特色、清水寺的山号为音羽山，主要供奉千手观音。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/qingshuisi.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/qingshuisi2.jpg" alt=""></p>
<p>游完清水寺已经中午了，找了一家评价不错的炸牛排店解决午饭，两人花费将近1w日元，味道还不错。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/zhaniupai.jpg" alt=""></p>
<p>下午做几站电车然后跟着Google Map 走过了几个村子，七拐八拐到了伏见稻荷大社，还穿过了动漫里面经常看到的电车红绿灯~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/dianche.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/cunzhuang.jpg" alt=""></p>
<ul>
<li>日本伏见稻荷大社建于8世纪，主要是祀奉以宇迦之御魂神为首的诸位稻荷神。稻荷神是农业与商业的神明，香客前来祭拜求取农作丰收、生意兴隆、交通安全。它是京都地区香火最盛的神社之一。在神社里，还能见到各式各样的狐狸石像，这是因为狐狸被视为神明稻荷的使者。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/damen.jpg" alt=""></li>
</ul>
<p>伏见稻荷大社里面最让人印象深刻的就是那绵延山头达数公里的千本鸟居，数量惊人的鸟居高达万座以上，大大小小的鸟居一直延伸到稻荷山顶，非常惊人。鸟居是「门」形状的 -&gt; ⛩️⛩️⛩️，上面会有署名还愿或者是一些结婚纪念、公司的供奉等。</p>
<p>鸟居是日本神社建筑物。主要用以区分神域与人类所居住的世俗界，算是一种结界，代表神域的入口，可以将它视为一种「门」。鸟居一般有两根支柱，上面有两个横梁，也有上面有题字的匾。一般用木材制造，刷上生漆。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/niaoju1.jpg" alt=""></p>
<p>我们一路沿着鸟居全程走下来了，从2点走到了5点。</p>
<p>一路上会有各种神，可以选择祈福捐赠，写心愿，也可以只参观。还有腰神不动神？ 那就去求下吧，毕竟坐办公室久了经常腰酸背痛~<br>最后不得不说，塞尔达里面的祈祷神像真的很像这里面的看到的神像了~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/shenxiang.jpg" alt=""></p>
<p>到了晚上不得不感叹，这边的商店和饭店下班真的好早，基本上8点就关门了，找了一圈吃饭的，最后找到了一个24小时的，点了荞麦面，自己选择配汤，还挺好吃。第二天一早也是吃的这家，因为很多店早上10点之后才开门。</p>
<h4 id="day3-平等院、奈良公园、东大寺、春日大社、若草山"><a href="#day3-平等院、奈良公园、东大寺、春日大社、若草山" class="headerlink" title="day3 平等院、奈良公园、东大寺、春日大社、若草山"></a>day3 平等院、奈良公园、东大寺、春日大社、若草山</h4><p>因为这天的计划是从京都去大版，带着两个大行李，如果再去景点就很麻烦了，就包了一天的车，从9点半到7点半，大概2k+，司机师傅是中国人，所以也没交流问题。</p>
<p>在携程定包车的时候，客服小姐姐说可以加一个景点，平等院，最开始计划中没有，后面她看顺路就去了，结果我下车忘了那钱，又得走回来拿现金，那边寺庙门票好像只能现金，不过门票都不贵，300日元左右就行。<br>平等院进去还是感觉很凉快的，遇到很多日本的学生成群结队的在里面参观，真的很多学生。目前10元的日元币面就是平等院正面，很多人带着硬币打卡。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/pingdengyuan1.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/pingdengyuan.jpg" alt=""></p>
<p>奈良公园、东大寺、春日大社、若草山其实都在一个区域。<br>我们直接再奈良公园下车，直接就能看到很多鹿🦌🦌🦌🦌在路边溜达，一点不怕人，还会点头跟人祈饼，很可爱。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/nailianggongyuan1.jpg" alt=""></p>
<p>之后我们一路走到了东大寺，进去参观了一下，只有一个主殿，主殿很高大。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/dongdasi.jpg" alt=""></p>
<p>参观完东大寺，已经中午了，我们在附近吃了中饭，休息了一下，就前往春日大社。中间路过了万叶植物园，不过这个季节不是植物园的观赏最佳的季节。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/chunridashe.jpg" alt=""></p>
<p>来之前在小红书查看攻略，很多姐妹都推荐奈良公园附近的若草山，说是很美，人少，鹿多。我们看时间还早，就觉得去那边待着，需要门票，五点前进就行。去之前买了两卷鹿饼。爬上去花了20分钟，还挺累，山上人比较少，而且，全部都是中国游客！！！ 都是小红书导航过来的吧~ (#^.^#)</p>
<p>山脚是个大草地，很多人在这里休息，小孩子玩闹~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/ruocaoshan.jpg" alt=""></p>
<p>上顶风很大，风景确实美，真的很出片~ 两盒鹿儿饼可以和小鹿玩很久。就是因为包车有时限没办法等落日。有点遗憾呢~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/ruocaoshan1.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/ruocaoshan2.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/ruocaoshan3.jpg" alt=""></p>
<p>6点多到了接下来几天要一直住的酒店，就在大阪的市中心，离梅田、心斋桥都比较近。环境还不错哦～ 视野很好<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/dabanjiudian.jpg" alt=""></p>
<h4 id="day4-大阪环球影城"><a href="#day4-大阪环球影城" class="headerlink" title="day4 大阪环球影城"></a>day4 大阪环球影城</h4><p>此行的一个另一个重点来啦，就是大阪环球影城，为了避开周末，前几天的行程特意安排到了京都奈良的景点，因为快速票实在买不到，只能顶门去，早上6点起床收拾好，6点半出发，好在出酒店就有出租车，直接打车到USJ，不过一刻钟的路程，加上过路费，就要5600+日元，对比国内感觉是十倍的价格了，难怪之前看的包车游非常贵。言归正传，我们到的时候，人还不多，我们直接排到了队伍的前面，过十分钟左右，人就非常多了。8点之前，有工作人员在场外维持秩序，还会给生日月的游客发生日贴纸，我正好是前一天生日，也给我发了一个，开熏～～ 一路上都有人跟你说 happy birthday，还怪不好意思嘞～</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj1.jpg" alt=""><br>八点的时候早鸟票的游客进去了，过了大概一刻钟之后，就准备放我们进入了，进去之后就跟着大家跑啊跑，一路跑到任天堂园区，我们在里面买了手环，撞了一会金币，就去排酷霸王挑战书，到的早几乎没有排队，就走进去就排到了，是个马里奥赛车类似的互动攒金币的游戏，很好玩。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj2.jpg" alt=""></p>
<p>期间领取了10点半再次进入任天堂园区的券，不领取后面是进不来的哦。后面我们就出任天堂园区去玩了飞天翼龙，排队大概排了40分钟。是倒着的旋转过山车，还是有些刺激的。 玩完正好，再进任天堂园区，这个时候就要排队查资格卷了。 我们再次进来后，就可以安心的收集金币、跟玩偶互动和收集金钥匙🔑了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj3.jpg" alt=""></p>
<p>早早领取了马里奥餐厅的券，但是也是快3点才能去吃了，真的饿啊。 餐厅吃的造型还行，味道只能说还可以。但是餐厅还是可以休息滴，回血一波～<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj5.jpg" alt=""><br>完成了三个金钥匙的收集，解锁了能量手环的关键挑战-酷霸王最终挑战，没有集齐三把钥匙是无法玩这个的哦~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj4.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj10.jpg" alt=""><br>任天堂园区最后的战果～ 金蘑菇 【能量手环的关键挑战获得】，奖杯🏆【酷霸王挑战书获得，需要开始玩和结束都刷手环！虫虫痛失一个🏆】<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj6.JPG" alt=""></p>
<p>到6点左右，很多项目排队时间都比较短，这个时候去玩，可以花很少时间玩很多项目。哈利波特、蜘蛛侠等都是互动性很强，反馈很强的游戏，和上海迪士尼的加勒彼海盗有些类似，但是相比剧情和互动还要多哦～ 小黄人和穿越地平线设计有些像，而且只有6个人一组，视角就很好，震动和体感反馈也很有意思。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj8.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj9.jpg" alt=""></p>
<p>玩到8点半真的玩不动了，撤了，今日成就。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/usj7.JPG" alt=""></p>
<h4 id="day5-大阪城、购物"><a href="#day5-大阪城、购物" class="headerlink" title="day5 大阪城、购物"></a>day5 大阪城、购物</h4><p>环球影城玩儿的太累了，后面两天都安排的休闲游，先去大阪城逛了逛～<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/dabancheng.jpg" alt=""></p>
<p>再去商场看看有什么可以买的，结果塞尔达的周边没有什么东西了。只能随便看看了～ 中午吃了海鲜自助～<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/daban.jpg" alt=""></p>
<h4 id="day6-回杭州"><a href="#day6-回杭州" class="headerlink" title="day6 回杭州"></a>day6 回杭州</h4><p>回程是晚上的票，所以上午还是有空再随便走走，昨天在商场看到了铃芽之旅的画展今天开，而且塞尔达周边没买到，所以还是想去碰碰运气。所以我们又去了梅田，没想到，今天塞尔达补货了，果断买买买。 这是我给自己买的。哈哈，每天工作看到都很开心～<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/daban1.jpg" alt=""></p>
<p>买买买之后还是有不短的时间，我们想去看画展，但是他吗线上购票的流程我们实在看不懂，现场的工作人员指引我们去对面地铁附近的罗森买，想着去碰碰运气，到了罗僧，工作人员让我们自己在loppi机上买票，啊这这这谁看的懂啊。后来求助好些人都被拒绝了，后来有两个大学生模样的男生帮助了我们，真的太感动了，买票真的步骤很繁琐，不懂日文真的搞不定，最后还是帮我们买到啦～<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/daban2.jpg" alt=""></p>
<p>最后飞机回去的时候，起飞的时候正好落日，飞机上升过程中，不断的看到落日，还挺浪漫～  伴随着落日，旅程结束。 完美 Ending ～<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%97%a5%e6%9c%ac%e5%85%b3%e8%a5%bf%e8%a1%8c/daban3.jpg" alt=""></p>
]]></content>
    <summary type="html">
    <![CDATA[<p>日本4月底放开，机票价格挺美丽，就计划腾个假期去日本关西玩玩，最后定了端午去，不过等正式定行程的时候，机票贵了挺多，不过相比四月份还是便宜很多的。最初我们想定私人跟团游，但是日本的包车真的很贵，遂放弃了。最终行程和机酒路线都是自己定制，交通大部分选择公共交通。不过相比国内的交通，日本交通复杂很多，同一个车站有不同目的地，同一个车不同车厢还有可能去的地方不一样。总之，跟着google map的提示来就行，好在很多地点是中文字，细心一些不会错。</p>
<ul>
<li>结果很美丽，收获超多开心和回忆~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E6%97%A5%E6%9C%AC%E5%85%B3%E8%A5%BF%E8%A1%8C/jijin.jpg" alt="">  </li>
</ul>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[呼伦贝尔草原行「逃离城市 & 逃回城市之旅」]]></title>
    <link href="http://yoursite.com/2022/10/09/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94%E8%8D%89%E5%8E%9F%E8%A1%8C/"/>
    <id>http://yoursite.com/2022/10/09/呼伦贝尔草原行/</id>
    <published>2022-10-09T10:07:23.000Z</published>
    <updated>2022-10-13T00:40:51.226Z</updated>
    <content type="html"><![CDATA[<h3 id="前序"><a href="#前序" class="headerlink" title="前序"></a>前序</h3><p>疫情已经快三年了，今年因为各地疫情此起披伏，一直不能远行，今年国庆眼看还有两周了，也没什么旅游长线可选，正好朋友推荐一起去呼伦贝尔，查了下，呼伦贝尔暂时还是安全的，所以就敲定了呼伦贝尔线路，太需要让自己放松下了。不过时间太赶，自驾游是来不及定了，所以就跟了游侠客的团，之前稻城亚丁川西行也是跟的游侠客，还不错，绝对不是购物团。</p>
<p>临近出发时，说满洲里因为疫情不能去，所以原定的左边的环线变成了右边的，去除了满洲里的行程，这年头，能出去就不错了，不挑也不遗憾。当下我们合计了下原定从海拉尔回来的机票改签了成了阿尔山飞回的机票，省事还便宜。没想到后面的变数越来越精彩，下文再详述。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/xingcheng.jpg" alt=""></p>
<a id="more"></a>
<h3 id="day1-～-集合"><a href="#day1-～-集合" class="headerlink" title="day1 ～ 集合"></a>day1 ～ 集合</h3><p>我们从杭州飞往海拉尔，另外两个朋友 steven 和迪哥带了个新朋友 justin(又名蓝莓，因为请妹子们吃不洗的蓝莓而被团员们传颂！)) 从上海飞到海拉尔。到达后游侠客安排好了接机服务，前往呼伦贝尔酒店，路上就感受到了温度的骤降，大概只有十几度。</p>
<p>当晚我们在人气很高的卢布里西餐厅（俄式）品尝到了和平时口味很不一样的美食。烤肉量很大，味道不错，瓦罐牛肉、奶酪面包、俄式盘肠也很好吃。皇后沙拉味道有些奇怪，但是也不错，可以尝尝。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/lubuli.jpg" alt=""></p>
<p>不过新结识的盆友 justin 因为肚子不舒服，没有吃到，导致我们也吃不完，所以把烤肉和俄肠打包带走。</p>
<p>饭后大家在海拉尔的伊敏河边散步，期待着第二天的旅途开始。</p>
<h3 id="day2-～-穿越呼伦贝尔大草原、根河"><a href="#day2-～-穿越呼伦贝尔大草原、根河" class="headerlink" title="day2 ～ 穿越呼伦贝尔大草原、根河"></a>day2 ～ 穿越呼伦贝尔大草原、根河</h3><p>7点半一早就集合出发（说好的休闲团呢），出发前就感受到了天气的寒冷，直接上羽绒服了。然后四人一车坐越野车穿越呼伦贝尔草原（全长300公里），抬头可见的蓝天白云下，是一望无际的大草原，黄色的大草原中有着低头吃草的马儿牛羊，途中路过无人的牧场，停车悄悄跨过牧场围栏去看超大的草卷。领队坑坑同学非常热情的帮我们团员们拍照，据说之前跟摄影团的哟~</p>
<p>车上拍的大草原 <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/caoyuan.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/caoyuan2.jpg" alt=""></p>
<p>草卷 <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/caojuan.JPEG" alt=""></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/caojuan2.jpg" alt=""></p>
<p>研究完牧场草卷后，又接着开到了观看莫日格勒河的最佳位置，莫日格勒河九曲十八弯，300公里的草原上弯曲出1500公里河道，描绘出了一副完美的草原风景画。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/morigele2.jpg" alt=""></p>
<p>莫日格勒河 ～ 摄影团朋友拍摄 <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/morigele.JPG" alt=""></p>
<p>十一点到达额尔古纳市区，在额尔古纳市区草草解决了中饭后，换乘大巴车，十二点半直奔根河大兴安岭景区，一路上途经了额尔古纳湿地，沿途能看到大片的桦树林和落叶松。途中还有游侠客一贯的自我介绍环节，大家基本都是上海和杭州来的，职业都是互联网，游戏，金融，HR啥的，很年轻、很有活力、很有爱的队伍。带队的是坑坑同学（一个热爱旅游的活力女子），地陪导游是一淼（来自东北，是给我们安排景点门票、酒店、做核酸等等事情的，非常辛苦），还有大巴车司机来自东北的很圆的廖师傅～～</p>
<p>两个小时左右到达根河大兴安岭景区，需要爬木栈道上去，不算高，慢慢走不是很累，沿路都是白桦树，不过来晚了，之前下过雨，树叶都落了很多，剩下的也是黄灰色，航拍也不是金黄的，不过一片片的白桦树连起来，也挺壮观，空气非常棒。一路上能看到对面的根河。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/genhe.JPG" alt=""></p>
<p> 根河航拍 <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/genhe2.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/genhe3.JPEG" alt=""></p>
<p>晚上在根河市住宿，吃了当地的特色铁锅炖排骨，还可以炖大鹅、鸡、鱼~ 人均70，贼好吃！还记得昨天打包的烤肉和烤肠吗？对，我们吃不下了，哎，损失一个打包盒。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/tieguodun.jpg" alt=""></p>
<h3 id="day3-～-莫尔道嘎、室韦"><a href="#day3-～-莫尔道嘎、室韦" class="headerlink" title="day3 ～ 莫尔道嘎、室韦"></a>day3 ～ 莫尔道嘎、室韦</h3><p>第二天温度已经零下了，说话能看到哈气的那种冷。今天是八点出发，住宿的地方提供的早餐非常简单，馒头玉米粥鸡蛋和小菜，据说后面的行程的条件会越来越简陋。<br>前往莫尔道嘎的路上是穿越大兴安岭，一路上很少有信号，两边都是小白桦树，其实顶部还是有些黄叶子，很想下去看看，但是因为跟团，无法自由行动，希望后面的景区让人满意吧…</p>
<p>到达莫尔道嘎正好赶上十点半的小火车，运气不错，是个网红小火车 ～ 大红色滴很上镜，而且错过这趟就得等一个小时。森林小火车全程一个半小时，会停四次，每次停不到十分钟，所以没什么好玩的，就在车上看看风景，火车车速不快，不过我们带了无人机，冒险跟拍了小火车，虽然紧张，但是跟拍顺利，整体感觉不错。</p>
<p>不可错过的烤肠 <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/xiaohuoche5.JPG" alt=""></p>
<p>小火车跟拍图 &amp; 视频 <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/xiaohuoche2.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/xiaohuoche3.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/xiaohuoche4.JPG" alt=""></p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/xiaohuoche.MP4"><br></video>

<p>莫尔道嘎森林中还保存着撮罗子，是鄂温克族人的居住的房子。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/cuoluozi.jpg" alt=""></p>
<p>下午三点左右就到了边境小镇~室韦<br>为了离俄罗斯更近，我们在额尔古纳河（界河）上了游船近距离眺望了下俄罗斯奥诺起小镇，感觉俄罗斯的的边境的建筑相比室韦的建筑也过于简陋了。 在游船甲板上隔岸看了下俄罗斯，不过因为温度太低了，而且当天还是多云，特别冷～ 在船上就更冷了，没看多久就回了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/bianjing.JPG" alt=""></p>
<p>晚上找了一家评分比较高的当地俄餐，除了炒酸黄瓜和沙拉，其他的菜品还是挺好吃的，蓝莓果汁也好喝，就是老板娘有点冷酷。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/ecan.JPG" alt=""></p>
<p>晚上就在室韦住，套娃酒店，酒店挺漂亮，房间蛮大，还有落地窗，能看到对面俄罗斯小镇。不过水特别黄，脏脏的，晚上还停电了一小时，配合低温的环境，这体验自行体会，不过内蒙的住宿环境就是都差不多，放低预期就行。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/taowajiudian.JPG" alt=""></p>
<h3 id="day4-～-白桦林、俄罗斯家访"><a href="#day4-～-白桦林、俄罗斯家访" class="headerlink" title="day4 ～ 白桦林、俄罗斯家访"></a>day4 ～ 白桦林、俄罗斯家访</h3><p>还是八点出发，前往白桦林景区的路上在恩和小镇看了下俄罗斯族民俗馆，特别小，内部只有各种动物的标本（只有皮毛是真的），而且内部没有取暖设备，太冷了，没仔细参观。大家没什么参观的兴致了，所以提早一些出发去白桦林景区，国庆过来，白桦树已经只剩下顶部还有一些些叶子了，有点遗憾，不过白桦树树干本身就很白，成片成片的白桦树看起来还是不错的。<br>景区需要坐观光车到终点，有一个栈道穿过白桦林到起点，中途还可以喂驯鹿，天气非常不错，蓝天白云太阳很暖和，不冷，大家热情高涨玩了两个小时。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/baihualin.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/baihualin2.JPG" alt=""></p>
<p>航拍图<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/baihualin3.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/baihualin4.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/baihualin5.JPG" alt=""></p>
<p>中午回到额尔古纳市区吃饭已经一点了，吃好饭还需要做核酸，前往额尔古纳湿地时已经两点多，不过湿地车程只有十几分钟就到了，到了地方，就是一个观景台，无法进入湿地内部，风很大，这个时候云很多了，挡住了阳光非常冷，不想去走观景栈道，就一直在观景台用无人机看风景。好在后面太阳出来了，看起来还不错。<br> <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/shidi3.JPG" alt=""><br> <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/shidi4.JPG" alt=""></p>
<p>俄罗斯家访<br>从额尔古纳湿地回到额尔古纳市区后直接去了访俄户，女主人玛丽莎热情的用当地的列巴加盐（面包和盐是他们最珍贵的物资）习俗在门口迎接我们每一个人，准备了各色的糕点宴会，宴会上还高歌了两曲，热情奔放，不愧是战斗民族。宴会后还玩了几个小游戏（碰蛋和抢棍棍），还安排我们换俄罗斯服装，不过太冷了，穿了好几层衣服就上了，穿起来很臃肿，衣服很美丽，但是穿起来并不十分美丽。重在体验嘛~  </p>
<p>俄罗斯服装、骑装、蒙古服装对比图<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/fuzhuang.jpg" alt=""></p>
<p>晚上住额尔古纳大酒店，酒店设施不错，也挺大，早餐相对室韦来说好很多了，之前一直渲染说后面伙食住宿会越来越差，也并不是嘛~</p>
<h3 id="day5-～-草原骑马、访蒙户"><a href="#day5-～-草原骑马、访蒙户" class="headerlink" title="day5 ～ 草原骑马、访蒙户"></a>day5 ～ 草原骑马、访蒙户</h3><p>上午八点半从酒店出发，一小时就到了蒙古黑山头的马场，这边沿路有很多马场，途中也能看到牧羊人和成群的羊，这天天气也不错，大太阳云不算多，骑马也不太冷。我们人多所以马群不太会跑很快，如果只有几个人马很容易跑起来。虽然说不能在马上拍照，但是我这个马还是挺温和的，所以途中也拍了很多照片…在中点能看到额尔古纳湿地，回程的时候，马也知道快下班了，很兴奋，就跑起来了，这个时候需要踩稳，顺着马。下马以后小腿会酸，可以拍一拍缓解，第二天腰背稍微有些酸痛。</p>
<p>很酷的带队骑官 - 隔壁摄影团拍的 <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/qima.JPG" alt=""><br>很酷我们 <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/qima2.JPG" alt=""></p>
<p>中午在蒙古包吃烤全羊，蒙古包挺好看<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/menggubao.jpg" alt=""></p>
<p>下午去了访蒙户，换上了蒙古服装，坐上了网红小火车，喂了小羊，射歪了几箭，拔输了河。下午一般云就多起来了，还是比较冷，而且这次的蒙古服装为了好看，衣服穿很少，简直美丽冻人。回来还发现掉了大疆的手持云台，里面还有未完成的旅行集锦，很可惜( •̥́ ˍ •̀ू )<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/menggufu.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/menggu.JPG" alt=""></p>
<p>晚上五点多回到海拉尔，此行住的最好的酒店~百府悦酒店，设施不错早餐也很OK，就是太热了~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/jiusian.JPG" alt=""></p>
<p>又回到海拉尔，据说索伦噶烤肉不错，9个小伙伴一拍即合果断去。嘎嘎好次，烤羊肉串针不戳哦~ 人均100多点，很实惠。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/suolunga.JPG" alt=""></p>
<h3 id="day6-～-巴彦呼硕牧场"><a href="#day6-～-巴彦呼硕牧场" class="headerlink" title="day6 ～ 巴彦呼硕牧场"></a>day6 ～ 巴彦呼硕牧场</h3><p>早上七点得知，5号晚上兴安盟发布通知说进阿尔山的外部人员需要隔离三天，但是早上又有新通知说给三天缓冲期，等到九点，前面团队的人成功进入了阿尔山后我们才出发，上午在巴彦呼硕牧场（科尔沁草原）看了最大的敖包（没错，就是敖包相会的敖包），据说诚心绕着敖包三圈许愿就能实现哦~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/keerqin3.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/keerqin.JPG" alt=""><br>在牧场近距离看了奶牛… <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/keerqin2.JPG" alt=""></p>
<p>中午只能在车上解决午餐，等到四点才到达阿尔山，进入阿尔山马上进行了核酸落地检。<br>阿尔山是最小的地级市，是中国最小的城市，只有三万多人，是个非常宁静的城市，只有一条主街，几分钟就走完了。并且水质非常好，所有生活用水都是矿泉水~ 空气非常棒，淘宝还有卖阿尔山空气的呢。哼哼~多呼吸多挣钱。<br>参观了下阿尔山的火车站，看了看绿皮火车，围观着这不多见的交通工具，感叹着它的原始！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/huochezhan.JPG" alt=""></p>
<p>到达圣彼得堡大酒店后时间还早，太阳还没有下山，我们航拍了下阿尔山整座城市和落日，也是挺美的。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/aershan.JPG" alt=""></p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/aershan.mp4"><br></video>

<p>圣彼得堡大酒店整体的风格比较复古，个人还是挺喜欢的，而且水真的非常好，洗脸都感觉脸很干净很滑。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/jiudian2.JPG" alt=""></p>
<p>晚上正打着牌呢，群里突然通知说阿尔山8号静默，而我们是8号阿尔山的飞机，大家只能紧急买7号唯一一趟下午四点多去沈阳的绿皮火车，然后8号从沈阳飞回家。机票买了又担心赶不上，又改签，折腾到凌晨…</p>
<h3 id="day7-～-阿尔山"><a href="#day7-～-阿尔山" class="headerlink" title="day7 ～ 阿尔山"></a>day7 ～ 阿尔山</h3><p>7号这天已经没有选择了，海拉尔7号突然通知开始静默，其他团员原定海拉尔走的也只能走沈阳方案了…<br>既然四点多才去沈阳，阿尔山景区还开放，那只能去玩了，所以上午赶着核酸点上班前去排队核酸，完了之后就直奔阿尔山景区，景区里面几乎没什么人了，都是游侠客团的人，属于包场了真是。快十点半到达杜鹃湖（5月份杜鹃盛开最好看），湖面结冰了，远处还有一群白天鹅，饲养员很担心白天鹅被冻住了，让我们无人机过去看了一下，好在它们都好好的呢。 接着马不停提的去了热门景点驼峰岭天池，好在这天天气非常好，晴空万里，而且前几天下了大雪，树下还覆盖了一层厚厚的白雪，算是赠送冬景福利了。回去的路上顺便看了下阿尔山大峡谷。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/aershan2.JPG" alt=""></p>
<p>驼铃峰天池、大峡谷、杜鹃湖上的白天鹅<img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/aershan1.JPG" alt=""></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/aershan3.JPG" alt=""></p>
<p>下午三点半到达阿尔山火车站，开始了逃离阿尔山之路，后面得知这趟火车是本月最后一趟火车了。真的挺幸运~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/huochezhan2.JPG" alt=""></p>
<h3 id="day8-～-THE-END"><a href="#day8-～-THE-END" class="headerlink" title="day8 ～ THE END"></a>day8 ～ THE END</h3><p>阿尔山 -&gt; 沈阳 绿皮火车半日体验，时间很赶，只能买到硬卧了，在车上有一个团比较折腾了，他们5号来的，只看了下天池和大峡谷就被通知酒店不让住了，让撤离。几乎没开始旅行就结束了。<br>到达沈阳，沈阳做了落地检，之后从沈阳飞回，不过各地政策不一，飞上海的凭借绿码顺利回家，杭州和深圳的团友则需要3+4居家隔离了。 不过不管怎么样，顺利到家就是最好的，不然只能留在内蒙就业了。</p>
<h3 id="摄影团美图"><a href="#摄影团美图" class="headerlink" title="摄影团美图"></a>摄影团美图</h3><p>火车上结识的热爱摄影的老大哥，他们此行起早贪黑挨冻拍摄的作品集<br> <img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/shidi.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/shidi2.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/sheying2.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/sheying4.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/sheying5.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/sheying6.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/sheying7.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/sheying8.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/sheying3.JPG" alt=""></p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>总体来说去的景点比较少，每天一个的样子，出发都比较早，晚上到酒店也比较早，整体休闲，在大巴车上的时间很多。坐的挺宽敞，但是坐车也挺累。</p>
<p>费用总结<br>报团费用：5000 - 240（最后一天提前离开） = 4760 （每人）<br>飞机票+火车票：飞机改签了几次，大概 4000 （每人）<br>吃饭：800（每人）<br>每次旅行总得丢下点什么，出发前丢了一双鞋子、途中丢了一个大疆手持云台。</p>
]]></content>
    <summary type="html">
    <![CDATA[<h3 id="前序"><a href="#前序" class="headerlink" title="前序"></a>前序</h3><p>疫情已经快三年了，今年因为各地疫情此起披伏，一直不能远行，今年国庆眼看还有两周了，也没什么旅游长线可选，正好朋友推荐一起去呼伦贝尔，查了下，呼伦贝尔暂时还是安全的，所以就敲定了呼伦贝尔线路，太需要让自己放松下了。不过时间太赶，自驾游是来不及定了，所以就跟了游侠客的团，之前稻城亚丁川西行也是跟的游侠客，还不错，绝对不是购物团。</p>
<p>临近出发时，说满洲里因为疫情不能去，所以原定的左边的环线变成了右边的，去除了满洲里的行程，这年头，能出去就不错了，不挑也不遗憾。当下我们合计了下原定从海拉尔回来的机票改签了成了阿尔山飞回的机票，省事还便宜。没想到后面的变数越来越精彩，下文再详述。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%91%BC%E4%BC%A6%E8%B4%9D%E5%B0%94/xingcheng.jpg" alt=""></p>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[青甘大环线]]></title>
    <link href="http://yoursite.com/2021/05/10/%E9%9D%92%E7%94%98%E5%A4%A7%E7%8E%AF%E7%BA%BF/"/>
    <id>http://yoursite.com/2021/05/10/青甘大环线/</id>
    <published>2021-05-10T13:31:56.000Z</published>
    <updated>2022-05-30T08:55:56.815Z</updated>
    <content type="html"><![CDATA[<h3 id="行程介绍"><a href="#行程介绍" class="headerlink" title="行程介绍"></a>行程介绍</h3><h4 id="行程日期"><a href="#行程日期" class="headerlink" title="行程日期"></a>行程日期</h4><p>前序部队：2021/04/28 - 2021/05/09<br>集合部队：2021/05/01 - 2021/05/09</p>
<h4 id="行程路线"><a href="#行程路线" class="headerlink" title="行程路线"></a>行程路线</h4><p>西宁-&gt;张掖-&gt;敦煌-&gt;鸣沙山-&gt;苏干湖-&gt;茫崖翡翠湖-&gt;西台吉乃尔湖-&gt;水上雅丹-&gt;东台吉乃尔湖-&gt;大柴旦翡翠湖-&gt;茶卡盐湖-&gt;青海湖-&gt;西宁<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map.png" alt=""></p>
<h4 id="行程亮点"><a href="#行程亮点" class="headerlink" title="行程亮点"></a>行程亮点</h4><p>翡翠湖、雅丹地貌、大戈壁、青海湖、敦煌、美食</p>
<a id="more"></a>
<h3 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h3><p>提前一个月定机票，路线，自驾用车，酒店，根据天气准备衣物~ 一定要做好防晒和补水。墨镜要有，否则盐湖教你散光！保暖也不能少，不仅昼夜温差大，风还贼大。</p>
<h3 id="人物介绍"><a href="#人物介绍" class="headerlink" title="人物介绍"></a>人物介绍</h3><p>虎虎（路线规划，导游，司机，摄影师，后勤），英英（美女模特），阳阳（美女模特），罗罗（司机，摄影师），虫虫（主力摄影师，司机），水水（摄影师，观察员）</p>
<h3 id="前预部队篇"><a href="#前预部队篇" class="headerlink" title="前预部队篇"></a>前预部队篇</h3><p>虎虎，英英，阳阳，罗罗在五一前三天就已经先抵达了西宁，他们去了塔尔寺、卓尔山、祁连山草原、青海湖环线。据他们的描述，说卓尔山体验非常棒，还进行了青海湖深度环湖游，羡煞我等！并且虎虎说还有惊喜等我我们。这个惊吓就是在一只横穿马路的小狗和速度贼快的后车夹击下，被追尾了，在地大物博的大西北等了80公里开外的交警来处理，折腾到深夜才到酒店。好在车还能正常行驶，并完成了本次所有车程！</p>
<h3 id="5月1日-集合"><a href="#5月1日-集合" class="headerlink" title="5月1日 集合"></a>5月1日 集合</h3><p>游玩：张掖七彩丹霞<br>住：张掖<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map5-1.png" alt="5月1日行程图"><br>早上六点半的飞机赶往西宁，虽然晚点半小时，但是准点到达了！中午十二点半坐高铁从西宁直接到张掖，和四位提前三天到的小伙伴汇合，一起驾车前往张掖~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/start.jpg" alt=""></p>
<h4 id="张掖七彩丹霞"><a href="#张掖七彩丹霞" class="headerlink" title="张掖七彩丹霞"></a>张掖七彩丹霞</h4><p>五点左右到达张掖七彩丹霞，比较幸运天气不错，丹霞很多彩~七彩丹霞不虚此行。还有热气球可以坐，但是我们没有坐(¬‿¬)。下来的时候还看见有古装小姐姐在跳舞。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/zhangye.jpg" alt="张掖七彩丹霞"></p>
<h3 id="5月2日"><a href="#5月2日" class="headerlink" title="5月2日"></a>5月2日</h3><p>游玩：嘉峪关、又见敦煌<br>住：敦煌，太阳温泉酒店<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map5-2.png" alt="5月2日行程图"></p>
<p>每日出发都是范导的高光时刻，范导把四个大行李箱稳稳的绑在车顶是每日出发的重要工作，也是罗罗和虫虫的重要辅助工作。当然我们都是全员参与滴！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/huhu.jpg" alt=""></p>
<h4 id="嘉峪关"><a href="#嘉峪关" class="headerlink" title="嘉峪关"></a>嘉峪关</h4><p>这天路程比较远，路上一直纠结是不是要去嘉峪关，后来还是去了，车停不到景区，只能停临时停车场，然后坐接驳车前往。嘉峪关的原始城墙已经很少了，目前看到的嘉峪关是斥资6亿后来修建的，个人感觉比较一般。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/jiayuguan.jpg" alt="嘉峪关"></p>
<p>中午找了一家性价比不错的烤肉店，猜猜是谁在烤肉？？？<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/rourou.jpg" alt=""></p>
<h4 id="又见敦煌"><a href="#又见敦煌" class="headerlink" title="又见敦煌"></a>又见敦煌</h4><p>因为有嘉峪关的行程，所以又见敦煌的演出买的是晚上十点的票，入住酒店后直接可以步行到又见敦煌的演出场馆。演出形式是有一个小时边走边看，后半个小时是坐着看大型舞台剧，感觉还是非常不错的。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/dunhuang.jpg" alt=""></p>
<p>阳阳小姐姐选的酒店非常nice，离又见敦煌和莫高窟数字文化中心都很近。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/jiudian1.jpg" alt="太阳温泉酒店"></p>
<h3 id="5月3日"><a href="#5月3日" class="headerlink" title="5月3日"></a>5月3日</h3><p>游玩：莫高窟、鸣沙山月牙泉、沙洲夜市<br>住：敦煌，太阳温泉酒店<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map5-3.png" alt="5月3日行程图"></p>
<h4 id="莫高窟"><a href="#莫高窟" class="headerlink" title="莫高窟"></a>莫高窟</h4><p>第二天不需要收拾行李，所以时间相对充裕，而且住在莫高窟数字文化中心附近，去莫高窟之前会在数字文化中心看一段敦煌相关影片后才坐十分钟的接泊车去莫高窟。每位游客只能看八个窟，不过我们额外看了一个预备窟，嘻嘻┌(˘⌣˘)ʃ…  一行人配有一个讲解员。窟内不能喝水、拍摄、需要戴口罩，因为窟内不能消毒，拍摄也损坏壁画，不少窟的壁画已经脱落，颜色变黑，所以有些人像是黑皮肤。讲解员小姐姐还是比较专业的~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/mogaoku.jpg" alt="莫高窟"></p>
<h4 id="鸣沙山月牙泉"><a href="#鸣沙山月牙泉" class="headerlink" title="鸣沙山月牙泉"></a>鸣沙山月牙泉</h4><p>从莫高窟出来，回到酒店修整了一下，吃过午饭后就前往鸣沙山月牙泉，五点左右抵达月牙泉，光线不是很好，月牙泉确实长的是月牙形状，去沙漠上面可以选择骑骆驼，或者自己上去，我们选择自己上去，因为骆驼比较慢。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/yueyaquan.jpg" alt=""></p>
<p>上去之后选择了沙地越野车去了更高的沙山。中途男生们还体验了一把自己开沙地越野车。上面人比较少，视野也不错，中途我家虫虫还丢了戒指在沙漠，幸运的是被我淘了几把沙子捡回来了。后面各自拍照合影玩到七点半才离开，遗憾的是没有体验骆驼，滑翔机，直升机~~~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/mingshashan.jpg" alt="鸣沙山, 看图脑补爱情大戏"></p>
<p>中间发现茫崖的酒店定错了，定成了西宁的同名酒店…赶紧临时定了另一家。<br>中途我们一直心心念念要吃瓜州的瓜，瓜州的瓜以瓜美瓜甜成名。结果经过瓜州停车区看到很多瓜摊，很兴奋的错过了！！！笃定下个瓜州服务区会有更多瓜摊，结果到了瓜州服务区只有几只瓜，人家说不是当季水果，但是我们还是买了一个，水分还挺多，就是不太甜 ಥ_ಥ。</p>
<h4 id="沙洲夜市"><a href="#沙洲夜市" class="headerlink" title="沙洲夜市"></a>沙洲夜市</h4><p>后面的行程会经过茫茫的戈壁，很多无人区，所以从鸣沙山月牙泉出来，我们直奔沙洲夜市买了一些后面两天路上的零食，干粮，水果，主要是后面两天中午吃的。顺便我们也在夜市的食驿吃了各种小吃，种类很齐全，味道很不错，吃的心满意足。顺道买了一个西瓜，两个蜜瓜。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/yeshi.jpg" alt=""></p>
<h3 id="5月4日"><a href="#5月4日" class="headerlink" title="5月4日"></a>5月4日</h3><p>游玩：阿克塞博罗转井影视基地(九层妖塔取景地)，大苏干湖<br>住：茫崖市花土沟镇，诚成宾馆<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map5-4.png" alt="5月4日行程图"></p>
<p>前往茫崖的路要经过很长很长的无人区，也有很多大货车，所以行车速度不能太快，所以路上花费了七个多小时。午饭只能在路边解决。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/wufan.jpg" alt=""></p>
<h4 id="阿克塞博罗转井影视基地"><a href="#阿克塞博罗转井影视基地" class="headerlink" title="阿克塞博罗转井影视基地"></a>阿克塞博罗转井影视基地</h4><p>其实就是一个废弃的村落，九层妖塔在这里取过景，我们就没有进去。不过也在外面进行了无人机首飞，试飞成功！！！</p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/1620101006458.MOV"><br></video>

<h4 id="大苏干湖"><a href="#大苏干湖" class="headerlink" title="大苏干湖"></a>大苏干湖</h4><p>大苏干湖其实不是一个景点，是一个鱼雷基地，处于关闭状态，不能进入，但是景色不错，还有条公路单独通往。我们就在大苏干湖进行了航拍和公路照。到了海西温度就变得很低，所以穿上了羽绒服。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/suganhu.jpg" alt="大苏干湖"></p>
<p>去茫崖的路上大货车非常多，对路线不熟，所以范导要求我们不要出声，我们小声交谈，后面换了罗罗开车，我们愉快的抵达了茫崖~花土沟镇，路上碰到很像脑花的地貌。哈哈~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/naohua.jpg" alt=""></p>
<p>花土沟镇是一个油田小镇，有很多「叩头机」在不知疲倦的工作。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/koutou.jpg" alt=""></p>
<h3 id="5月5日"><a href="#5月5日" class="headerlink" title="5月5日"></a>5月5日</h3><p>游玩：茫崖翡翠湖、西台吉乃尔湖、水水雅丹、水上雅丹落日<br>住：水上雅丹 乌素特水上雅丹酒店<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map5-5.png" alt="5月5日行程图"></p>
<h4 id="尕斯库勒湖"><a href="#尕斯库勒湖" class="headerlink" title="尕斯库勒湖"></a>尕斯库勒湖</h4><p>在地图上看到尕斯库勒湖，就想着过去航拍，一早出发过去，走了一段土路，给运油大货车行走的路，实在是不好走，不过我们的越野车性能不错，虽然有点颠簸，还是挺有feel的。走到距离五公里的地方发现有围栏挡着，附近有机场还是个禁飞区。无奈只能跟着大货车回到主路。中间范村委说去考察路线，秒回，秒了快半个小时才回来，要不是时间因素，我们很有可能按范村委在信号不好的情况下探到的路去逛逛<br>去下一站…<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/genche.jpg" alt=""></p>
<h4 id="茫崖翡翠湖"><a href="#茫崖翡翠湖" class="headerlink" title="茫崖翡翠湖"></a>茫崖翡翠湖</h4><p>无门票 只需要停车费10元</p>
<p>下一站是本次行程的一个重点路线–茫崖翡翠湖，行程比常规的青甘环线多走了两百多公里，上午十点左右抵达茫崖翡翠湖。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/mangya-logo.png" alt=""></p>
<p>工业废水孕育出来的自然景观也能如此美丽震撼<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/mangya-2.jpg" alt="航拍"><br>花了20块钱躺在透明小船上美美的拍了一通，船长大爷还教你摆 Pose，也是非常专业了~~ 哈哈，超值体验。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/mangya-1.jpg" alt=""></p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/mangya.MP4"><br></video>

<p>离开茫崖翡翠湖前往下一站途中感受到了大西北的荒凉，一望无际，沧海桑田。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/gebi.JPG" alt=""></p>
<p>由于是走回头路，对路线比较熟悉，范导这次开车心情愉快，稳中求快…还分享了他的歌单，我们纷纷表示对他品味表示肯定！</p>
<h4 id="西台吉乃尔湖"><a href="#西台吉乃尔湖" class="headerlink" title="西台吉乃尔湖"></a>西台吉乃尔湖</h4><p>前往水上雅丹的行程中途经西台吉乃尔湖，属于柴达木盆地，占地126平方公里。西台吉乃尔湖又叫鸳鸯湖，因为有一条很长的道路直接在湖面上，途中找了个停车点下车游玩了下，深蓝色流淌的湖水配上蓝蓝的天空，光影交错。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/xitai.JPG" alt=""></p>
<h4 id="水水雅丹"><a href="#水水雅丹" class="headerlink" title="水水雅丹"></a>水水雅丹</h4><p>从西台吉乃尔湖到水上雅丹的途中，大小一片延绵的雅丹地貌，地势比较平坦，开车进去无压力，五六点的天气非常的好，天非常蓝，于是临时起意开车进入，尽情游览拍摄。还航拍了心心念念的跟车航拍视频…<br>不过雅丹信号很弱，大多数地方都是无，所以跟车航拍的司机靠人肉喊，当然喊不回来，囧!</p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/FinalVideo_1620700664.641729.MOV"><br></video><br>是大片的感觉~~<br><video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/FinalVideo_1620225030.469762.mov"><br></video>

<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/shuishuiyadan.JPG" alt=""></p>
<h4 id="水上雅丹日落"><a href="#水上雅丹日落" class="headerlink" title="水上雅丹日落"></a>水上雅丹日落</h4><p>抵达水上雅丹，入住后正好晚上八点左右，爬上小山坡欣赏日落…<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/yadanluori.jpg" alt=""></p>
<h3 id="5月6日"><a href="#5月6日" class="headerlink" title="5月6日"></a>5月6日</h3><p>游玩：乌素特水上雅丹，东台吉乃尔湖，最美公路，大柴旦翡翠湖<br>住：海西大柴旦镇，维也纳酒店<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map5-6.png" alt=" 5月6日行程图"><br>水上雅丹地貌，相比陆地雅丹地貌视觉上还是有很大不同，碧水蓝天下的雅丹地貌别具一番风味<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/shuishangyadan.jpg" alt=""><br>航拍发现一个〔碧绿之眼〕，哈哈(♥ω♥ ) ~♪，开心<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/eye.JPG" alt=""></p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/1620264092073.MOV"><br></video>


<h4 id="东台吉乃尔湖"><a href="#东台吉乃尔湖" class="headerlink" title="东台吉乃尔湖"></a>东台吉乃尔湖</h4><p>东台吉乃尔湖和西台吉乃尔湖原来本来是一个湖，后来水位下降被分割成了两个湖，相距35公里。<br>和西台吉乃尔完全不一样，东台吉乃尔湖是一个白色盐碱地和碧绿湖水组成的素雅湖泊，有一大片白色的盐地，湖水淡淡的颜色，十分雅致。光线很强，一定要戴墨镜！！！不然有去茫茫雪地配超强阳光的深刻体会。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/dongtai2.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/dongtai1.jpg" alt=""></p>
<h4 id="小柴旦湖"><a href="#小柴旦湖" class="headerlink" title="小柴旦湖"></a>小柴旦湖</h4><p>路程中会路过小柴旦湖，但是看攻略说比较难找到路，所以决定不去。但是去大柴旦湖本身就会路过小柴旦湖，路过的时候发现小柴旦湖还是挺美的，远处雪山，岸上黄草，深绿的湖水。等我们发现湖边有个白色房子有个小路口可以进入后已经错过了路口，由于是高速上已经无法回头。只能在车上拍几张照片啦~  还看到湖面有一个湖中桥，遗憾没能停下去看！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/xiaochaidan2.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/xiaochaidan1.JPG" alt=""></p>
<h4 id="最美公路"><a href="#最美公路" class="headerlink" title="最美公路"></a>最美公路</h4><p>315最美公路，有几段大下坡上坡，极具视觉效果，下车拍摄了一张公路照…但是不能逗留太久，还是比较危险的！！网红们倒是很拼，我们就遇到一个帅锅马路上脱了衣服光着上身拍照或者直播？<br>提醒这个行为很危险哦，大货车下坡不减速的！！！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/gonglu.JPG" alt=""></p>
<h4 id="大柴旦翡翠湖"><a href="#大柴旦翡翠湖" class="headerlink" title="大柴旦翡翠湖"></a>大柴旦翡翠湖</h4><p>住 大柴旦镇 维也纳酒店<br>背靠雪山的大柴旦湖，湖水是深绿的翡翠颜色，是天然湖泊，占地面积非常大，道路分割出了很多大大小小的湖。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/dachaidan1.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/dachaidan2.JPG" alt=""></p>
<p>可惜的是翡翠之眼没开放。太累了也没有留下来看夕阳，迎着夕阳前往酒店了。车上看夕阳也很美！</p>
<h3 id="5月7日"><a href="#5月7日" class="headerlink" title="5月7日"></a>5月7日</h3><p>游玩：茶卡盐湖、草原星空<br>住：青海湖，大美草原度假牧场<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map5-7.png" alt="5月7日行程图"></p>
<h4 id="茶卡盐湖"><a href="#茶卡盐湖" class="headerlink" title="茶卡盐湖"></a>茶卡盐湖</h4><p>本次行程名气最大的景点，大家的兴致都很高，驱车近五小时到了茶卡盐湖。买票坐接驳车再坐小火车到终点居然用快两小时。穿靴套进入湖中体验感觉有些名不副实，只有一块比较大的湖，其他都是小水洼，远处是雪山…不过人物出片还是不错的。航拍也挺有趣~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/chaka.JPG" alt=""></p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/1620382119106.MOV"><br></video>

<p>从茶卡盐湖出来快八点了，还要驱车两小时到青海湖住宿，路上粉色晚霞挂在雪山上，夜色下星星很多。星空下赶路也别具一番风味…</p>
<p>酒店门口星空拍摄图, 因为酒店外有个很亮的大灯，在酒店阴影下看起来星空被一分为二。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/xingkong.jpg" alt=""></p>
<p>酒店还不错哦，里面很温暖<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/damei.JPG" alt=""></p>
<p>可惜没有定闹钟，没看到日出~( •̥́ ˍ •̀ू )<br>小伙伴去看过日出，觉得可能高山上带云彩应该更加震撼</p>
<h3 id="5月8日"><a href="#5月8日" class="headerlink" title="5月8日"></a>5月8日</h3><p>游玩：青海湖<br>住：西宁<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map5-8.png" alt="5月8日行程图"></p>
<h4 id="青海湖二郎剑"><a href="#青海湖二郎剑" class="headerlink" title="青海湖二郎剑"></a>青海湖二郎剑</h4><p>青海湖环湖有很多『野景点』，是当地牧民自己圈的景点，可以停车，会收一点钱。我们看中了一个湖边景点，要每人10块，但是范导力挽狂澜讲到了10块钱6个人，真猛士也！！<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/10yuan.JPG" alt=" 湖边休闲时光"></p>
<p>因为其他小伙伴之前三天去过青海湖二郎剑，所以只有我们俩去了一下二郎剑游玩了一小时，坐了十分钟的快艇。湖水颜色是深蓝色很干净，二郎剑是本次行程的最后一个景点。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/erlangjian2.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/erlangjian1.jpg" alt=""></p>
<p>乘坐快艇，可见远处雪山<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/kuaiting.jpg" alt=""></p>
<h4 id="西宁-水井巷"><a href="#西宁-水井巷" class="headerlink" title="西宁-水井巷"></a>西宁-水井巷</h4><p>下午四点到了西宁去水井巷，似乎水井巷有改建，体验没有敦煌夜市好。不过遇到个还不错的阿姨（阿姨顺便去做个脸，👍🏻👍🏻👍🏻），带我们去了附近超市，买了一堆牛肉干，蛮好。话说，牛肉干真的蛮贵。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/niurou.JPG" alt=""></p>
<h3 id="回程"><a href="#回程" class="headerlink" title="回程"></a>回程</h3><p>酒店早餐体验很棒，吃完早餐，出发乘坐11点的飞机回上海~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/zaocan.JPG" alt=""></p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>最好提前一个月定机票酒店，规划路线，确认路线和酒店，定自驾车。温度在 0 ~ 30 度之间，所以四季的衣服都要准备哦~ 防晒、墨镜不能少，不然黑三个度。天气非常干，面膜得贴起来！<br>费用合计：15429 （2人）<br>机票：去程 2820元  回程 1120元<br>酒店：3598元<br>吃饭：2210元<br>油费：1425元<br>门票：1000元<br>租车：2556元<br>特产：400元<br>其他零食：300元</p>
]]></content>
    <summary type="html">
    <![CDATA[<h3 id="行程介绍"><a href="#行程介绍" class="headerlink" title="行程介绍"></a>行程介绍</h3><h4 id="行程日期"><a href="#行程日期" class="headerlink" title="行程日期"></a>行程日期</h4><p>前序部队：2021/04/28 - 2021/05/09<br>集合部队：2021/05/01 - 2021/05/09</p>
<h4 id="行程路线"><a href="#行程路线" class="headerlink" title="行程路线"></a>行程路线</h4><p>西宁-&gt;张掖-&gt;敦煌-&gt;鸣沙山-&gt;苏干湖-&gt;茫崖翡翠湖-&gt;西台吉乃尔湖-&gt;水上雅丹-&gt;东台吉乃尔湖-&gt;大柴旦翡翠湖-&gt;茶卡盐湖-&gt;青海湖-&gt;西宁<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e9%9d%92%e7%94%98%e5%a4%a7%e7%8e%af%e7%ba%bf/map.png" alt=""></p>
<h4 id="行程亮点"><a href="#行程亮点" class="headerlink" title="行程亮点"></a>行程亮点</h4><p>翡翠湖、雅丹地貌、大戈壁、青海湖、敦煌、美食</p>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[彩云之南]]></title>
    <link href="http://yoursite.com/2020/10/21/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/"/>
    <id>http://yoursite.com/2020/10/21/彩云之南/</id>
    <published>2020-10-20T21:48:09.000Z</published>
    <updated>2022-02-27T03:21:57.149Z</updated>
    <content type="html"><![CDATA[<p>行程日期：2020/09/28 - 2020/10/08<br>行程路线：杭州-&gt;丽江-&gt;香格里拉-&gt;雨崩-&gt;香格里拉-&gt;泸沽湖-&gt;丽江-&gt;杭州<br>行程亮点：美景、美食、高海拔徒步<br>人物：fufu、英英、阳阳、罗罗、虫虫、水水</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/xincheng.png" alt=""><br><a id="more"></a></p>
<h3 id="前序"><a href="#前序" class="headerlink" title="前序"></a>前序</h3><p>出发前小伙伴们的游玩 - 英英回忆编写</p>
<p>在丽江机场，英英和阳阳姐姐第一次见面。罗罗还带了一个28寸的行李箱。之后就被淘汰了。刚见面英英还觉得有一点拘谨，没想到后面会很喜欢阳阳和罗罗。</p>
<p>四人先在机场附近住了一晚，收拾好之后出来找吃的，小旅馆没有吃的，四个人沿着马路走了一会，略微对比了一下，找到了个小店，开始了云南舌尖上的旅行。云南的菜味道都很浓郁，连青菜似乎都比上海的香，第一晚虽然是路边摊，吃起来也很香，还发现了一种叫酸枣汁的当地饮料和叫水性杨花的青菜。</p>
<p>第二天穿过雾气缠绕着蜿蜒的马路，我们到了沿途第一个让英英印象深刻的景区—-泸沽湖。<br>先吃午饭，叫柴火鸡，味道也很好，还贴着锅边烤着饼，就是我觉得略贵。<br>入住之前去了走婚桥，英英已经没有什么印象了，略过。</p>
<p>直接入住泸沽湖上的名宿，这个住宿是阳阳订的，一开窗帘，就是湖景，出门沿着石头街道，走几步就到了街区，阳阳姐姐这个住宿订的很赞呀。据说过几年这边的名宿局不让开了，3，4公里开外也的确在大兴土木中。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/湖景名宿.jpg" alt=""></p>
<p>泸沽湖的第二天，800包了一个人力船带着我们游湖。湖光山色，波光粼粼，水波荡漾中在广阔的湖上游船，开阔的视野和清澈冷冽的湖水，一扫大城市待久了的憋闷感。英英本来想试着划船，略微尝试了一下就放弃了，不好意思大费周章。就这样一边游船一边发呆拍照也很好。阳光真的很明媚，所以阳阳姐姐的手背就在行程开始的第二天就被晒黑了~~ 船夫是一堆夫妻吧，带着我们上了一个小岛，岛上似乎有一个网红旅馆还是餐厅，但是很贵，我们就没有选择住在这里。阳阳姐姐订的名宿就很不错。摇船回程，船夫专门带我们去看了一下浅水区开在水里的花，白色小小的花在湖水中摇曳，自在随意的样子，原来就是我们饭桌上的水性杨花。哈哈哈。它的名字就叫水性杨花，有点小惊奇。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/泸沽湖游船.jpg" alt=""></p>
<p>游湖结束，我们在泸沽湖附近转了两圈，这里草木茂盛，还有一个高台，登上高台的过程发现蛮多石头勒出来的祈福的石头山，当地人用来祈福的石头堆，有一些造型还蛮巧妙。和这边的塔遥相呼应，给云南这边的少数民族增添了一些神秘色彩。然后在附近还发现了山凹凹里的人家，有水有花有草有树，一副田园景色，也真的有人在这里写生。英英专门穿着红色的长裙来拍照，果然很出片。还勾起了阳阳姐姐买红色裙子的兴趣。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/红色裙子？.jpg" alt=""></p>
<h3 id="出发汇合-9-28"><a href="#出发汇合-9-28" class="headerlink" title="出发汇合 9-28"></a>出发汇合 9-28</h3><p>前序提到有四个小伙伴是25号就出发了，他们先去泸沽湖玩了几天，28号约定在丽江汇合。<br>我们先到了丽江古城的客栈，客栈还是不错的，由于云南瑞丽之前爆出疫情，云南的游客少了很多，客栈性价比还是很不错的。<br>出入古城刷身份证后就可以人脸识别进出了。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang1.jpg" alt=""><br>中午到了丽江,丽江古城街道上花草很多，找了个客栈吃饭，味道还不错。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang2.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang4.JPG" alt=""></p>
<p>下午在万古楼喝喝咖啡、饮料（不好喝，还贵）看古城的全貌。是个惬意的下午了，心情 up up ！！</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang3.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang5.JPG" alt=""></p>
<p>傍晚小伙伴从拉市海回来，在客栈集合，彩云之南小队正式集结。✌🏻<br>晚饭找了一家腊排骨（我一直以为是辣排骨^_^）火锅美美的吃了一顿。</p>
<h3 id="虎跳峡-9-29"><a href="#虎跳峡-9-29" class="headerlink" title="虎跳峡 9-29"></a>虎跳峡 9-29</h3><p>大部队汇合后，后面的行程是包了一个车和一位跟车司机，不用自己开车，行程还是比较自由和轻松滴。</p>
<p>行程的第一个景点虎跳峡，定位是边徒步边游玩，9点从丽江出发去虎跳峡，一路上慢悠悠走，路过了一个观景台（玉龙山？？），上去看了看。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yulongshan.JPG" alt=""></p>
<p>到虎跳峡已经快中午12点了，虎跳峡还是挺震撼的，近观江水声势浩大，水声哗啦啦震天响。第一个景点，当然要拍照记录啦！带上了中秋节前发的盲盒不倒翁（吉祥物，哈哈）。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/hutiaoxia1.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/hutiaoxia.JPG" alt=""></p>
<p>在虎跳峡吃了简单的自热饭午餐后，背上背包开始徒步。我们计划沿虎跳峡徒步到一个地方（不记得了）。兜兜转转路过一个小村子，还想去摘人家的梨，但是有个看起来很凶的狗子守着，就算了。路上好多大货车路过，我们吐槽不像徒步路线呀，无奈，徒步策划人huhu就一个人前去探路，发现可能走下去要么迷路，要么晚上到不了香格里拉，所以原路徒步返回公路，中间huhu在狗子的狂吠声中为英英捡了几个看起来刚掉下来的梨，本来还在想要不要留下一些钱，但是遇到了可能是主人的阿婆，说拿走拿走，哈哈，不虚此行~ 虽然梨好像不是很甜，又有什么关系呢？</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/hutiaoxia2.JPG" alt=""></p>
<p>一路上走走停停，估计也就走了3、4公里吧，实在是搞不清路线，就让师傅来接我们了。接上我们都快下午4点了，出发前往住宿的地方，路上碰到风景不错的地方，忍不住下车休息休息，也坐车2小时了，不过云南6点的时候太阳还高挂呢，感觉不到快晚上了。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/hezhao.jpg" alt=""></p>
<p>8点终于到了香格里拉，找了一个网红餐厅，但是排队两小时，故而放弃，由于游客比较少，空闲的餐馆还是很多的，找了一家看起来不错的餐厅，美美的吃了一顿烤肉火锅。物美价廉，不错不错~ 大快朵颐！</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/day1.jpg" alt=""></p>
<p>入住的民宿老板人很热情，很热情的介绍可以游玩的地方，接下来的两天，我们都会住在这个民宿。因为疫情，又提前了几天到，游客比较少，不拥挤。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/minsu1.jpg" alt=""></p>
<h3 id="香格里拉-9-30"><a href="#香格里拉-9-30" class="headerlink" title="香格里拉 9-30"></a>香格里拉 9-30</h3><p>30号一早，我们在民宿附近找了一家早点店，吃了一碗热腾腾的粉，阳阳姐姐和英英买了几个青稞饼，可以充当这天游玩的干粮。我们买好小吃、零食、水果、自热饭、自热火锅，整装待发！虽然在高海拔地区，但是热情不减。</p>
<h4 id="普达措公园"><a href="#普达措公园" class="headerlink" title="普达措公园"></a>普达措公园</h4><p>早上九点从香格里拉市区出发，到达普达措公园是10点半。<br>为响应国家生态保护计划，普达措公园只开放部分景区，比较可惜不能游玩完整的普达措公园，花了80元买了景区电瓶车到达第一个下车点，开始徒步前往属都湖（碧塔海在维修不开放）景点。沿途风景很美，原始森林类型的自然风光令人心旷神怡。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo1.jpg" alt=""></p>
<p>新手摄影师和萌新摄影师在交流拍照心得，整的挺专业的样子，一看，你俩就拍了个小蘑菇？？？</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo3.JPG" alt=""></p>
<p>为了这次旅行节前减肥，特意尝试做了个美甲，当然要晒一下啦.</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo2.jpg" alt=""></p>
<p>中间徒步上了个台阶，突然心跳加速，有点喘了，哎嘛，兴奋过头了，毕竟3500多的海拔，还是要悠着点。中午的时候，找了个凉亭，吃了一顿自热饭解决了午餐。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo4.png" alt=""></p>
<p>午餐过后，又开始徒步，走走停停，不太远的路程走了2个小时，只因为风景太美。让人流连。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo5.JPG" alt=""></p>
<h4 id="普达措公园-属都湖"><a href="#普达措公园-属都湖" class="headerlink" title="普达措公园 - 属都湖"></a>普达措公园 - 属都湖</h4><p>到了属都湖，眼前一亮，美就一个字，所有的疲劳一扫而空，不虚此行矣。可惜此行普达措公园其他景点在维修，不能一览全貌，也不能去体验森林采蘑菇。<br>我们在湖边游玩，拍照记录，感受大自然的美。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo13.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo6.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo10.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo7.jpg" alt=""></p>
<p>网红天空之镜的拍照服务当然是要体验啦，就是这阳光明媚+镜面快把人烤糊了。不过摄影师技术不咋地，放大一看，曝光太多了，都有白斑了，以后去可得放大放大再放大的检查一遍。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo8.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo11.jpg" alt=""></p>
<p>让自家的摄影师蹭下湖边的摆拍造型设备，结果，是真糊了，无所谓，也高兴。</p>
<p>在huhu导游的催促下，4点了我们才开始了属都湖的环湖徒步，走到环保车站后，就不能往前走了，我们就开始往回走。司机师傅也一直催我们开始回去了，我们才开始回。回到香格里拉，简单吃了个晚饭，逛了逛夜晚的街道，回到民宿，晚上玩儿了一晚 switch 游戏，尽兴的一天。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/pudacuo12.JPG" alt=""></p>
<h3 id="香格里拉-雨崩-10-1"><a href="#香格里拉-雨崩-10-1" class="headerlink" title="香格里拉 ~ 雨崩 10-1"></a>香格里拉 ~ 雨崩 10-1</h3><h4 id="金沙江大弯"><a href="#金沙江大弯" class="headerlink" title="金沙江大弯"></a>金沙江大弯</h4><p>次日，正好收国庆和中秋节，我们这天从香格里拉前往雨崩村，因为路程比较长，所以8点前就得出发了。大概十点，到达金沙江大弯，正好下车在观景台逗留了一会，歇歇脚。高海拔的自然风光有种独特的大气、坚韧的美感。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/jingshajiang.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/jingshajiang2.jpg" alt=""></p>
<h4 id="白马雪山"><a href="#白马雪山" class="headerlink" title="白马雪山"></a>白马雪山</h4><p>看完金沙江大湾，旅程继续，因为海拔持续在升高，坐在车里大家都有些不舒服，十一点半到达白马雪山，这天云比较重，没看到雪山，这里更多的是一个休憩区，有个界碑。我们在这边下车放松了一会，huhu导游还跑到了一座山顶山，来了场海拔4292米的高原徒步？</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/baimaxueshan.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/baimaxueshan1.jpg" alt=""></p>
<h4 id="飞来寺"><a href="#飞来寺" class="headerlink" title="飞来寺"></a>飞来寺</h4><p>离开白马雪山，中午的时候来到了飞马寺，到的时候云层比较厚，看不到对面的梅里雪山，所以我们先去附近简单的吃了一顿午餐，味道一般，略贵。饭后再次来到飞来寺的观景台，这个时候看到梅里雪山从云层中若隐若现。从观景台的望远镜上可以看到梅里雪山的一些细节，貌似看到了冰川~ 嘿嘿~ 听说有个摄影师在这里住了一个月想要看日照金山，但是最近一个月都是多云，所以没有成功拍到。只能遗憾离去，所以有的时候并不能心想事成，开心就好。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/feilaisi1.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/feilaisi.jpg" alt=""></p>
<h4 id="到达雨崩"><a href="#到达雨崩" class="headerlink" title="到达雨崩"></a>到达雨崩</h4><p>离开飞来寺后我们开始前往雨崩村，一路上海拔开始下降，还有一段路是穿过云层，很难看清路，司机师傅还是有些紧张，提醒我们不要睡着，穿过云雾遇到了一个小堵车，原来是有个大货车有些问题？我们以为要等到车修好才能走，结果我们堵了十几分钟后，发现小车是可以从旁边走的，哈哈，运气不错。<br>去雨崩村需要在中途换进村的吉普车，我们收拾好在雨崩两天所需的物资装到徒步包，剩下的行李放在车上。吉普车开了大概四五十分钟，一路上是真的很颠簸，只能扶着吉普车。三点左右，我们到了民宿，我们定的名宿在下雨崩，民宿还放了一些天空之镜的网红装备。<br>在雨崩村，能够更近的看到梅里雪山。而且有几条很有名的徒步路线，所以这些年雨崩的游客还是很多的，基本上是过来徒步的。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng3.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng.jpg" alt=""></p>
<p>整顿好了之后，开始准备过中秋啦 ，我们本来想在一楼大厅边吃晚饭边看节目，但是民宿主人说他们自己要用，所以我们在把晚餐（自热饭、自热火锅、方便面、月饼）放在了自己房间，结果发现买的一堆一次性碗并没有带过来，emo了~ 不过中秋节吃方便面也是新奇的成就了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng4.jpg" alt=""></p>
<p>晚餐过后，有一个团队在楼下唱歌跳舞，我还被拉过去…帮忙录视频！有意思。<br>看他们玩的很嗨，我们也开始在房间玩起了switch的跳舞游戏，没想到虫虫天赋可以，勇夺第一，玩太嗨了！晚上月亮很亮，还拍了一张星空照~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng2.JPG" alt=""></p>
<h3 id="雨崩-10-2"><a href="#雨崩-10-2" class="headerlink" title="雨崩 10-2"></a>雨崩 10-2</h3><p>清晨的雨崩村，炊烟袅袅，很有些意境，感受到岁月静好了，深呼吸，呼~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng6.jpg" alt=""></p>
<p>此行一个最大的体验，就是来雨崩体验高原徒步，就是今天啦，激动！！ 早餐是在名宿吃了一碗稀饭+鸡蛋，还不错，很实惠。早上7点我们整装待发，在名宿门口碰到同样准备去徒步的歪果仁在喝着咖啡，感概他们身体素质真好，都是小短裤穿着。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng5.png" alt=""></p>
<h4 id="神瀑徒步"><a href="#神瀑徒步" class="headerlink" title="神瀑徒步"></a>神瀑徒步</h4><p>我们选择了比较安全轻松的一个路线 「神瀑」，雨崩还有一个比较知名的路线-冰壶，相对比较危险，就不尝试了。<br>前期的路程还是比较轻松的，都是平坦的道路，我们沿着梅里雪山方向走，我还背了两个氧气罐，一路叮当作响，像头小毛驴。<br>雨崩的环境比较潮湿，土地很肥沃，树木很茂盛，随处可见红伞伞、白杆杆~ 别说，这些菌菇小模样长的真好看，很诱人。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng8.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng7.jpg" alt=""></p>
<p>到达神瀑脚下，大概徒步了4、5公里的样子，已经快中午了，找了路人小哥帮我们拍了一个纪念合照，拍完我们有些饿了，就在路上吃了一顿简单的午饭，没错，又又又是自热饭！</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng9.JPG" alt=""></p>
<p>要到达神瀑，需要王山上走两三公里，对于高海拔徒步来说，这有点累了，所以我们一鼓作气，中间没有多做停留，互相大气，走到了神瀑。神瀑附近很冷，据说来神瀑祈福的人会绕行三圈，所以我们一行人也纷纷加入绕行祈福。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng10.JPG" alt=""></p>
<p>简单的补充了一下能量，我们开始下山，原路返回。返回的路程比较轻松，我们一路走走停停，两个氧气罐哐哐响，不过我们一路没有吸氧，赞一个。路上我们堆了几个玛尼堆。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng12.jpg" alt=""></p>
<p>路上看到一家很美的名宿，正好我们太累了，又要等掉队的huhu和英英，所以我们在名宿点了一壶热茶，在二楼喝茶、休息，徒步完躺着真惬意。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng11.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/yubeng13.jpg" alt=""></p>
<p>等到huhu和英英回来都半小时后了，我们正好休息完，回客栈去咯。晚餐依然是自热饭、自热火锅、方便面。实在是背了太多进村了。</p>
<h3 id="雨崩徒步-回到香格里拉-10-3"><a href="#雨崩徒步-回到香格里拉-10-3" class="headerlink" title="雨崩徒步 ~ 回到香格里拉 10-3"></a>雨崩徒步 ~ 回到香格里拉 10-3</h3><p>第二天一大早，我们看着剩余的十几份方便面、自热饭、自热火锅陷入可沉思。想到后面还要吃掉，心情就不美丽了。我们就合计了一下，发现大家都不想再吃了，剩下的想跟名宿老板换早餐，但是老板好像不太想换。于是我们就去找杂货铺的老板换了十几瓶矿泉水。</p>
<h4 id="尼龙大峡谷"><a href="#尼龙大峡谷" class="headerlink" title="尼龙大峡谷"></a>尼龙大峡谷</h4><p>出村的方式我们选择从尼龙大峡谷徒步下山。话不多说，背上背包，走起来。大部分的路比较平坦好走，路上看到了比两只手还大的蘑菇。还有一些大大小小的蘑菇，这边真的是有很多蘑菇。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/nilong.jpg" alt=""></p>
<p>后面有段路还比较崎岖，速度慢了一些些。经过一个村庄，还挺美。路上也碰到很多一起徒步下山的，还碰到一个大概十岁的小朋友，自己背着自己的东西和他父亲一起徒步下山，还是挺厉害的。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/nilong2.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/nilong3.jpg" alt=""></p>
<p>下山大概徒步了四五个小时，联系到了我们的司机师傅，接上了疲惫的我们~</p>
<h4 id="香格里拉夜市"><a href="#香格里拉夜市" class="headerlink" title="香格里拉夜市"></a>香格里拉夜市</h4><p>回到香格里拉，我们美美的吃了一顿，对比方便面，自热饭，真的太美味了。看了几天的小蘑菇，终于可以吃了！</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/xianggelila2.JPG" alt=""></p>
<p>吃完我们逛了下香格里拉夜市，夜市也是美美的~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/xianggelila.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/xianggelila4.jpg" alt=""></p>
<p>这次的住宿没有在上次的名宿住，是找了一个藏族特色的酒店住-桑珠别院，装修的很有特色，房间也很大，地暖开的很足，很暖和。能够美美的休息了一晚了~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/xianggelila3.JPG" alt=""></p>
<h3 id="香格里拉-10-4"><a href="#香格里拉-10-4" class="headerlink" title="香格里拉 10-4"></a>香格里拉 10-4</h3><p>一早起来吃了一顿丰盛的早餐、味道还是挺好的哦~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/xianggelila5.jpg" alt=""></p>
<h4 id="松赞林寺"><a href="#松赞林寺" class="headerlink" title="松赞林寺"></a>松赞林寺</h4><p>今天的第一站，松赞林寺，当然要来一些开光加持的纪念品呀~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/songzanlinsi.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/songzanlinsi1.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/songzanlinsi2.jpg" alt=""></p>
<h4 id="纳帕海"><a href="#纳帕海" class="headerlink" title="纳帕海"></a>纳帕海</h4><p>昨天回来的路上经过纳帕海，感觉风景不错，今天特意过来游玩了一下，天气没有昨天好，所以感觉没昨天好看了。不过我们体验了下骑马，前面的妹子骑的马比较烈，跑起来了，带的我的马也跑起来了。把马主人都吓的一直追，但是没追上，好在到了一个马群的聚集地，有人接我们下马，马儿就自己去吃草了。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/napahai.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/napahai1.JPG" alt=""></p>
<p>我们发现了一只营业的羊，挺可爱的，抓着人家狂合照。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/napahai2.jpg" alt=""></p>
<p>回到香格里拉，碰到上海的朋友，之前本来想约他们一起来云南旅游，但是他们说没有计划，但是没想到他们和另外两个朋友一起来了。那来都来了，就一起吃饭啦~  饭后，一起看了香格里拉的旅游节的演出，然后又一起在房间打牌。就像之前一起徒步旅行一样，挺好，哈哈！</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/xianggelila6.jpg" alt=""></p>
<h3 id="丽江-泸沽湖-10-5"><a href="#丽江-泸沽湖-10-5" class="headerlink" title="丽江 - 泸沽湖 10-5"></a>丽江 - 泸沽湖 10-5</h3><p>第二天，我们一行人先到了丽江，后面的行程我们是分开的。他们要去大理，而我们去泸沽湖。<br>中午在丽江吃了特色的菌菇火锅。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang11.jpg" alt=""></p>
<h4 id="泸沽湖"><a href="#泸沽湖" class="headerlink" title="泸沽湖"></a>泸沽湖</h4><p>午饭后，我们一行人分两队，我们另外租了车前往泸沽湖。下午6点多才到民宿-次第花开，因为疫情游客比较少，民宿都不贵，老板还特意给我们换了二楼更大的房间，是独栋的，三百多一天，晚上还会送夜宵，早餐也不错。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu4.png" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu5.png" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu6.png" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu7.png" alt=""></p>
<p>晚上在民宿附近吃了这边的特色菜石锅鱼。</p>
<h3 id="泸沽湖-10-6"><a href="#泸沽湖-10-6" class="headerlink" title="泸沽湖 10-6"></a>泸沽湖 10-6</h3><p>第二天在民宿吃了早餐，就开始了环湖游~</p>
<h4 id="走婚桥"><a href="#走婚桥" class="headerlink" title="走婚桥"></a>走婚桥</h4><p>走婚桥是泸沽湖比较有名的景点之一，湖边有丰富的水草，露出湖面有半人高，走婚桥就建造在这些水草上面。一路行来风景不错.</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu8.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu9.jpg" alt=""></p>
<p>不过走到深处就是一些景点配套的特产商品区域，有很多小朋友在摊位上边做作业边兜售商品。可能他们已经习惯了吧，不知该有什么感想。</p>
<h4 id="游湖"><a href="#游湖" class="headerlink" title="游湖"></a>游湖</h4><p>离开走婚桥我们开始了游湖，这个季节水性杨花已经非常少了，能够依稀看到一些白色的花儿飘在水面上。湖水很清澈，我们中途去了亲爱的客栈拍摄地，结果不让进去，只能在门外看看，没啥意思。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu3.jpg" alt=""></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu10.jpg" alt=""></p>
<h4 id="里格村"><a href="#里格村" class="headerlink" title="里格村"></a>里格村</h4><p>里格村在我们住的湖边的对面，风景比较好，有一个小岛连接，小岛上有一些民宿，从路上看有点像心型呢，之前阳阳姐姐就是住在这边，下次再来可以考虑考虑住这边的湖景房，哈哈。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu11.jpg" alt=""></p>
<p>我们在岛上的名宿喝了个下午茶，准备回去啦~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu1.jpg" alt=""></p>
<p>回到名宿，还早呢，我们沿着湖边散步，很惬意。走到一个装修很美的名宿，楼下可以吃甜点饮料，正好走累了，就点了些甜点，还有一只大熊陪着我们消磨时光到了晚上。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu12.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu13.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu14.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu15.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu2.jpg" alt=""></p>
<p>晚上特意找了一家地锅鸡，有种小时候围着灶台吃饭的情形，就是现炒的菜，有点上火，嘻嘻~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/luguhu16.jpg" alt=""></p>
<h3 id="泸沽湖-丽江-10-7"><a href="#泸沽湖-丽江-10-7" class="headerlink" title="泸沽湖 - 丽江 10-7"></a>泸沽湖 - 丽江 10-7</h3><p>假期快结束了，我们的机票还是从丽江返回杭州，所以要先从泸沽湖回到丽江。中午12点我们抵达丽江，找了一个好偏得名宿，名宿还行，就是要爬好久的阶梯，价格就非常实惠了只要105rmb。下午我们终于有空好好逛逛丽江了。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang9.JPG" alt=""></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang6.jpg" alt=""></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang7.jpg" alt=""></p>
<p>古城好像都差不多。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/lijiang8.jpg" alt=""></p>
<h3 id="回程-10-8"><a href="#回程-10-8" class="headerlink" title="回程 10-8"></a>回程 10-8</h3><p>第二天一早从丽江古城去丽江机场飞回杭州，假期结束~</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>行程日期：2020/09/28 - 2020/10/08<br>行程路线：杭州-&gt;丽江-&gt;香格里拉-&gt;雨崩-&gt;香格里拉-&gt;泸沽湖-&gt;丽江-&gt;杭州<br>行程亮点：美景、美食、高海拔徒步<br>人物：fufu、英英、阳阳、罗罗、虫虫、水水</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%BD%A9%E4%BA%91%E4%B9%8B%E5%8D%97/xincheng.png" alt=""><br></p>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[探究 AutoRelease]]></title>
    <link href="http://yoursite.com/2020/05/04/%E6%8E%A2%E7%A9%B6-AutoRelease/"/>
    <id>http://yoursite.com/2020/05/04/探究-AutoRelease/</id>
    <published>2020-05-03T17:58:38.000Z</published>
    <updated>2021-05-10T05:41:02.946Z</updated>
    <content type="html"><![CDATA[<p>参考文章:</p>
<ul>
<li><a href="http://satanwoo.github.io/2019/07/02/RevisitAutorelease/?nsukey=jw8uyyU1C%2BzqPgSpg5Kie0F9Bj4HNHiPMBkxPWPBuEs1ZyVoZwklMAJVkv0TeJgILqxLQOH2a0Di8DhFj5abLdtFE3p09pb3az4o9B7IY7rvyZHamZN1OIh5zBQZv1J%2FnHLc6QkiMW%2Fo2PY9fVAeVQN%2FQ5lBojKaT%2FXmKQuCTY5E1MoBK4Ir7Qi6un5pXxvKQutSkFhgEVUn%2FboyV6pdxQ%3D%3D" target="_blank" rel="noopener">Revisit iOS Autorelease 之不经意间可能被影响的优化</a></li>
<li><a href="https://draveness.me/autoreleasepool/" target="_blank" rel="noopener">自动释放池的前世今生 —- 深入解析 autoreleasepool</a></li>
<li><a href="http://blog.sunnyxx.com/2014/10/15/behind-autorelease/" target="_blank" rel="noopener">黑幕背后的Autorelease</a></li>
</ul>
]]></content>
    <summary type="html">
    <![CDATA[<p>参考文章:</p>
<ul>
<li><a href="http://satanwoo.github.io/2019/07/02/RevisitAutorelease/?nsukey=jw8uyyU1C%2BzqPgSpg5Kie0F9Bj4HNHiPMBkxPWPBuEs]]>
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[Understanding Crashes and Crash Logs]]></title>
    <link href="http://yoursite.com/2020/04/12/Understanding-Crashes-and-Crash-Logs/"/>
    <id>http://yoursite.com/2020/04/12/Understanding-Crashes-and-Crash-Logs/</id>
    <published>2020-04-12T13:34:05.000Z</published>
    <updated>2023-10-08T07:18:42.628Z</updated>
    <content type="html"><![CDATA[<p>前段时间通过搭建 Crash 平台的机会，知道了如何进行 Crash 的解析和聚类。那么如何去理解一个 Crash 呢？这篇文章是通过 WWDC 中的资料进行整理学习的。</p>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2018/414/" target="_blank" rel="noopener">Understanding Crashes and Crash Logs - WWDC 2018</a> 及相关的视频和资料。</li>
<li>参考文章：<a href="https://medium.com/flawless-app-stories/understanding-crashes-and-crash-logs-fff9781a5df4" target="_blank" rel="noopener">Understanding Crashes and Crash Logs</a></li>
</ul>
<h3 id="崩溃的基本原理"><a href="#崩溃的基本原理" class="headerlink" title="崩溃的基本原理"></a>崩溃的基本原理</h3><h4 id="什么是崩溃"><a href="#什么是崩溃" class="headerlink" title="什么是崩溃"></a>什么是崩溃</h4><p>崩溃当你的 app 试图做一些不被允许的事情导致被突然终止。如：</p>
<ul>
<li>impossible for CPU to execute code : CPU 无法执行某些代码 （除以0）</li>
<li>Operating system is enforcing a policy: 操作系统正在执行某些策略 (为了保证系统流畅性，操作系统kill了 启动时间过长、使用了太多内存的app)</li>
<li>Programming language is preventing failure: 编程语言自身阻止失败并触发崩溃 (数组越界)</li>
<li>Developer is preventing failure: 开发者自己触发（assert）</li>
</ul>
<h4 id="查看崩溃的方式"><a href="#查看崩溃的方式" class="headerlink" title="查看崩溃的方式"></a>查看崩溃的方式</h4><ul>
<li>Crashes Organizer window</li>
<li>Devices window</li>
<li>Automated testing (Xcode、 Xcode Server、 Xcode-build)</li>
<li>Console app</li>
<li>Sharing from device (用户隐私共享数据分享)</li>
</ul>
<a id="more"></a>
<h3 id="如何阅读崩溃日志"><a href="#如何阅读崩溃日志" class="headerlink" title="如何阅读崩溃日志"></a>如何阅读崩溃日志</h3><p>log文件中包含crash的的基本信息，crash原因，崩溃栈，寄存器、loader images。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/crashlog.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/crashlog2.png" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/crashlog3.png" alt=""></p>
<h3 id="如何分析-Crash-原因"><a href="#如何分析-Crash-原因" class="headerlink" title="如何分析 Crash 原因"></a>如何分析 Crash 原因</h3><p>有了这些信息后，怎么分析crash原因呢？首先看一下Exception Type，通过这个可以知道crash的原因，</p>
<h4 id="EXC-BAD-INSTRUCTION-SIGILL"><a href="#EXC-BAD-INSTRUCTION-SIGILL" class="headerlink" title="EXC_BAD_INSTRUCTION (SIGILL)"></a>EXC_BAD_INSTRUCTION (SIGILL)</h4><p>下面这个例子中 EXC_BAD_INSTRUCTION 的意思是 CPU 可能尝试执行不合法的指令。也可以看一下Crash Thread的调用栈.<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/crashreson.png" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/crashcode.jpg" alt=""></p>
<p>引出断言和前提条件 (发生错误时故意中止该过程)</p>
<ul>
<li>强制拆开存储 nil 的 Optional (针对 Swift)</li>
<li>Array越界访问</li>
<li>算术溢出</li>
<li>未捕获的异常</li>
<li>代码中的自定义断言</li>
</ul>
<h4 id="EXC-CRASH-SIGKILL"><a href="#EXC-CRASH-SIGKILL" class="headerlink" title="EXC_CRASH (SIGKILL)"></a>EXC_CRASH (SIGKILL)</h4><p>另一个具体的例子 EXC_CRASH ，下面这个crash信息可以看出，具体原因是看门狗定时器超时，一般是因为APP启动的时间过长或者响应系统事件事件超时导致；比如在主线程进行网络请求，主线程会一直卡住直到网络回调回来。后来占用资源也可能会被系统 kill （0xdead10cc）。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/badfood.jpg" alt=""></p>
<p>被操作系统kill的几种情况</p>
<ul>
<li>Watchdog events ：Watchdog 定时器超时</li>
<li>Device overheated ：设备过热</li>
<li>Memory exhaustion : 内存超出</li>
<li>Invalid code signature ：无效签名</li>
</ul>
<p>Avoiding Launch Timeouts (如何避免启动超时)</p>
<ul>
<li>Frequent crash reason in app review (进行app审查)</li>
<li>Disabled in Simulator and in the debugger （在模拟器和调试器中被禁用）</li>
<li>Test your app without the debugger :<br>  1:without the debugger ： 非 debug 模式测试你的应用<br>  2:on a real device ： 在真实的设备上<br>  3:on older hardware ： 在较旧的硬件上</li>
</ul>
<h4 id="EXC-BAD-ACCESS-SIGSEGV"><a href="#EXC-BAD-ACCESS-SIGSEGV" class="headerlink" title="EXC_BAD_ACCESS (SIGSEGV)"></a>EXC_BAD_ACCESS (SIGSEGV)</h4><p>这个例子是出现了 Memory Errors 导致。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/memerror.jpg" alt=""><br>导致内存出错的原因：</p>
<ul>
<li>写入只读存储器 </li>
<li>从根本不存在的内存中读取。(读取释放后的对象)</li>
<li>over released</li>
<li>buffer overflow</li>
</ul>
<p>通过crash地址可以得到更多信息，7fdd5e70700这个地址在MALLOC_TINY的地址空间范围内，<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/tiny.png" alt=""><br>当free函数删除一个对象时，它会将其插入到其他 dead 对象的空闲列表中。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/rotatedfree.png" alt=""></p>
<p>如何找到具体的对象 ？有没有办法知道具体是哪个object被多次release导致的crash呢？日志里面虽然有调用栈信息，但是都是编译器生成的函数，没有跟crash相关的具体信息。下面通过一个具体的例子说明如何找到LoginViewController中被多次release的对象。</p>
<ul>
<li>在命令行或者xcode打开lldb</li>
<li>command script import lldb.macosx.crashlog</li>
<li>加载crash log文件</li>
</ul>
<p>Exception Codes    异常出错的代码（常见代码有以下几种)<br>0x8badf00d错误码：Watchdog超时，意为“ate bad food”。<br>0xdeadfa11错误码：用户强制退出，意为“dead fall”。<br>0xbaaaaaad错误码：用户按住Home键和音量键，获取当前内存状态，不代表崩溃。<br>0xbad22222错误码：VoIP应用（因为太频繁？）被iOS干掉。<br>0xc00010ff错误码：因为太烫了被干掉，意为“cool off”。<br>0xdead10cc错误码：因为在后台时仍然占据系统资源（比如通讯录）被干掉，意为“dead lock”。</p>
<h4 id="Crash-Log-Analysis-Summary"><a href="#Crash-Log-Analysis-Summary" class="headerlink" title="Crash Log Analysis Summary"></a>Crash Log Analysis Summary</h4><ul>
<li>Understand the crash reason : 明白 Crash 原因</li>
<li>Examine the crashed thread’s stack trace  : 检查崩溃线程的堆栈跟踪</li>
<li>Look for more clues in bad address and disassembly : 在错误地址和反汇编中查找更多线索</li>
</ul>
<h4 id="Crash-Analysis-Tips"><a href="#Crash-Analysis-Tips" class="headerlink" title="Crash Analysis Tips"></a>Crash Analysis Tips</h4><ul>
<li>Look at code other than the line that crashed : 看看除了崩溃的行之外的代码</li>
<li>Look at thread stack traces other than the crashed thread : 查看崩溃线程以外的线程堆栈跟踪</li>
<li>Look at more than one crash log : 查看多个 crash log</li>
<li>Use Address Sanitizer and Zombies to reproduce memory errors : 使用 Address Sanitizer 和僵 Zombies 来重现内存错误<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/common.png" alt=""></li>
</ul>
<p>-&gt; 参考资料：<a href="https://developer.apple.com/library/archive/technotes/tn2151/_index.html" target="_blank" rel="noopener">Understanding and Analyzing Application Crash Reports</a></p>
<h3 id="Multithreading-Issues-多线程问题"><a href="#Multithreading-Issues-多线程问题" class="headerlink" title="Multithreading Issues (多线程问题)"></a>Multithreading Issues (多线程问题)</h3><h4 id="Symptoms-of-Multithreading-Bugs-in-Crash-Logs"><a href="#Symptoms-of-Multithreading-Bugs-in-Crash-Logs" class="headerlink" title="Symptoms of Multithreading Bugs in Crash Logs"></a>Symptoms of Multithreading Bugs in Crash Logs</h4><ul>
<li>One of the hardest bug types to reproduce and diagnose: 最难复制和诊断的错误类型之一</li>
<li>Multithreading bugs often cause memory corruptions: 多线程错误通常会导致内存损坏</li>
<li>Multiple threads currently executing similar code: 当前正在执行类似代码的多个线程</li>
<li>One bug can appear as different crash points : 一个bug可以显示为不同的崩溃点</li>
</ul>
<p>Edit Scheme → Dignostics → Thread Sanitizer → finding buffer overflows<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/sanitizer.png" alt=""></p>
<h4 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h4><ul>
<li>Test your app on real devices</li>
<li>Try to reproduce crashes</li>
<li>Use bug-finding tools on hard-to-reproduce crashes</li>
<li>Address Sanitizer for memory corruption bugs : 使用Address Sanitizer 调试内存问题</li>
<li>Thread Sanitizer for multithreading problems : 使用Thread Sanitizer调试多线程问题</li>
</ul>
<h4 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h4><ul>
<li>User Organizer to access crash logs: 关注 Organizer 中的crash</li>
<li>Analyze reproducible crahses : 分析重复的crahses</li>
<li>Look for signs of memory corruption and threading issues : 查找内存损坏和线程问题的迹象</li>
<li>Use bug-finding tools to help reproduce: 利用工具帮助复现问题</li>
<li>给每个线程加个名字，发生崩溃容易定位问题</li>
</ul>
<p>参考文档 &amp; 视频：</p>
<ul>
<li><a href="https://developer.apple.com/documentation/code_diagnostics/thread_sanitizer" target="_blank" rel="noopener">Thread Sanitizer</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2017/406" target="_blank" rel="noopener">Finding Bugs Using Xcode Runtime Tools</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2018/416" target="_blank" rel="noopener">iOS Memory Deep Dive</a></li>
</ul>
<h4 id="如何判定发生了-OOM-Out-Of-Memory）"><a href="#如何判定发生了-OOM-Out-Of-Memory）" class="headerlink" title="如何判定发生了 OOM (Out Of Memory）"></a>如何判定发生了 OOM (Out Of Memory）</h4><p>收到低内存警告不一定会 crash，OOM 时也不一定能收到低内存警告<br>facebook的做法是在app启动时使用排除法：</p>
<ul>
<li>App没有升级</li>
<li>App没有调用exit()或abort()退出</li>
<li>用户没有强退App</li>
<li>系统没有升级/重启</li>
<li>App当时没有后台运行</li>
<li>App出现FOOM</li>
</ul>
<p>如果 app 收到了低内存警告，又在几秒钟之内 crash 了，基本上就可以 100% 确定发生了 OOM。</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>前段时间通过搭建 Crash 平台的机会，知道了如何进行 Crash 的解析和聚类。那么如何去理解一个 Crash 呢？这篇文章是通过 WWDC 中的资料进行整理学习的。</p>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2018/414/" target="_blank" rel="noopener">Understanding Crashes and Crash Logs - WWDC 2018</a> 及相关的视频和资料。</li>
<li>参考文章：<a href="https://medium.com/flawless-app-stories/understanding-crashes-and-crash-logs-fff9781a5df4" target="_blank" rel="noopener">Understanding Crashes and Crash Logs</a></li>
</ul>
<h3 id="崩溃的基本原理"><a href="#崩溃的基本原理" class="headerlink" title="崩溃的基本原理"></a>崩溃的基本原理</h3><h4 id="什么是崩溃"><a href="#什么是崩溃" class="headerlink" title="什么是崩溃"></a>什么是崩溃</h4><p>崩溃当你的 app 试图做一些不被允许的事情导致被突然终止。如：</p>
<ul>
<li>impossible for CPU to execute code : CPU 无法执行某些代码 （除以0）</li>
<li>Operating system is enforcing a policy: 操作系统正在执行某些策略 (为了保证系统流畅性，操作系统kill了 启动时间过长、使用了太多内存的app)</li>
<li>Programming language is preventing failure: 编程语言自身阻止失败并触发崩溃 (数组越界)</li>
<li>Developer is preventing failure: 开发者自己触发（assert）</li>
</ul>
<h4 id="查看崩溃的方式"><a href="#查看崩溃的方式" class="headerlink" title="查看崩溃的方式"></a>查看崩溃的方式</h4><ul>
<li>Crashes Organizer window</li>
<li>Devices window</li>
<li>Automated testing (Xcode、 Xcode Server、 Xcode-build)</li>
<li>Console app</li>
<li>Sharing from device (用户隐私共享数据分享)</li>
</ul>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[搭建 iOS-Crash 平台实践总结]]></title>
    <link href="http://yoursite.com/2020/01/20/%E5%BF%AB%E9%80%9F%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AAcrash%E5%B9%B3%E5%8F%B0/"/>
    <id>http://yoursite.com/2020/01/20/快速搭建一个crash平台/</id>
    <published>2020-01-19T16:25:47.000Z</published>
    <updated>2023-10-08T07:19:09.012Z</updated>
    <content type="html"><![CDATA[<p>前期调研准备可参考上一篇系列文章：<a href="https://www.xuyanlan.com/2019/02/20/iOS-crash-report/" target="_blank" rel="noopener">https://www.xuyanlan.com/2019/02/20/iOS-crash-report/</a></p>
<h3 id="客户端收集-Crash"><a href="#客户端收集-Crash" class="headerlink" title="客户端收集 Crash"></a>客户端收集 Crash</h3><p>客户端收集 Crash 使用的是 <a href="https://github.com/microsoft/plcrashreporter" target="_blank" rel="noopener">PLCrashReporter</a> 这个开源的库，集成方法有很多成熟的文章可以参考，这里不再赘述。而且有需要的话你可以在 <code>signal_handler_callback</code> 方法中获取到崩溃信息然后追加崩溃瞬间的一些 App 信息，对于定位 Crash 有着重要的作用。</p>
<h3 id="Crash-解析-llvm8-0"><a href="#Crash-解析-llvm8-0" class="headerlink" title="Crash 解析 - llvm8.0"></a>Crash 解析 - llvm8.0</h3><p>实践中 Crash 解析方式并未用到上一篇文章中提到的自己实现的 macho 解析工具，但是前期的准备工作让后期搭建工作更加顺利。如果你的服务器不是 MacOS (是的话可以直接是使用 symbolicatecrash，只需要收集各个版本的固件即可)。我们所部署的 Crash 解析服务器是Linux。<br>发现了一个成熟的 Crash 解析工具 - <a href="http://llvm.org/" target="_blank" rel="noopener">llvm</a>, 我所使用的版本是llvm8.0，部署非常简单，下载解压后就能使用。<a href="http://llvm.org/docs/CommandGuide/" target="_blank" rel="noopener">http://llvm.org/docs/CommandGuide/</a> 中列出了一些 Commands。 其中主要使用到的是:</p>
<ul>
<li><a href="http://llvm.org/docs/CommandGuide/llvm-symbolizer.html" target="_blank" rel="noopener">llvm-symbolizer</a> - convert addresses into source code locations</li>
<li><a href="http://llvm.org/docs/CommandGuide/llvm-readelf.html" target="_blank" rel="noopener">llvm-readelf</a> - GNU-style LLVM Object Reader</li>
</ul>
<p>llvm-symbolizer 用于定位代码位置，这个是解析 Crash 的重点，来看一个例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">llvm-symbolizer --obj=XXX:arm64 0x100301A5C  0x1003014DC 0x1002FDE74 0x1002FDD3C  0x1002FBE60 0x100188B10</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p><code>llvm-symbolizer</code> 使用非常简单，<code>--obj</code> 指定 Binary Image 文件（如：xxx.dSym、libobjc.A.dylib）路径，后面可以跟同一个 Binary Image 中的多个文件地址（File Address）。但是从 Crash 文件中不能直接得到文件地址，上篇文章中提到了获取文件地址的方式，File Address = Stack address - Load address + Slide Value。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">//crash的某一行信息，0x0000000195dd13a8 为 Stack address, 0x195da1000 是 Load address。 最后的十进制值其实就是 Stack address - Load address 的值了。</span><br><span class="line">1 libdispatch.dylib 0x0000000195dd13a8 0x195da1000 + 197544</span><br></pre></td></tr></table></figure>
<p>而通过 Load address 可以从 Binary Images 里面找到对应的库:<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/image.png" alt=""></p>
<ul>
<li>dSym 类型的 Binary Image 能够直接通过判断 arm 来获取 Slide Value，<pre><code>Slide Value（32位虚拟地址）: 0x00004000
Slide Value（64位虚拟地址）: 0x0000000100000000 
</code></pre></li>
<li>系统库获取则要依靠从库文件中读取出来，可以通过 <code>llvm-readelf</code> 命令来获取 Slide Value，其中 Segment 的 Text 段中的vmaddr 就是 Slide Value 了。如图：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/segment.png" alt=""><br>可通过 llvm-dwarfdump -uuid  获取到的 UUID 和 Slide Value进行 Map，记录到对应系统库的一个文件中，真正解析的时候直接通过 Binary Images 定位到 UUID 后通过 Map 查找到 Slide Value 进行计算。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/segment.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/json.jpg" alt=""></li>
</ul>
<p>附上一张平时 debug 时没开断点，abort 住了只能拿到未解析的堆栈信息，利用llvm 查找崩溃的截图。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/crash.png" alt=""><br>至此，想必你已经知道了如何通过 llvm 来解析 Crash 中的某行崩溃信息， 接下来的事情就是如何批量解析和平台化了。</p>
<h3 id="批量解析-Crash-文件-amp-平台聚类"><a href="#批量解析-Crash-文件-amp-平台聚类" class="headerlink" title="批量解析 Crash 文件 &amp; 平台聚类"></a>批量解析 Crash 文件 &amp; 平台聚类</h3><pre><code>我们已经收集到很多 Crash 文件了，上面说了如何解析一行崩溃信息。那么就可以进行批量解析了和聚类了。
</code></pre><p>其实很简单，我们做的只是利用了 <code>llvm-symbolizer</code> 命令同时能够解析多个崩溃地址的特性：</p>
<ul>
<li>整合多个收集上来的 Crash 文件，再统一解析后切分成单个解析好的 Crash 文件。只需要简单的脚本即可实现。</li>
<li>制定聚类规则<br>  1：找到有 AppName 相关的 Crash 堆栈 Thread 信息，优先级： Last Exception Backtrace &gt; Crashed Thread: XXX &gt; 第一个包含自身 AppName 堆栈的 Thread &gt; Thread 0<br>  2: 定位到的 Thread 提取出 AppName 相关的 Crash 堆栈信息，去除地址相关的信息，进行 md5，这就聚类好了。</li>
<li>其他定制需求： 如某个 Crash 出现的系统版本、App 版本、新Crash告警，报表等。</li>
</ul>
<h3 id="号-unknown-arm64问题解决"><a href="#号-unknown-arm64问题解决" class="headerlink" title="??? 号 + unknown-arm64问题解决"></a>??? 号 + unknown-arm64问题解决</h3><p>有些机型收上来发现image是???，基地址是0x0，搜索发现是苹果新出了arm64e,参考以下文章：<br><a href="https://www.jianshu.com/p/03580e3a1a05" target="_blank" rel="noopener">arm64e符号翻译与PAC问题</a><br>知道原因后发现可以通过crash地址恢复image 和 基地址。</p>
<ul>
<li>通过 va = va &amp; 0x0fffffffff; 将崩溃地址复原，遍历所有的image，得到基地址和对应的image。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e5%88%9b%e5%bb%bacrash%e5%b9%b3%e5%8f%b0/re.jpg" alt=""></li>
<li>又可以愉快的解析啦~</li>
</ul>
]]></content>
    <summary type="html">
    <![CDATA[<p>前期调研准备可参考上一篇系列文章：<a href="https://www.xuyanlan.com/2019/02/20/iOS-crash-report/" target="_blank" rel="noopener">https://www.xuyanlan.com/2019/02/20/iOS-crash-report/</a></p>
<h3 id="客户端收集-Crash"><a href="#客户端收集-Crash" class="headerlink" title="客户端收集 Crash"></a>客户端收集 Crash</h3><p>客户端收集 Crash 使用的是 <a href="https://github.com/microsoft/plcrashreporter" target="_blank" rel="noopener">PLCrashReporter</a> 这个开源的库，集成方法有很多成熟的文章可以参考，这里不再赘述。而且有需要的话你可以在 <code>signal_handler_callback</code> 方法中获取到崩溃信息然后追加崩溃瞬间的一些 App 信息，对于定位 Crash 有着重要的作用。</p>
<h3 id="Crash-解析-llvm8-0"><a href="#Crash-解析-llvm8-0" class="headerlink" title="Crash 解析 - llvm8.0"></a>Crash 解析 - llvm8.0</h3><p>实践中 Crash 解析方式并未用到上一篇文章中提到的自己实现的 macho 解析工具，但是前期的准备工作让后期搭建工作更加顺利。如果你的服务器不是 MacOS (是的话可以直接是使用 symbolicatecrash，只需要收集各个版本的固件即可)。我们所部署的 Crash 解析服务器是Linux。<br>发现了一个成熟的 Crash 解析工具 - <a href="http://llvm.org/" target="_blank" rel="noopener">llvm</a>, 我所使用的版本是llvm8.0，部署非常简单，下载解压后就能使用。<a href="http://llvm.org/docs/CommandGuide/" target="_blank" rel="noopener">http://llvm.org/docs/CommandGuide/</a> 中列出了一些 Commands。 其中主要使用到的是:</p>
<ul>
<li><a href="http://llvm.org/docs/CommandGuide/llvm-symbolizer.html" target="_blank" rel="noopener">llvm-symbolizer</a> - convert addresses into source code locations</li>
<li><a href="http://llvm.org/docs/CommandGuide/llvm-readelf.html" target="_blank" rel="noopener">llvm-readelf</a> - GNU-style LLVM Object Reader</li>
</ul>
<p>llvm-symbolizer 用于定位代码位置，这个是解析 Crash 的重点，来看一个例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">llvm-symbolizer --obj=XXX:arm64 0x100301A5C  0x1003014DC 0x1002FDE74 0x1002FDD3C  0x1002FBE60 0x100188B10</span><br></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[runloop]]></title>
    <link href="http://yoursite.com/2019/08/01/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3-runloop/"/>
    <id>http://yoursite.com/2019/08/01/深入理解-runloop/</id>
    <published>2019-08-01T14:53:20.000Z</published>
    <updated>2023-10-08T07:19:24.067Z</updated>
    <content type="html"><![CDATA[<p>深入理解 runloop  </p>
<p>本文参考了多位前辈的文章、视频和源码进行学习、以及总结加深理解。</p>
<ul>
<li><a href="https://v.youku.com/v_show/id_XODgxODkzODI0.html?refer=seo_operation.liuxiao.liux_00003303_3000_Qzu6ve_19042900" target="_blank" rel="noopener">iOS线下分享《RunLoop》by 孙源@sunnyxx</a></li>
<li><a href="https://blog.ibireme.com/2015/05/18/runloop/" target="_blank" rel="noopener">深入理解RunLoop</a><br>RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念。runloop是与线程相关的基础架构的一部分。runloop是指用于安排工作，并协调接收传入事件的事件处理循环。runloop的目的是在有工作时保持线程忙，并在没有工作时时让线程进入休眠状态。本文从源码入手，理解 runloop 原理，以及相关自动释放池、延迟回调、触摸事件、屏幕刷新等功能。</li>
</ul>
<h3 id="RunLoop-概念"><a href="#RunLoop-概念" class="headerlink" title="RunLoop 概念"></a>RunLoop 概念</h3><p>一般来讲，一个线程一次只执行一个任务，任务执行完成后线程就退出了，runloop 就是能让线程保持随时能处理任务但不退出的一个机制。这种机制就是 Event Loop 模型，实现这种模型的关键点在于：如何管理事件/消息，如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。</p>
<p>OSX/iOS 系统中，提供了两个这样的对象：NSRunLoop 和 CFRunLoopRef。<br>CFRunLoopRef 是在 CoreFoundation 框架内的，它提供了纯 C 函数的 API，所有这些 API 都是线程安全的。<br>NSRunLoop 是基于 CFRunLoopRef 的封装，提供了面向对象的 API，但是这些 API 不是线程安全的。</p>
<p>RunLoop 实际上就是一个对象，这个对象管理了其需要处理的事件和消息，并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后，就会一直处于这个函数内部 “接受消息-&gt;等待-&gt;处理” 的循环中，直到这个循环结束（比如传入 quit 的消息），函数返回。这种说法比较抽象，下面会结合开发过程中使用到的例子来配合源码进行理解。<br><a id="more"></a></p>
<h3 id="RunLoop-和线程"><a href="#RunLoop-和线程" class="headerlink" title="RunLoop 和线程"></a>RunLoop 和线程</h3><p>每个线程会对应一个 RunLoop ，当你首次在线程（非主线程）中访问 RunLoop 时会自动创建一个 RunLoop ，线程销毁时销毁对应 Runloop，而主线程的 RunLoop 在main函数中会一直保持运行状态。<br>苹果不允许直接创建 RunLoop，它只提供了两个自动获取的函数：CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 这两个函数内部的逻辑大概是下面这样:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">/// 全局的Dictionary，key 是 pthread_t， value 是 CFRunLoopRef</span><br><span class="line">static CFMutableDictionaryRef loopsDic;</span><br><span class="line">/// 访问 loopsDic 时的锁</span><br><span class="line">static CFSpinLock_t loopsLock;</span><br><span class="line"> </span><br><span class="line">/// 获取一个 pthread 对应的 RunLoop。</span><br><span class="line">CFRunLoopRef _CFRunLoopGet(pthread_t thread) &#123;</span><br><span class="line">    OSSpinLockLock(&amp;loopsLock);</span><br><span class="line">    </span><br><span class="line">    if (!loopsDic) &#123;</span><br><span class="line">        // 第一次进入时，初始化全局Dic，并先为主线程创建一个 RunLoop。</span><br><span class="line">        loopsDic = CFDictionaryCreateMutable();</span><br><span class="line">        CFRunLoopRef mainLoop = _CFRunLoopCreate();</span><br><span class="line">        CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    /// 直接从 Dictionary 里获取。</span><br><span class="line">    CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));</span><br><span class="line">    </span><br><span class="line">    if (!loop) &#123;</span><br><span class="line">        /// 取不到时，创建一个</span><br><span class="line">        loop = _CFRunLoopCreate();</span><br><span class="line">        CFDictionarySetValue(loopsDic, thread, loop);</span><br><span class="line">        /// 注册一个回调，当线程销毁时，顺便也销毁其对应的 RunLoop。</span><br><span class="line">        _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    OSSpinLockUnLock(&amp;loopsLock);</span><br><span class="line">    return loop;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line">CFRunLoopRef CFRunLoopGetMain() &#123;</span><br><span class="line">    return _CFRunLoopGet(pthread_main_thread_np());</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line">CFRunLoopRef CFRunLoopGetCurrent() &#123;</span><br><span class="line">    return _CFRunLoopGet(pthread_self());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="RunLoop-机制"><a href="#RunLoop-机制" class="headerlink" title="RunLoop 机制"></a>RunLoop 机制</h3><p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/runloop/jizhi.png" alt=""></p>
<p>主线程几乎所有的函数都是从下面的6个函数中发起的。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__</span><br><span class="line">__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__</span><br><span class="line">__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__</span><br><span class="line">__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__</span><br><span class="line">__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__</span><br><span class="line">__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__</span><br></pre></td></tr></table></figure></p>
<p>Source0 示例<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/runloop/stack.png" alt=""></p>
<ul>
<li>CFRunloop 和 Thread 是一一对应，但是 CFRunloop 自身可以支持嵌套结构。</li>
<li>RunloopMode：runloop 必须在某种 mode 下执行</li>
<li>CFRunloopTimer：NSTimer，performSelector:afterDelay, CADisplayLink(和系统刷新频率一致)</li>
<li>CFRunloopSource: Source是runloop 的数据源（protocol）,定义了两个 Version 的 Source<br>  1: Source0 用来管理 App内部事件、App 自身负责管理（如 UIEnvent、CFSocket）<br>  2: Source1 由 runloop 和内核管理，mach port（轻量级得进程间通讯的方式） 驱动如 CFMachPort、CFMessagePort。<br>  3：有需要的话可以从中选择一个 Source 自己实现</li>
<li>CFRunloopOberver：向外部报告 Runloop 得状态变化，cocoa 中很多机制都是由 CFRunloopOberver触发，如 CAAnimation, AutoRelease</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">    /* Run Loop Observer Activities */</span><br><span class="line">typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) &#123;</span><br><span class="line">    kCFRunLoopEntry = (1UL &lt;&lt; 0), //进入RunLoop</span><br><span class="line">    kCFRunLoopBeforeTimers = (1UL &lt;&lt; 1),//即将处理Timer事件</span><br><span class="line">    kCFRunLoopBeforeSources = (1UL &lt;&lt; 2),//即将处理Source事件</span><br><span class="line">    kCFRunLoopBeforeWaiting = (1UL &lt;&lt; 5),//即将休眠</span><br><span class="line">    kCFRunLoopAfterWaiting = (1UL &lt;&lt; 6),//被唤醒</span><br><span class="line">    kCFRunLoopExit = (1UL &lt;&lt; 7),//退出</span><br><span class="line">    kCFRunLoopAllActivities = 0x0FFFFFFFU</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="runloop-应用"><a href="#runloop-应用" class="headerlink" title="runloop 应用"></a>runloop 应用</h3><p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/runloop/runloop-cocoa.jpg" alt=""></p>
<h4 id="RunloopMode"><a href="#RunloopMode" class="headerlink" title="RunloopMode"></a>RunloopMode</h4><ul>
<li>NSDefaultRunLoopMode，默认情况下使用</li>
<li>NSConnectionReplyMode， 开发者一般用不到，系统用来处理NSConnection相关事件</li>
<li>NSModalPanelRunLoopMode, 处理 modal panels 事件</li>
<li>UITrackingRunLoopMode, 用于处理拖拽和用户交互的模式</li>
<li>NSRunloopCommonModes: 集合模式，默认包括 Default，Modal，Event Tracking 三大模式，可以处理几乎所有事件</li>
<li>应用场景举例：主线程的 RunLoop 里有两个预置的 Mode：kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为”Common”属性。DefaultMode 是 App 平时所处的状态，TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时，Timer 会得到重复回调，但此时滑动一个TableView时，RunLoop 会将 mode 切换为 TrackingRunLoopMode，这时 Timer 就不会被回调，并且也不会影响到滑动操作。<br>有时你需要一个 Timer，在两个 Mode 中都能得到回调，一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式，就是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。另外一种方法是为 timer 单独开启一个线程，和主线程 runloop 互不影响。</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">// 方法1</span><br><span class="line">//可以将 timer 分别加入到这两个 mode</span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer: self.timer forMode:UITrackingRunLoopMode];</span><br><span class="line">// 方法2</span><br><span class="line">// 将 timer 加入到 NSRunloopCommonModes 中。</span><br><span class="line">[[NSRunLoop currentRunLoop] addTimer: self.timer forMode:NSRunLoopCommonModes];</span><br><span class="line"></span><br><span class="line">// 方法3</span><br><span class="line">// 将 timer 放到另一个线程中，然后开启另一个线程的 runloop，这样可以保证与主线程互不干扰，而现在主线程正在处理页面滑动</span><br><span class="line">dispatch_async(dispatch_get_global_queue(0, 0), ^&#123;</span><br><span class="line">    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(handlTimer2:) userInfo:nil repeats:YES];</span><br><span class="line">    [[NSThread currentThread] setName:@&quot;timerThread&quot;];</span><br><span class="line">    [[NSRunLoop currentRunLoop] run];</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h4 id="AutoreleasePool"><a href="#AutoreleasePool" class="headerlink" title="AutoreleasePool"></a>AutoreleasePool</h4><p>CFRunloopOberver 在 Runloop 两次 Sleep 之间对 AutoreleasePool 进行了 Push 和 Pop。也就是说触发了 CFRunloopOberver 监听会对 AutoreleasePool 进行处理。<br>如App启动时，往主线程 RunLoop注册了两个Observer 其回调都是 _wrapRunLoopWithAutoreleasePoolHandler(),第一个 Observer 监视的事件是 Entry(即将进入Loop)，其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647，优先级最高，保证创建释放池发生在其他所有回调之前。第二个 Observer 监视了两个事件： BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池；Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647，优先级最低，保证其释放池子发生在其他所有回调之后。<br>测试代码：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">//添加observer监听</span><br><span class="line"></span><br><span class="line">- (void)testRunloop &#123;</span><br><span class="line">    //监听runloop</span><br><span class="line">    CFRunLoopObserverContext context = &#123;0,(__bridge void*)self,NULL,NULL&#125;;</span><br><span class="line">    CFRunLoopObserverRef runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities,YES,0,&amp;runLoopObserverCallBack,&amp;context);</span><br><span class="line">    CFRunLoopAddObserver(CFRunLoopGetCurrent(), runLoopObserver, kCFRunLoopCommonModes);</span><br><span class="line">    </span><br><span class="line">    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(handlTimerRunloop) userInfo:nil repeats:NO];</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) &#123;</span><br><span class="line">    switch (activity) &#123;</span><br><span class="line">        case kCFRunLoopEntry:</span><br><span class="line">            NSLog(@&quot;进入RunLoop&quot;);</span><br><span class="line">            break;</span><br><span class="line">        case kCFRunLoopBeforeTimers:</span><br><span class="line">            NSLog(@&quot;即将处理Timer事件&quot;);</span><br><span class="line">            break;</span><br><span class="line">        case kCFRunLoopBeforeSources:</span><br><span class="line">            NSLog(@&quot;即将处理Source事件&quot;);</span><br><span class="line">            break;</span><br><span class="line">        case kCFRunLoopBeforeWaiting:</span><br><span class="line">            NSLog(@&quot;即将休眠&quot;);</span><br><span class="line">            break;</span><br><span class="line">        case kCFRunLoopAfterWaiting:</span><br><span class="line">            NSLog(@&quot;被唤醒&quot;);</span><br><span class="line">            break;</span><br><span class="line">        case kCFRunLoopExit:</span><br><span class="line">            NSLog(@&quot;退出RunLoop&quot;);</span><br><span class="line">            break;</span><br><span class="line">        default:</span><br><span class="line">            break;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    //    NSLog(@&quot;-----&gt; activity is %lu&quot;, activity);</span><br><span class="line">//    NSLog(@&quot;-----&gt; runloop mode is %@&quot;, [[NSRunLoop currentRunLoop] currentMode]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>给 _wrapRunLoopWithAutoreleasePoolHandler 添加断点测试发现。<br>在进入Runloop之前（截图不好体现，可亲测）、即将休眠和退出Runloop后断点都进入了。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/runloop/observerHandler.png" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/runloop/exit.png" alt=""></p>
<p>待续…</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>深入理解 runloop  </p>
<p>本文参考了多位前辈的文章、视频和源码进行学习、以及总结加深理解。</p>
<ul>
<li><a href="https://v.youku.com/v_show/id_XODgxODkzODI0.html?refer=seo_operation.liuxiao.liux_00003303_3000_Qzu6ve_19042900" target="_blank" rel="noopener">iOS线下分享《RunLoop》by 孙源@sunnyxx</a></li>
<li><a href="https://blog.ibireme.com/2015/05/18/runloop/" target="_blank" rel="noopener">深入理解RunLoop</a><br>RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念。runloop是与线程相关的基础架构的一部分。runloop是指用于安排工作，并协调接收传入事件的事件处理循环。runloop的目的是在有工作时保持线程忙，并在没有工作时时让线程进入休眠状态。本文从源码入手，理解 runloop 原理，以及相关自动释放池、延迟回调、触摸事件、屏幕刷新等功能。</li>
</ul>
<h3 id="RunLoop-概念"><a href="#RunLoop-概念" class="headerlink" title="RunLoop 概念"></a>RunLoop 概念</h3><p>一般来讲，一个线程一次只执行一个任务，任务执行完成后线程就退出了，runloop 就是能让线程保持随时能处理任务但不退出的一个机制。这种机制就是 Event Loop 模型，实现这种模型的关键点在于：如何管理事件/消息，如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。</p>
<p>OSX/iOS 系统中，提供了两个这样的对象：NSRunLoop 和 CFRunLoopRef。<br>CFRunLoopRef 是在 CoreFoundation 框架内的，它提供了纯 C 函数的 API，所有这些 API 都是线程安全的。<br>NSRunLoop 是基于 CFRunLoopRef 的封装，提供了面向对象的 API，但是这些 API 不是线程安全的。</p>
<p>RunLoop 实际上就是一个对象，这个对象管理了其需要处理的事件和消息，并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后，就会一直处于这个函数内部 “接受消息-&gt;等待-&gt;处理” 的循环中，直到这个循环结束（比如传入 quit 的消息），函数返回。这种说法比较抽象，下面会结合开发过程中使用到的例子来配合源码进行理解。<br></p>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[【译】A taste of MVVM and Reactive paradigm]]></title>
    <link href="http://yoursite.com/2019/06/26/mvvm%E3%80%90%E8%AF%91%E3%80%91/"/>
    <id>http://yoursite.com/2019/06/26/mvvm【译】/</id>
    <published>2019-06-26T15:48:03.000Z</published>
    <updated>2022-02-27T04:21:22.729Z</updated>
    <content type="html"><![CDATA[<p>【译】A taste of MVVM and Reactive paradigm</p>
<blockquote>
<p>Medium 原文 <a href="https://medium.com/flawless-app-stories/a-taste-of-mvvm-and-reactive-paradigm-5288a819cca1" target="_blank" rel="noopener">A taste of MVVM and Reactive paradigm</a><br> 原文博客 <a href="https://flawlessapp.io/blog/a-taste-of-mvvm-and-reactive-paradigm/" target="_blank" rel="noopener">A taste of MVVM and Reactive paradigm</a> </p>
</blockquote>
<p>我喜欢 Swift，就像许多其他面向对象的编程语言一样。 Swift 允许你表示具有某些特点和执行一些操作的真实世界对象。</p>
<p>我倾向于认为 App 是一个每个对象都是一个人的世界。他们工作和沟通。如果一个人不能独自完成工作，他需要寻求帮助。举一个项目，例如，如果经理必须自己完成所有的工作，他会发疯的。因此需要组织和委派任务，并且需要许多人在项目上进行协作：设计师，测试人员，Scrum 主管，开发人员。任务完成后，需要通知经理。</p>
<p>这可能不是一个好例子。但至少你了解 OOP 中沟通和授权的重要性。当我开始 iOS 编程时，我对“架构”一词非常感兴趣。但在做了一段时间后，这一切都归结为识别和分担责任。本文讲述了 MVC 和 MVVM 的简单 Extract 类重构，以及如何进一步研究 Rx。您可以自由地创建自己的架构，但无论您做什么，一致性都是关键，不要让您的队友感到困惑或惊讶。</p>
<h3 id="MVC"><a href="#MVC" class="headerlink" title="MVC"></a>MVC</h3><p>看看你最熟悉的架构 - MVC，模型视图控制器的简称。 在新建一个 iOS 项目时总是会得到一个这样的架构。 View 是您使用 <code>UIView</code>，<code>UIButton</code>，<code>UILabel</code> 呈现数据的位置。 Model 只是数据的一个设想的词。 它可以是您的实体，来自网络的数据，来自数据库的对象或来自缓存。Controller 是在 Model 和 View 间进行调解的东西。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/mvvm/1.jpg" alt=""></p>
<h3 id="宇宙中心-UIViewController"><a href="#宇宙中心-UIViewController" class="headerlink" title="宇宙中心 - UIViewController"></a>宇宙中心 - UIViewController</h3><p> <code>ViewController</code> 的问题在于它往往是巨大的。 Apple 把它作为宇宙的中心，它拥有许多属性和责任。你可以用 <code>UIViewController</code> 做很多事情。诸如与故事板交互，管理视图，配置视图轮换，状态恢复等事情。 <code>UIViewController</code> 设计了很多可以覆盖和自定义的方法。<br><a id="more"></a></p>
<p>看看 <code>UIViewController</code> <a href="https://developer.apple.com/documentation/uikit/uiviewcontroller" target="_blank" rel="noopener">文档</a> 中的许多部分，如果没有 <code>UIViewController</code>，则无法执行以下操作。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">func viewDidLoad()</span><br><span class="line">var preferredStatusBarStyle: UIStatusBarStyle &#123; get &#125;</span><br><span class="line">UITableViewDataSource</span><br><span class="line">var presentationController: UIPresentationController? &#123; get &#125;</span><br><span class="line">func childViewControllerForScreenEdgesDeferringSystemGestures() -&gt; UIViewController?</span><br><span class="line">func didMove(toParentViewController parent: UIViewController?)</span><br><span class="line">var systemMinimumLayoutMargins: NSDirectionalEdgeInsets</span><br><span class="line">var edgesForExtendedLayout: UIRectEdge</span><br><span class="line">var previewActionItems: [UIPreviewActionItem]</span><br><span class="line">var navigationItem: UINavigationItem</span><br><span class="line">var shouldAutorotate: Bool</span><br></pre></td></tr></table></figure>
<p>随着应用程序的增长，我们需要为其他逻辑添加更多代码。例如网络，数据源，处理多个代理，present 或 push 子视图控制器。当然，我们可以将所有内容放在视图控制器上，但这会产生出一个超大的 viewController.m 文件，这是很容易让你失去对 viewController 的把控，因为所有的东西都放在了这个巨型视图控制器中。你会倾向于引入重复的代码，并且修复错误变得很难，因为它们遍布各处。</p>
<p>Windows Phone 中的 <code>Page</code> 或 Android 中的 <code>Activity</code> 也是如此。它们用于屏幕或部分功能屏幕。某些操作只能通过它们完成，如 <a href="https://msdn.microsoft.com/en-us/library/system.windows.controls.page.onnavigatedto%28v=vs.105%29.aspx" target="_blank" rel="noopener">Page.OnNavigatedTo</a> ，<a href="https://msdn.microsoft.com/en-us/library/system.windows.controls.page.onnavigatedto%28v=vs.105%29.aspx" target="_blank" rel="noopener">Activity.onCreate</a> 。</p>
<h3 id="架构术语"><a href="#架构术语" class="headerlink" title="架构术语"></a>架构术语</h3><p>当 ViewController 做很多事情时你会怎么做？您将工作移到其他组件。顺便说一句，如果您希望其他对象执行用户输入处理，则可以使用 Presenter。如果 Presenter 做得太多，那么它可以将业务逻辑偏移到 Interactor。此外，还有更多架构术语可供使用。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">let buzzWords = [</span><br><span class="line">  &quot;Model&quot;, &quot;View&quot;, &quot;Controller&quot;, &quot;Entity&quot;, &quot;Router&quot;, &quot;Clean&quot;, &quot;Reactive&quot;, </span><br><span class="line">  &quot;Presenter&quot;, &quot;Interactor&quot;, &quot;Megatron&quot;, &quot;Coordinator&quot;, &quot;Flow&quot;, &quot;Manager&quot;</span><br><span class="line">]</span><br><span class="line">let architecture = buzzWords.shuffled().takeRandom()</span><br><span class="line">let acronym = architecture.makeAcronym()</span><br></pre></td></tr></table></figure>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/mvvm/2.jpg" alt=""></p>
<p>在所有架构术语汇编完成后，我们得到了一个架构。<a href="https://github.com/onmyway133/fantastic-ios-architecture" target="_blank" rel="noopener">这里有更多</a>，包括简单的提取类重构，拥抱 MVC 或从 Clean Code，Rx，EventBus 或 Redux 中获取灵感。选择取决于项目，有些团队更喜欢这类架构而不是另一种架构。</p>
<h3 id="务实的程序员"><a href="#务实的程序员" class="headerlink" title="务实的程序员"></a>务实的程序员</h3><p>人们对什么是好的架构有不同的看法。对我来说，这是关于明确的关注分离，良好的沟通模式和使用舒适度。架构中的每个组件都应该是可识别的并且具有特定的角色。沟通必须清楚，以便我们知道哪个对象正在相互通信。这与良好的依赖注入一起使测试更容易。</p>
<p>理论上听起来不错的事情在实践中可能效果不佳。分离的域对象很酷，协议扩展很酷，多层抽象很酷。但是它们中可能有太多的问题。</p>
<p>如果你对设计模式有足够的了解，你就知道它们都归结为这些简单的原则：</p>
<ul>
<li>封装变化的内容：确定应用程序的各个方面的变化，并将它们与保持不变的方面分开。</li>
<li>编程到接口，而不是实现</li>
<li>更喜欢继承的组合</li>
</ul>
<p>如果我们要掌握一件事，那就是画结构图。关键是要确定责任，并以合理和一致的方式将其组成。向你的队友咨询最合适的，总是在编写代码的时候考虑到你也将是未来的维护者。然后你就会写得更好。</p>
<h3 id="不要和系统做斗争"><a href="#不要和系统做斗争" class="headerlink" title="不要和系统做斗争"></a>不要和系统做斗争</h3><p>一些架构引入了全新的范例。其中有些很麻烦，人们编写脚本来生成模板代码。有很多解决问题的方法是好的。但对我来说，有时候我觉得他们在与这个体系作斗争。有些任务很容易，而有些琐碎的任务则变得非常困难。我们不应该仅仅因为一个架构是时髦的，就把自己限制在一个架构中。要务实，不要武断。</p>
<p>在 iOS 中，我们应该接受 MVC。<code>UIViewController</code> 不适用于内容的全屏显示。它们可以拆分和组合达到拆分功能的目的。我们可以使用 <a href="http://khanlou.com/2015/10/coordinators-redux/" target="_blank" rel="noopener">Coordinator</a> 和 <a href="https://github.com/onmyway133/blog/issues/106" target="_blank" rel="noopener">FlowController</a> 来管理依赖关系和处理流。<a href="https://www.swiftbysundell.com/posts/custom-container-view-controllers-in-swift" target="_blank" rel="noopener">状态转换容器</a>，嵌入式逻辑<a href="https://www.swiftbysundell.com/posts/logic-controllers-in-swift" target="_blank" rel="noopener">控制器</a>，<a href="https://davedelong.com/blog/2017/11/06/a-better-mvc-part-3-fixing-massive-view-controller/" target="_blank" rel="noopener">内容切分</a>。这种令人欣慰的 <code>ViewController</code> 方法在 iOS 中可以很好地与 MVC 配合使用，是我的首选方法。</p>
<h3 id="MVVM"><a href="#MVVM" class="headerlink" title="MVVM"></a>MVVM</h3><p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/mvvm/3.jpg" alt=""></p>
<p>另一个足够好的方法是将一些任务重定向到另一个对象，我们称之为 ViewModel 。这个名字不重要，你可以把它命名为反应堆，大师，恐龙。重要的是你的团队要有一个约定的名字。ViewModel 从 ViewController 中拆分一些任务，并在完成后告诉 ViewController。CocoaTouch 中有一些<a href="https://www.objc.io/issues/7-foundation/communication-patterns/" target="_blank" rel="noopener">通信模式</a>，例如要使用的委托、闭包。</p>
<p>ViewModel 是独立的，没有对 UIKit 的引用，只有输入和输出。我们可以把<a href="https://ashfurrow.com/blog/mvvm-is-exceptionally-ok/" target="_blank" rel="noopener">很多东西</a>放到 ViewModel 中，比如计算、格式化、联网、业务逻辑。此外，如果您不喜欢 ViewModel 变得庞大，那么您肯定需要创建一些专用的对象。ViewModel 是获得超薄 ViewController 的第一步。</p>
<h3 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h3><p>下面是一个非常简单的视图模型，它基于<code>用户</code>模型格式化数据，是同步进行的。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">class ProfileController: UIViewController &#123;</span><br><span class="line">  override func viewDidLoad() &#123;</span><br><span class="line">    super.viewDidLoad()</span><br><span class="line">    let viewModel = ViewModel(user: user)</span><br><span class="line">    nameLabel.text = viewModel.name</span><br><span class="line">    birthdayLabel.text = viewModel.birthdayString</span><br><span class="line">    salaryLabel.text = viewModel.salary</span><br><span class="line">    piLabel.text = viewModel.millionthDigitOfPi</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="异步"><a href="#异步" class="headerlink" title="异步"></a>异步</h3><p>我们一直在使用异步 API。如果我们想显示用户的 Facebook 好友数量呢？为了实现这一点，我们需要调用 Facebook  API，而这个操作需要时间。视图模型可以通过闭包进行报告。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">viewModel.getFacebookFriends &#123; friends in</span><br><span class="line">  self.friendCountLabel.text = &quot;\(friends.count)&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>在内部，ViewModel 可以将任务重定向到专用的 Facebook API 客户端对象.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">class ViewModel &#123;</span><br><span class="line">  func getFacebookFriends(completion: [User] -&gt; Void) &#123;</span><br><span class="line">    let client = APIClient()</span><br><span class="line">    client.getFacebookFriends(for: user) &#123; friends in</span><br><span class="line">      DispatchQueue.main.async &#123;</span><br><span class="line">        completion(friends)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="Android版Jetpack"><a href="#Android版Jetpack" class="headerlink" title="Android版Jetpack"></a>Android版Jetpack</h3><p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/mvvm/4.jpg" alt=""></p>
<p>谷歌在 2017 年的谷歌 IO 上推出了 Android 架构组件，现在是 Jetpack 的一部分。它有 ViewModel 和 LiveData ，这也是一种应用于 Android 的 MVVM 。ViewModel 通过配置更改存活下来，并根据要使用的活动的 LiveData 通知结果。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">class MyActivity : AppCompatActivity() &#123;</span><br><span class="line">    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) &#123;</span><br><span class="line">        super.onCreate(savedInstanceState, persistentState)</span><br><span class="line"></span><br><span class="line">        val model = ViewModelProviders.of(this).get(MyViewModel::class.java)</span><br><span class="line">        model.getUsers().observe(this, &#123; users -&gt;</span><br><span class="line">            // update UI</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>这就是我喜欢 ViewModel 的原因之一。如果我们遵循这样的 ViewModel ，那么 iOS 和 Android 之间的代码结构就会变得相似。不需要一些随机的 JavaScript 跨平台解决方案。您只需学习一次这个概念，就可以将其应用到 iOS 和 Android 上。我在 iOS 上学习 ViewModel、RxSwift ，当我在 Android 上使用 RxJava 和 RxBinding 时，感觉就像在家一样。Kickstarter 项目也证明了这在 iOS 和 Android 应用程序中很好地工作。</p>
<h3 id="绑定"><a href="#绑定" class="headerlink" title="绑定"></a>绑定</h3><p>为了封装闭包，我们可以创建一个名为 Binding 的类，它可以通知一个或多个监听器。它利用了 <code>Didset</code> 的优点，使其可观测性变得清晰。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">class Binding&lt;T&gt; &#123;</span><br><span class="line">  var value: T &#123;</span><br><span class="line">    didSet &#123;</span><br><span class="line">      listener?(value)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  private var listener: ((T) -&gt; Void)?</span><br><span class="line">  init(value: T) &#123;</span><br><span class="line">    self.value = value</span><br><span class="line">  &#125;</span><br><span class="line">  func bind(_ closure: @escaping (T) -&gt; Void) &#123;</span><br><span class="line">    closure(value)</span><br><span class="line">    listener = closure</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>以下是如何在 ViewModel 中使用的 Binding 示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">class ViewModel &#123;</span><br><span class="line">  let friends = Binding&lt;[User]&gt;(value: [])</span><br><span class="line">  init() &#123;</span><br><span class="line">    getFacebookFriends &#123;</span><br><span class="line">      friends.value = $0</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  func getFacebookFriends(completion: ([User]) -&gt; Void) &#123;</span><br><span class="line">  	// Do the work</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>不论何时，当获取或更改 friends 时，ViewController 会相应地更新。这叫做对变化的反应。<br>你经常看到 MVVM 引入了反应式框架，这是有原因的。它们提供了许多链接操作符，并使反应式编程更容易和更具声明性。</p>
<h3 id="RxSwift"><a href="#RxSwift" class="headerlink" title="RxSwift"></a>RxSwift</h3><p>也许 Swift 中最常见的反应式框架是 RXSwift。我喜欢它的一点是它遵循了<a href="http://reactivex.io/" target="_blank" rel="noopener">响应式编程</a>模式。因此，如果您已经使用了 RxJava 、RxJS 或 RxKotlin ，您会感到更加熟悉。</p>
<h3 id="Observable"><a href="#Observable" class="headerlink" title="Observable"></a>Observable</h3><p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/mvvm/5.jpg" alt=""><br>RXSwift 通过 Observable 统一了同步和异步操作。你应该像下面这么做。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">class ViewModel &#123;</span><br><span class="line">  let friends: Observable&lt;[User]&gt;</span><br><span class="line">  init() &#123;</span><br><span class="line">    let client = APIClient()</span><br><span class="line">    friends = Observable&lt;[User]&gt;.create(&#123; subscriber in</span><br><span class="line">      client.getFacebookFriends(completion: &#123; friends in</span><br><span class="line">        subscriber.onNext(friends)</span><br><span class="line">        subscriber.onCompleted()</span><br><span class="line">      &#125;)</span><br><span class="line">      return Disposables.create()</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>RXSwift 的强大功能在于它的众多操作符，这些操作符可以帮助您链接可观察的对象。在这里，您可以调用 2 个网络请求，等待两个请求都完成，然后汇总 friends。这是非常流线型的，可以节省你很多时间。您可以在这里注册 Observable 监听，当请求完成时会触发它：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">override func viewDidLoad() &#123;</span><br><span class="line">  super.viewDidLoad()</span><br><span class="line">  viewModel.friends.subscribe(onNext: &#123; friends in</span><br><span class="line">    self.friendsCountLabel.text = &quot;\(friends.count)&quot;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="输入和输出"><a href="#输入和输出" class="headerlink" title="输入和输出"></a>输入和输出</h3><p>ViewModel 和 RX 的一个优点是，我们可以使用 Observable 分离输入和输出，它提供了一个清晰的界面。点击阅读更多源码内容：<a href="https://github.com/onmyway133/blog/issues/87" target="_blank" rel="noopener"> Input and output container </a>。</p>
<p>下面很明显， fetch 是一个输入，而 friends 是可行的输出。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">class ViewModel &#123;</span><br><span class="line">  class Input &#123;</span><br><span class="line">    let fetch = PublishSubject&lt;()&gt;()</span><br><span class="line">  &#125;</span><br><span class="line">  class Output &#123;</span><br><span class="line">    let friends: Driver&lt;[User]&gt;</span><br><span class="line">  &#125;</span><br><span class="line">  let apiClient: APIClient</span><br><span class="line">  let input: Input</span><br><span class="line">  let output: Output</span><br><span class="line">  init(apiClient: APIClient) &#123;</span><br><span class="line">    self.apiClient = apiClient</span><br><span class="line">    // Connect input and output</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">class ProfileViewController: BaseViewController&lt;ProfileView&gt; &#123;</span><br><span class="line">  let viewModel: ProfileViewModelType</span><br><span class="line">  init(viewModel: ProfileViewModelType) &#123;</span><br><span class="line">    self.viewModel = viewModel</span><br><span class="line">  &#125;</span><br><span class="line">  override func viewDidLoad() &#123;</span><br><span class="line">    super.viewDidLoad()</span><br><span class="line">    // Input</span><br><span class="line">    viewModel.input.fetch.onNext(())</span><br><span class="line">    // Output</span><br><span class="line">    viewModel.output.friends.subscribe(onNext: &#123; friends in</span><br><span class="line">      self.friendsCountLabel.text = &quot;\(friends.count)&quot;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="reactive-如何工作"><a href="#reactive-如何工作" class="headerlink" title="reactive 如何工作"></a>reactive 如何工作</h3><p>如果你喜欢 Rx ，在使用一些框架一段时间后，了解它们是很好的。有一些概念，如 <code>Signal</code>, <code>SignalProducer</code>, <code>Observable</code>, <code>Promise</code>, <code>Future</code>, <code>Task</code>, <code>Job</code>, <code>Launcher</code>, <code>Async</code>，有些人对它们可以有很好的区分。在这里，我简单地称之为 <code>Signal</code>，它是一种可以发出信号值的东西。</p>
<h3 id="Monad"><a href="#Monad" class="headerlink" title="Monad"></a>Monad</h3><p><code>Signal</code> 及其 <code>Result</code> 只是 <a href="https://en.wikipedia.org/wiki/Monad_%28functional_programming%29" target="_blank" rel="noopener">Monads</a> ，它是可以被映射和链接的东西。</p>
<p><code>Signal</code> 使用延迟的执行回调闭包。它可以获取或推送。这就是 <code>Signal</code> 更新值和调用回调的顺序的方式。</p>
<p>执行回调方法意味着我们将一个函数传递给另一个函数。传入函数在适当的时候被调用。</p>
<h3 id="同步和异步"><a href="#同步和异步" class="headerlink" title="同步和异步"></a>同步和异步</h3><p>Monad 可以是同步模式或异步模式。同步更容易理解，但异步在实践中已经很熟悉和使用了。</p>
<ul>
<li>同步：通过返回立即得到返回值</li>
<li>异步：通过回调块得到返回值</li>
</ul>
<p>下面是一个简单的同步和异步自由函数示例：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">// Sync</span><br><span class="line">func sum(a: Int, b: Int) -&gt; Int &#123;</span><br><span class="line">    return a + b</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// Async</span><br><span class="line">func sum(a: Int, b: Int, completion: Int -&gt; Void) &#123;</span><br><span class="line">    // Assumed it is a very long task to get the result</span><br><span class="line">    let result = a + b</span><br><span class="line"></span><br><span class="line">    completion(result)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>以及同步和异步如何应用于返回值类型。注意异步版本，我们在一个完成闭包中得到转换值，而不是从函数立即返回。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">enum Result&lt;T&gt; &#123;</span><br><span class="line">  case value(value: T)</span><br><span class="line">  case failure(error: Error)</span><br><span class="line"></span><br><span class="line">  // Sync</span><br><span class="line">  public func map&lt;U&gt;(f: (T) -&gt; U) -&gt; Result&lt;U&gt; &#123;</span><br><span class="line">    switch self &#123;</span><br><span class="line">    case let .value(value):</span><br><span class="line">      return .value(value: f(value))</span><br><span class="line">    case let .failure(error):</span><br><span class="line">      return .failure(error: error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  // Async</span><br><span class="line">  public func map&lt;U&gt;(f: @escaping ((T), (U) -&gt; Void) -&gt; Void) -&gt; (((Result&lt;U&gt;) -&gt; Void) -&gt; Void) &#123;</span><br><span class="line">    return &#123; g in   // g: Result&lt;U&gt; -&gt; Void</span><br><span class="line">      switch self &#123;</span><br><span class="line">      case let .value(value):</span><br><span class="line">        f(value) &#123; transformedValue in  // transformedValue: U</span><br><span class="line">          g(.value(value: transformedValue))</span><br><span class="line">        &#125;</span><br><span class="line">      case let .failure(error):</span><br><span class="line">        g(.failure(error: error))</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="推送信号"><a href="#推送信号" class="headerlink" title="推送信号"></a>推送信号</h3><p>给出这样一个信号链：<br><code>A -(map)-&gt; B -(flatMap)-&gt; C -(flatMap)-&gt; D -(subscribe)</code><br>推送信号，当 信号A 在一个事件发生时，它通过 CallBacks 事件传播。<code>PushSignal</code> 在 RxSwift 中类似于 <code>PublishSubject</code>。</p>
<ul>
<li>通过向源信号发送事件触发。</li>
<li>我们必须保持 A，因为它使其信号保持</li>
<li>我们订阅最后一个 D</li>
<li>我们将事件发送到第一个 A</li>
<li>A 的回调被调用，它依次使用 A 的映射结果调用 B 的回调，然后 B 的回调使用 B 的平面映射结果调用 C 的回调，依此类推。</li>
</ul>
<p>它类似于 <a href="https://promisesaplus.com/" target="_blank" rel="noopener">Promise A+</a> ，您可以在我的 <a href="https://github.com/onmyway133/Then/blob/master/Pod/Classes/Promise.swift" target="_blank" rel="noopener">Then framework</a> 中看到 Promise A+ 的 Swift 实现。现在，这里是一个简单的 <code>PushSignal</code> 的 Swift 4 实现。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">public final class PushSignal&lt;T&gt; &#123;</span><br><span class="line">  var event: Result&lt;T&gt;?</span><br><span class="line">  var callbacks: [(Result&lt;T&gt;) -&gt; Void] = []</span><br><span class="line">  let lockQueue = DispatchQueue(label: &quot;Serial Queue&quot;)</span><br><span class="line"></span><br><span class="line">  func notify() &#123;</span><br><span class="line">    guard let event = event else &#123;</span><br><span class="line">      return</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    callbacks.forEach &#123; callback in</span><br><span class="line">      callback(event)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  func update(event: Result&lt;T&gt;) &#123;</span><br><span class="line">    lockQueue.sync &#123;</span><br><span class="line">      self.event = event</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    notify()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  public func subscribe(f: @escaping (Result&lt;T&gt;) -&gt; Void) -&gt; Signal&lt;T&gt; &#123;</span><br><span class="line">    // Callback</span><br><span class="line">    if let event = event &#123;</span><br><span class="line">      f(event)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    callbacks.append(f)</span><br><span class="line"></span><br><span class="line">    return self</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  public func map&lt;U&gt;(f: @escaping (T) -&gt; U) -&gt; Signal&lt;U&gt; &#123;</span><br><span class="line">    let signal = Signal&lt;U&gt;()</span><br><span class="line"></span><br><span class="line">    _ = subscribe &#123; event in</span><br><span class="line">      signal.update(event: event.map(f: f))</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    return signal</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>下面是如何使用 PushSignal 将链从字符串转换为其长度，您应该看到 4，即打印的字符串 “test” 的长度。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">let signal = PushSignal&lt;String&gt;()</span><br><span class="line"></span><br><span class="line">_ = signal.map &#123; value in</span><br><span class="line">  return value.count</span><br><span class="line">&#125;.subscribe &#123; event in</span><br><span class="line">  if case let .value(value) = event &#123;</span><br><span class="line">    print(value)</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    print(&quot;error&quot;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">signal.update(event: .value(value: &quot;test&quot;))</span><br></pre></td></tr></table></figure>
<h3 id="获取信号"><a href="#获取信号" class="headerlink" title="获取信号"></a>获取信号</h3><p>给出这样一个信号链：<br><code>A -(map)-&gt; B -(flatMap)-&gt; C -(flatMap)-&gt; D -(subscribe)</code></p>
<p>获取信号，有时称为 Future，意味着当我们订阅最终的信号 D 时，它会导致先前的信号被激活：</p>
<ul>
<li>通过订阅最终信号 D 触发；</li>
<li>我们必须保持 D，因为它使其信号保持</li>
<li>我们订阅最后一个 D</li>
<li>D 的操作运行，它导致 C 的操作运行，… 然后 A 的操作运行。执行任务（如获取网络、检索数据库、文件访问、大量计算等）以获取结果，并调用A的完成。然后，A 的完成调用 B 的完成，结果由 B 的映射映射，…一直映射到订阅方的完成 block。</li>
</ul>
<p>这里是 PullSignal 的一个 Swift 4 实现。PullSignal 类似于 Rxswift 中的 Observable 和 ReactiveSwift 中的SignalProducer。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">public struct PullSignal&lt;T&gt; &#123;</span><br><span class="line">  let operation: ((Result&lt;T&gt;) -&gt; Void) -&gt; Void</span><br><span class="line"></span><br><span class="line">  public init(operation: @escaping ((Result&lt;T&gt;) -&gt; Void) -&gt; Void) &#123;</span><br><span class="line">    self.operation = operation</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  public func start(completion: (Result&lt;T&gt;) -&gt; Void) &#123;</span><br><span class="line">    operation() &#123; event in</span><br><span class="line">      completion(event)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  public func map&lt;U&gt;(f: @escaping (T) -&gt; U) -&gt; PullSignal&lt;U&gt; &#123;</span><br><span class="line">    return PullSignal&lt;U&gt; &#123; completion in</span><br><span class="line">      self.start &#123; event in</span><br><span class="line">        completion(event.map(f: f))</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>链是不活动的，直到您调用链中的最后一个信号开始，这将触发操作流到第一个信号。运行这个代码，您应该看到 4 ，控制台上打印的字符串 “test” 的长度。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">let signal = PullSignal&lt;String&gt; &#123; completion in</span><br><span class="line">  // There should be some long running operation here</span><br><span class="line">  completion(Result.value(value: &quot;test&quot;))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">signal.map &#123; value in</span><br><span class="line">  value.count</span><br><span class="line">&#125;.start &#123; event in</span><br><span class="line">  if case let .value(value) = event &#123;</span><br><span class="line">    print(value)</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    print(&quot;error&quot;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>我希望这些代码段足够简单，能够帮助您理解信号在后台是如何工作的，以及如何区分冷热信号。为了得到一个完全工作的信号框架，您需要实现更多的操作。如 <code>retry</code> , <code>rebounce</code> , <code>throttle</code> , <code>queue</code> , <code>flatten</code>, <code>filter</code>, <code>delay</code>, <code>combine</code> 和添加 UIKit 支持，就像 RxCocoa 所做的，具体可以在我的 <a href="https://github.com/onmyway133/archives/tree/master/Signal/Pod/Classes" target="_blank" rel="noopener">Signal repo</a> 中查看实现。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>架构是一个非常常见的话题。希望这篇文章能给您的决策带来一些想法。MVC 在 iOS 中占主导地位，MVVM 是一个好朋友，RX 是一个强大的工具。以下是一些更有趣的读物：</p>
<ul>
<li><a href="https://ashfurrow.com/blog/mvvm-is-exceptionally-ok/" target="_blank" rel="noopener">MVVM is Exceptionally OK</a></li>
<li><a href="https://academy.realm.io/posts/krzysztof-zablocki-mDevCamp-ios-architecture-mvvm-mvc-viper/" target="_blank" rel="noopener">Good iOS Application Architecture: MVVM vs. MVC vs. VIPER</a></li>
<li><a href="https://www.youtube.com/watch?v=YWVzCd5FYbs" target="_blank" rel="noopener">A Better MVC</a></li>
<li><a href="https://blog.uptech.team/taming-great-complexity-mvvm-coordinators-and-rxswift-8daf8a76e7fd" target="_blank" rel="noopener">Taming Great Complexity: MVVM, Coordinators and RxSwift</a></li>
<li><a href="http://blogs.microsoft.co.il/bnaya/2010/03/13/rx-for-beginners-part-9-hot-vs-cold-observable/" target="_blank" rel="noopener">Rx — for beginners (part 9): Hot Vs. Cold observable</a></li>
<li><a href="https://github.com/ReactiveX/RxSwift/blob/master/Documentation/HotAndColdObservables.md" target="_blank" rel="noopener">Hot and Cold Observables</a></li>
<li><a href="https://stackoverflow.com/questions/17082255/when-to-use-ienumerable-vs-iobservable" target="_blank" rel="noopener">When to use IEnumerable vs IObservable?</a></li>
<li><a href="https://www.youtube.com/watch?v=AcDaWe3S75c" target="_blank" rel="noopener">Functional Reactive Programming without Black Magic</a></li>
<li><a href="https://www.youtube.com/watch?v=mbd6g7NfR-8" target="_blank" rel="noopener">Swift Sync and Async Error Handling</a></li>
</ul>
]]></content>
    <summary type="html">
    <![CDATA[<p>【译】A taste of MVVM and Reactive paradigm</p>
<blockquote>
<p>Medium 原文 <a href="https://medium.com/flawless-app-stories/a-taste-of-mvvm-and-reactive-paradigm-5288a819cca1" target="_blank" rel="noopener">A taste of MVVM and Reactive paradigm</a><br> 原文博客 <a href="https://flawlessapp.io/blog/a-taste-of-mvvm-and-reactive-paradigm/" target="_blank" rel="noopener">A taste of MVVM and Reactive paradigm</a> </p>
</blockquote>
<p>我喜欢 Swift，就像许多其他面向对象的编程语言一样。 Swift 允许你表示具有某些特点和执行一些操作的真实世界对象。</p>
<p>我倾向于认为 App 是一个每个对象都是一个人的世界。他们工作和沟通。如果一个人不能独自完成工作，他需要寻求帮助。举一个项目，例如，如果经理必须自己完成所有的工作，他会发疯的。因此需要组织和委派任务，并且需要许多人在项目上进行协作：设计师，测试人员，Scrum 主管，开发人员。任务完成后，需要通知经理。</p>
<p>这可能不是一个好例子。但至少你了解 OOP 中沟通和授权的重要性。当我开始 iOS 编程时，我对“架构”一词非常感兴趣。但在做了一段时间后，这一切都归结为识别和分担责任。本文讲述了 MVC 和 MVVM 的简单 Extract 类重构，以及如何进一步研究 Rx。您可以自由地创建自己的架构，但无论您做什么，一致性都是关键，不要让您的队友感到困惑或惊讶。</p>
<h3 id="MVC"><a href="#MVC" class="headerlink" title="MVC"></a>MVC</h3><p>看看你最熟悉的架构 - MVC，模型视图控制器的简称。 在新建一个 iOS 项目时总是会得到一个这样的架构。 View 是您使用 <code>UIView</code>，<code>UIButton</code>，<code>UILabel</code> 呈现数据的位置。 Model 只是数据的一个设想的词。 它可以是您的实体，来自网络的数据，来自数据库的对象或来自缓存。Controller 是在 Model 和 View 间进行调解的东西。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/mvvm/1.jpg" alt=""></p>
<h3 id="宇宙中心-UIViewController"><a href="#宇宙中心-UIViewController" class="headerlink" title="宇宙中心 - UIViewController"></a>宇宙中心 - UIViewController</h3><p> <code>ViewController</code> 的问题在于它往往是巨大的。 Apple 把它作为宇宙的中心，它拥有许多属性和责任。你可以用 <code>UIViewController</code> 做很多事情。诸如与故事板交互，管理视图，配置视图轮换，状态恢复等事情。 <code>UIViewController</code> 设计了很多可以覆盖和自定义的方法。<br></p>]]>
    
    </summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[app 加载过程 & 启动速度优化]]></title>
    <link href="http://yoursite.com/2019/02/26/app-%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B/"/>
    <id>http://yoursite.com/2019/02/26/app-加载过程/</id>
    <published>2019-02-25T18:21:33.000Z</published>
    <updated>2023-10-08T07:19:21.758Z</updated>
    <content type="html"><![CDATA[<p>上篇文章介绍了 MachO 文件的结构，你可能注意到其中的 LC_LOAD_DYLINKER 是 dyld， LC_MAIN 加载命令就是加载程序的主入口。这篇文章就详细讲讲 App 的加载过程。</p>
<h4 id="MachO-可执行文件类型"><a href="#MachO-可执行文件类型" class="headerlink" title="MachO 可执行文件类型"></a>MachO 可执行文件类型</h4><p>Xcode build 出的 .app 包中可以看到一个 exec 可执行文件（所有 .o文件集合），同样是一个 MachO 文件，filetype 就是 MH_EXECUTE 类型。<br>MachOView中查看如下。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/app-%e5%8a%a0%e8%bd%bd%e8%bf%87%e7%a8%8b/exec.png" alt=""><br><a id="more"></a></p>
<h4 id="link-map"><a href="#link-map" class="headerlink" title="link map"></a>link map</h4><p>MachO 中重要的信息都在 Section 中。<br>可以通过 Xcode 开启 Write Link Map File = YES 后生成的 txt 文件来辅助分析 Section。可以帮助你更好的了解 App 的加载。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/app-%e5%8a%a0%e8%bd%bd%e8%bf%87%e7%a8%8b/text_text.png" alt=""></p>
<h5 id="Object-files"><a href="#Object-files" class="headerlink" title="Object files"></a>Object files</h5><p>这个部分包括的内容如下：</p>
<blockquote>
<p>.o文件，也就是.m文件编译后的结果。<br>.a文件<br>需要link的framework<br>前面是文件的编号（section中用到），后面是文件的路径。</p>
</blockquote>
<h5 id="Sections"><a href="#Sections" class="headerlink" title="Sections"></a>Sections</h5><p>这个区域提供了各个段（Segment）和节（Section）在可执行文件中的位置和大小。这个区域完整的描述克可执行文件中的全部内容，对应 MachO 的 segment 和 section<br>其中，段分为两种<br><code>__TEXT</code> 代码段<br><code>__DATA</code> 数据段<br><code>__text</code>节的地址是0x100001A50，大小是0x0002436D，二者相加的就是<code>__stubs</code>的位置0x100025DBE。</p>
<h5 id="Symbols"><a href="#Symbols" class="headerlink" title="Symbols"></a>Symbols</h5><p>Section 部分将二进制文件进行了一级划分。而，Symbols 对 Section 中的各个段进行了二级划分，</p>
<p>例如，对于<code>__TEXT __text</code>,表示代码段中的代码内容<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"># Symbols:</span><br><span class="line">地址     	大小         文件编号  方法名</span><br><span class="line"># Address	Size    	File  	Name</span><br><span class="line">0x100001A50	0x00000120	[  2] -[EasyViewController sectionSource]</span><br><span class="line">0x100001B70	0x00001720	[  2] -[EasyViewController dataSource]</span><br><span class="line">0x100003290	0x00000610	[  2] -[EasyViewController viewDidLoad]</span><br><span class="line">0x1000038A0	0x00000080	[  2] -[EasyViewController viewDidAppear:]</span><br><span class="line">0x100003920	0x00000300	[  2] -[EasyViewController viewWillAppear:]</span><br><span class="line">...</span><br><span class="line">0x100025A60	0x0000035D	[ 17] _parseSystemVersionPList</span><br><span class="line">0x100025DBE	0x00000006	[ 18] _CFRunLoopAddObserver // 这里开始是__stubs</span><br></pre></td></tr></table></figure><br><code>__Text __stubs</code></p>
<p>对于<code>__Data __objc_var</code><br>搜索 0x100036F60 可以找到如下信息<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/app-%e5%8a%a0%e8%bd%bd%e8%bf%87%e7%a8%8b/var.png" alt=""></p>
<p>我们在每次编译过后，生成的 dsym 中，就存储了16进制的函数地址映射。可以通过 MachoView 查看 SymbolString。SymbolString包含了方法段的启始地址。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/app-%e5%8a%a0%e8%bd%bd%e8%bf%87%e7%a8%8b/symbolString.png" alt=""><br><code>_DWARF __debbug_line</code>中存储了行号信息<br><code>_DWARF __debbug_info</code> 和 <code>_DWARF __debbug_frame</code> dwarf-dump –lookup 就是通过 SymbolString 和  <code>__debbug_line</code> 和 <code>_DWARF __debbug_info</code>等信息来获取崩溃信息。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/app-%e5%8a%a0%e8%bd%bd%e8%bf%87%e7%a8%8b/dwarf_line.png" alt=""><br>实际测试还需要进一步对 DWARF 格式有更多的了解，后续再说。</p>
<h4 id="dyld"><a href="#dyld" class="headerlink" title="dyld"></a>dyld</h4><p>App开始启动后，系统首先加载可执行文件 （所有 .o 文件集合）,然后加载动态链接库 dyld，dyld是一个专门用来加载动态链接库的库，递归加载所有的依赖动态链接库。<br>动态链接库包括：iOS 中用到的所有系统 framework，加载OC runtime方法的libobjc，系统级别的libSystem，CoreFoundation等。<br>系统使用动态链接的好处：</p>
<ul>
<li>代码共用：很多程序都动态链接了这些 lib，但它们在内存和磁盘中中只有一份，方便缓存。</li>
<li>易于维护：由于被依赖的 lib 是程序执行时才链接的，所以这些 lib 很容易做更新<br>dyld(the dynamic link editor), Apple 的动态链接器，所有动态链接库和我们App中的静态库.a和所有类文件编译后的.o文件最终都是由dyld(the dynamic link editor)，Apple的动态链接器来加载到内存中。每个image都是由一个叫做ImageLoader的类来负责加载（一一对应）.</li>
</ul>
<p>dyld 加载动态链接库的流程有：</p>
<ul>
<li>load dylibs image 读取库镜像文件: 分析所依赖的动态库 -&gt; 找到动态库 MachO 文件 -&gt; 读取 MachO 文件 -&gt; 通过UUID验证文件- -&gt;注册文件签名 -&gt; 调用Segment<br>启动优化: <blockquote>
<p>少非系统库的依赖<br>合并非系统库<br>使用静态库，比如把代码加入主程序</p>
</blockquote>
</li>
<li>Rebase image &amp; Bind image： ASLR(address space layout randomization 地址空间随机化，每个macho都随机了一个slide)使得可执行文件和动态链接库在虚拟内存中的加载地址每次启动都不固定，之所以需要Rebase，是因为刚刚提到的 ASLR 使得地址随机化，导致起始地址不固定，另外由于Code Sign，导致不能直接修改Image。Rebase的时候只需要增加对应的偏移量即可。待Rebase的数据都存放在<code>__LINKEDIT</code> 中。可以通过MachOView查看：Dynamic Loader Info -&gt; Rebase Info<br>rebase修复的是指向当前镜像内部的资源指针； 而bind指向的是镜像外部的资源指针。  rebase步骤先进行，需要把镜像读入内存，并以page为单位进行加密验证，保证不会被篡改，所以这一步的瓶颈在IO。bind在其后进行，由于要查询符号表，来指向跨镜像的资源，加上在rebase阶段，镜像已被读入和加密验证，所以这一步的瓶颈在于CPU计算.<br>优化：<blockquote>
<p>减少Objc类数量， 减少selector数量<br>减少C++虚函数数量<br>加载完macho和动态链接库和进行了地址修正之后,dyld所做的事情完成了大部分.</p>
</blockquote>
</li>
<li>Objc setup : dyld 回调 Objc Runtime，执行Setup<blockquote>
<p>从 <code>_DATA __objc_classlist</code> 段中获取类信息，注册Objc到一个全局的类的映射表中。<br>从 <code>_DATA __objc_protolist</code> 段中获取中获取Protocol、category等属性与类进行关联，把category的定义插入方法列表 (category registration)<br>保证每一个selector唯一 (selctor uniquing)</p>
</blockquote>
</li>
<li><p>nitializers<br>以上三步属于静态调整(fix-up)，都是在修改__DATA segment中的内容，而这里则开始动态调整，开始在堆和堆栈中写入内容。 </p>
<blockquote>
<p>执行+load方法，循环类和类扩展列表调用+load方法<br>执行c/c++初始化构造器, 如attribute((constructor)) void SomeInitializationWork()<br>初始化全局静态变量,非基本类型的C++静态全局变量的创建(通常是类或结构体)(non-trivial initializer) 比如一个全局静态结构体的构建，如果在构造函数中有繁重的工作，那么会拖慢启动速度<br>优化：<br>不是必须在 +load方法中执行的任务放到initialize中<br>减少不必要的全局静态变量</p>
</blockquote>
</li>
<li><p>通过可执行文件的 LC_MAIN ，拿到entryoff 再加上MachO的首地址（内核传来的slide偏移）就得到了main函数地址。</p>
</li>
</ul>
<p>主要流程总结</p>
<blockquote>
<p>dyld 开始将程序二进制文件初始化<br>交由 ImageLoader 读取 image，其中包含了我们的类、方法等各种符号<br>由于 runtime 向 dyld 绑定了回调，当 image 加载到内存后，dyld 会通知 runtime 进行处理<br>runtime 接手后调用 mapimages 做解析和处理，接下来 loadimages 中调用 callloadmethods 方法，遍历所有加载进来的 Class，按继承层级依次调用 Class 的 +load 方法和其 Category 的 +load 方法<br>至此，可执行文件中和动态库所有的符号(Class，Protocol，Selector，IMP，…)都已经按格式成功加载到内存中，被 runtime 所管理，再这之后，runtime 的那些方法(动态添加 Class、swizzle 等等才能生效)。整个事件由 dyld 主导，完成运行环境的初始化后，配合 ImageLoader 将二进制文件按格式加载到内存， 动态链接依赖库，并由 runtime 负责加载成 objc 定义的结构，所有初始化工作结束后，dyld 调用真正的 main 函数。</p>
</blockquote>
<p>参考文章:<br><a href="https://techblog.toutiao.com/2018/05/29/untitled-24/?from=singlemessage&amp;isappinstalled=0" target="_blank" rel="noopener">【性能优化】iOS客户端启动速度优化</a><br><a href="https://blog.csdn.net/Hello_Hwc/article/details/78317863" target="_blank" rel="noopener">深入理解iOS App的启动过程</a><br><a href="https://blog.cnbluebox.com/blog/2017/06/20/dyldyu-objc/" target="_blank" rel="noopener">dyld与ObjC</a> </p>
]]></content>
    <summary type="html">
    <![CDATA[<p>上篇文章介绍了 MachO 文件的结构，你可能注意到其中的 LC_LOAD_DYLINKER 是 dyld， LC_MAIN 加载命令就是加载程序的主入口。这篇文章就详细讲讲 App 的加载过程。</p>
<h4 id="MachO-可执行文件类型"><a href="#MachO-可执行文件类型" class="headerlink" title="MachO 可执行文件类型"></a>MachO 可执行文件类型</h4><p>Xcode build 出的 .app 包中可以看到一个 exec 可执行文件（所有 .o文件集合），同样是一个 MachO 文件，filetype 就是 MH_EXECUTE 类型。<br>MachOView中查看如下。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/app-%e5%8a%a0%e8%bd%bd%e8%bf%87%e7%a8%8b/exec.png" alt=""><br></p>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[MachO 文件结构分析]]></title>
    <link href="http://yoursite.com/2019/02/24/macho-analysis/"/>
    <id>http://yoursite.com/2019/02/24/macho-analysis/</id>
    <published>2019-02-23T17:53:35.000Z</published>
    <updated>2023-10-08T07:17:49.258Z</updated>
    <content type="html"><![CDATA[<p>上一篇文章中提到了如何自建一个 Crash 平台，其中通过对系统库 (MachO) 的结构解析来寻找崩溃符号。这篇文章就具体讲讲 MachO 文件的结构分析。</p>
<p>iOS中，我们平时看见的 MachO 文件你肯定不陌生，包括静态库(.a)、dSym (yourAppName.dSym)、系统动态库 (/usr/lib/libobjc.A.dylib)、可执行文件等。具体类型下面会讲到。</p>
<p>MachO 二进制文件可以根据前四字节的magic_num来判断是不是 Fat (包含一个或多个架构，有 Fat_Header), 每个架构同样是的 MachO文件。可以这样比喻，相当于对一个或多个文件用文件夹压缩了下。zip 包相当于 Fat，文件是 Thin。每个文件的内部结构式一致的。</p>
<h4 id="Fat"><a href="#Fat" class="headerlink" title="Fat"></a>Fat</h4><p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/fat.png" alt=""><br>可以看到 Fat 多了 Fat_Header信息, 信息中包含架构数，每个架构的基本信息。<br>Fat 可以通过lipo -thin 命令分解出 thin。 thin 也可以合并成 Fat。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">//分解</span><br><span class="line">lipo BICrashAnalyzeDemo -thin arm64 -output crashAnalyzeDemoARM64</span><br><span class="line">lipo BICrashAnalyzeDemo -thin armv7 -output crashAnalyzeDemoARMV7</span><br><span class="line">//合并</span><br><span class="line">lipo crashAnalyzeDemoARM64 crashAnalyzeDemoARMV7 -create -output BICrashAnalyzeDemo</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h4 id="Thin"><a href="#Thin" class="headerlink" title="Thin"></a>Thin</h4><p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/thin.png" alt=""><br>所以我们只需要了解Thin的 MachO 文件内部结构。<br>macho大致结构如下:<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/macho.jpg" alt=""></p>
<h4 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h4><p>macho 文件的大致信息，包含文件类型、32位还是64（MH_MAGIC_64）、架构、lc 个数，lc 大小<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/header.png" alt=""><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">struct mach_header &#123;</span><br><span class="line">	uint32_t	magic;		/* mach magic number identifier */</span><br><span class="line">	cpu_type_t	cputype;	/* cpu specifier */</span><br><span class="line">	cpu_subtype_t	cpusubtype;	/* machine specifier */</span><br><span class="line">	uint32_t	filetype;	/* type of file */</span><br><span class="line">	uint32_t	ncmds;		/* number of load commands */</span><br><span class="line">	uint32_t	sizeofcmds;	/* the size of all the load commands */</span><br><span class="line">	uint32_t	flags;		/* flags */</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/headercode.png" alt=""></p>
<ul>
<li>magic：MachO文件的魔数，用来确定其属于64位（0xfeedfacf/MH_MAGIC_64）还是32位（0xfeedface/MH_MAGIC），分别对应的是arm64和 armv7的Header。</li>
<li>cputype和cupsubtype代表的是cpu的类型和其子类型，定义如下：<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">#define CPU_TYPE_ARM ((cpu_type_t)12)</span><br><span class="line">#define CPU_SUBTYPE_ARM_V6 ((cpu_subtype_t)6)</span><br><span class="line">#define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t)9)</span><br><span class="line">#define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t)11)</span><br><span class="line"></span><br><span class="line">#define CPU_TYPE_ARM64 ((cpu_type_t)16777228)</span><br><span class="line">#define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t)0)</span><br><span class="line"></span><br><span class="line">#define CPU_TYPE_I386 ((cpu_type_t)7)</span><br><span class="line">#define CPU_SUBTYPE_X86_ALL ((cpu_subtype_t)3)</span><br><span class="line"></span><br></pre></td></tr></table></figure></li>
<li><p>filetype 上面提到的 filetype，例子中为 dSym<br>可以看到苹果源文件中包含的所有类型。类型包含在 MachO 的 mach_header(_64) 的filetype字段</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"> *</span><br><span class="line"> * Constants for the filetype field of the mach_header</span><br><span class="line"> */</span><br><span class="line">#define	MH_OBJECT	0x1		/* relocatable object file */可重定位的目标文件，编译器对源代码编译得到的中间结果。如gcc-c生成的</span><br><span class="line">#define	MH_EXECUTE	0x2		/* demand paged executable file */ 可执行文件(应用程序生成的二进制文件)，</span><br><span class="line">#define	MH_FVMLIB	0x3		/* fixed VM shared library file */</span><br><span class="line">#define	MH_CORE		0x4		/* core file */</span><br><span class="line">#define	MH_PRELOAD	0x5		/* preloaded executable file */</span><br><span class="line">#define	MH_DYLIB	0x6		/* dynamically bound shared library */ 动态库</span><br><span class="line">#define	MH_DYLINKER	0x7		/* dynamic link editor */ 动态链接库</span><br><span class="line">#define	MH_BUNDLE	0x8		/* dynamically bound bundle file */</span><br><span class="line">#define	MH_DYLIB_STUB	0x9		/* shared library stub for static */ </span><br><span class="line">					/*  linking only, no section contents */</span><br><span class="line">#define	MH_DSYM		0xa		/* companion file with only debug */ dSym 文件 gcc-g生成</span><br><span class="line">					/*  sections */</span><br><span class="line">#define	MH_KEXT_BUNDLE	0xb		/* x86_64 kexts */ 64位内核扩展</span><br></pre></td></tr></table></figure>
</li>
<li><p>ncmds  load command 个数</p>
</li>
<li>sizeofcmds 所有 load command 大小</li>
<li>flags 执行相关的一些设置，用途如下<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line">#define	MH_NOUNDEFS	0x1		/* the object file has no undefined</span><br><span class="line">					   references */</span><br><span class="line">#define	MH_INCRLINK	0x2		/* the object file is the output of an</span><br><span class="line">					   incremental link against a base file</span><br><span class="line">					   and can&apos;t be link edited again */</span><br><span class="line">#define MH_DYLDLINK	0x4		/* the object file is input for the</span><br><span class="line">					   dynamic linker and can&apos;t be staticly</span><br><span class="line">					   link edited again */</span><br><span class="line">#define MH_BINDATLOAD	0x8		/* the object file&apos;s undefined</span><br><span class="line">					   references are bound by the dynamic</span><br><span class="line">					   linker when loaded. */</span><br><span class="line">#define MH_PREBOUND	0x10		/* the file has its dynamic undefined</span><br><span class="line">					   references prebound. */</span><br><span class="line">#define MH_SPLIT_SEGS	0x20		/* the file has its read-only and</span><br><span class="line">					   read-write segments split */</span><br><span class="line">#define MH_LAZY_INIT	0x40		/* the shared library init routine is</span><br><span class="line">					   to be run lazily via catching memory</span><br><span class="line">					   faults to its writeable segments</span><br><span class="line">					   (obsolete) */</span><br><span class="line">#define MH_TWOLEVEL	0x80		/* the image is using two-level name</span><br><span class="line">					   space bindings */</span><br><span class="line">#define MH_FORCE_FLAT	0x100		/* the executable is forcing all images</span><br><span class="line">					   to use flat name space bindings */</span><br><span class="line">#define MH_NOMULTIDEFS	0x200		/* this umbrella guarantees no multiple</span><br><span class="line">					   defintions of symbols in its</span><br><span class="line">					   sub-images so the two-level namespace</span><br><span class="line">					   hints can always be used. */</span><br><span class="line">#define MH_NOFIXPREBINDING 0x400	/* do not have dyld notify the</span><br><span class="line">					   prebinding agent about this</span><br><span class="line">					   executable */</span><br><span class="line">#define MH_PREBINDABLE  0x800           /* the binary is not prebound but can</span><br><span class="line">					   have its prebinding redone. only used</span><br><span class="line">                                           when MH_PREBOUND is not set. */</span><br><span class="line">#define MH_ALLMODSBOUND 0x1000		/* indicates that this binary binds to</span><br><span class="line">                                           all two-level namespace modules of</span><br><span class="line">					   its dependent libraries. only used</span><br><span class="line">					   when MH_PREBINDABLE and MH_TWOLEVEL</span><br><span class="line">					   are both set. */ </span><br><span class="line">#define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000/* safe to divide up the sections into</span><br><span class="line">					    sub-sections via symbols for dead</span><br><span class="line">					    code stripping */</span><br><span class="line">#define MH_CANONICAL    0x4000		/* the binary has been canonicalized</span><br><span class="line">					   via the unprebind operation */</span><br><span class="line">#define MH_WEAK_DEFINES	0x8000		/* the final linked image contains</span><br><span class="line">					   external weak symbols */</span><br><span class="line">#define MH_BINDS_TO_WEAK 0x10000	/* the final linked image uses</span><br><span class="line">					   weak symbols */</span><br><span class="line"></span><br><span class="line">#define MH_ALLOW_STACK_EXECUTION 0x20000/* When this bit is set, all stacks </span><br><span class="line">					   in the task will be given stack</span><br><span class="line">					   execution privilege.  Only used in</span><br><span class="line">					   MH_EXECUTE filetypes. */</span><br><span class="line">#define MH_ROOT_SAFE 0x40000           /* When this bit is set, the binary </span><br><span class="line">					  declares it is safe for use in</span><br><span class="line">					  processes with uid zero */</span><br><span class="line">                                         </span><br><span class="line">#define MH_SETUID_SAFE 0x80000         /* When this bit is set, the binary </span><br><span class="line">					  declares it is safe for use in</span><br><span class="line">					  processes when issetugid() is true */</span><br><span class="line"></span><br><span class="line">#define MH_NO_REEXPORTED_DYLIBS 0x100000 /* When this bit is set on a dylib, </span><br><span class="line">					  the static linker does not need to</span><br><span class="line">					  examine dependent dylibs to see</span><br><span class="line">					  if any are re-exported */</span><br><span class="line">#define	MH_PIE 0x200000			/* When this bit is set, the OS will</span><br><span class="line">					   load the main executable at a</span><br><span class="line">					   random address.  Only used in</span><br><span class="line">					   MH_EXECUTE filetypes. */</span><br><span class="line">#define	MH_DEAD_STRIPPABLE_DYLIB 0x400000 /* Only for use on dylibs.  When</span><br><span class="line">					     linking against a dylib that</span><br><span class="line">					     has this bit set, the static linker</span><br><span class="line">					     will automatically not create a</span><br><span class="line">					     LC_LOAD_DYLIB load command to the</span><br><span class="line">					     dylib if no symbols are being</span><br><span class="line">					     referenced from the dylib. */</span><br><span class="line">#define MH_HAS_TLV_DESCRIPTORS 0x800000 /* Contains a section of type </span><br><span class="line">					    S_THREAD_LOCAL_VARIABLES */</span><br><span class="line"></span><br><span class="line">#define MH_NO_HEAP_EXECUTION 0x1000000	/* When this bit is set, the OS will</span><br><span class="line">					   run the main executable with</span><br><span class="line">					   a non-executable heap even on</span><br><span class="line">					   platforms (e.g. i386) that don&apos;t</span><br><span class="line">					   require it. Only used in MH_EXECUTE</span><br><span class="line">					   filetypes. */</span><br></pre></td></tr></table></figure>
用途<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/flags.jpg" alt=""></li>
</ul>
<h4 id="Load-Commands"><a href="#Load-Commands" class="headerlink" title="Load Commands"></a>Load Commands</h4><p>用于告诉loader如何设置并加载二进制数据<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">//基本信息</span><br><span class="line">struct load_command &#123;</span><br><span class="line">	uint32_t cmd;		/* type of load command */</span><br><span class="line">	uint32_t cmdsize;	/* total size of command in bytes */</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><br>cmd类型有很多，查看 loader.h中的定义<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * After MacOS X 10.1 when a new load command is added that is required to be</span><br><span class="line"> * understood by the dynamic linker for the image to execute properly the</span><br><span class="line"> * LC_REQ_DYLD bit will be or&apos;ed into the load command constant.  If the dynamic</span><br><span class="line"> * linker sees such a load command it it does not understand will issue a</span><br><span class="line"> * &quot;unknown load command required for execution&quot; error and refuse to use the</span><br><span class="line"> * image.  Other load commands without this bit that are not understood will</span><br><span class="line"> * simply be ignored.</span><br><span class="line"> */</span><br><span class="line">#define LC_REQ_DYLD 0x80000000</span><br><span class="line"></span><br><span class="line">/* Constants for the cmd field of all load commands, the type */</span><br><span class="line">#define	LC_SEGMENT	0x1	/* segment of this file to be mapped */ </span><br><span class="line">#define	LC_SYMTAB	0x2	/* link-edit stab symbol table info */</span><br><span class="line">#define	LC_SYMSEG	0x3	/* link-edit gdb symbol table info (obsolete) */</span><br><span class="line">#define	LC_THREAD	0x4	/* thread */</span><br><span class="line">#define	LC_UNIXTHREAD	0x5	/* unix thread (includes a stack) */</span><br><span class="line">#define	LC_LOADFVMLIB	0x6	/* load a specified fixed VM shared library */</span><br><span class="line">#define	LC_IDFVMLIB	0x7	/* fixed VM shared library identification */</span><br><span class="line">#define	LC_IDENT	0x8	/* object identification info (obsolete) */</span><br><span class="line">#define LC_FVMFILE	0x9	/* fixed VM file inclusion (internal use) */</span><br><span class="line">#define LC_PREPAGE      0xa     /* prepage command (internal use) */</span><br><span class="line">#define	LC_DYSYMTAB	0xb	/* dynamic link-edit symbol table info */</span><br><span class="line">#define	LC_LOAD_DYLIB	0xc	/* load a dynamically linked shared library */</span><br><span class="line">#define	LC_ID_DYLIB	0xd	/* dynamically linked shared lib ident */</span><br><span class="line">#define LC_LOAD_DYLINKER 0xe	/* load a dynamic linker */</span><br><span class="line">#define LC_ID_DYLINKER	0xf	/* dynamic linker identification */</span><br><span class="line">#define	LC_PREBOUND_DYLIB 0x10	/* modules prebound for a dynamically */</span><br><span class="line">				/*  linked shared library */</span><br><span class="line">#define	LC_ROUTINES	0x11	/* image routines */</span><br><span class="line">#define	LC_SUB_FRAMEWORK 0x12	/* sub framework */</span><br><span class="line">#define	LC_SUB_UMBRELLA 0x13	/* sub umbrella */</span><br><span class="line">#define	LC_SUB_CLIENT	0x14	/* sub client */</span><br><span class="line">#define	LC_SUB_LIBRARY  0x15	/* sub library */</span><br><span class="line">#define	LC_TWOLEVEL_HINTS 0x16	/* two-level namespace lookup hints */</span><br><span class="line">#define	LC_PREBIND_CKSUM  0x17	/* prebind checksum */</span><br><span class="line"></span><br><span class="line">/*</span><br><span class="line"> * load a dynamically linked shared library that is allowed to be missing</span><br><span class="line"> * (all symbols are weak imported).</span><br><span class="line"> */</span><br><span class="line">#define	LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)</span><br><span class="line"></span><br><span class="line">#define	LC_SEGMENT_64	0x19	/* 64-bit segment of this file to be</span><br><span class="line">				   mapped */</span><br><span class="line">#define	LC_ROUTINES_64	0x1a	/* 64-bit image routines */</span><br><span class="line">#define LC_UUID		0x1b	/* the uuid */</span><br><span class="line">#define LC_RPATH       (0x1c | LC_REQ_DYLD)    /* runpath additions */</span><br><span class="line">#define LC_CODE_SIGNATURE 0x1d	/* local of code signature */</span><br><span class="line">#define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */</span><br><span class="line">#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */</span><br><span class="line">#define	LC_LAZY_LOAD_DYLIB 0x20	/* delay load of dylib until first use */</span><br><span class="line">#define	LC_ENCRYPTION_INFO 0x21	/* encrypted segment information */</span><br><span class="line">#define	LC_DYLD_INFO 	0x22	/* compressed dyld information */</span><br><span class="line">#define	LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD)	/* compressed dyld information only */</span><br><span class="line">#define	LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */</span><br><span class="line">#define LC_VERSION_MIN_MACOSX 0x24   /* build for MacOSX min OS version */</span><br><span class="line">#define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */</span><br><span class="line">#define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */</span><br><span class="line">#define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat</span><br><span class="line">				    like environment variable */</span><br><span class="line">#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */</span><br><span class="line">#define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */</span><br><span class="line">#define LC_SOURCE_VERSION 0x2A /* source version used to build binary */</span><br><span class="line">#define LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */</span><br></pre></td></tr></table></figure><br>常用的有<br>LC_UUID：确定文件的唯一标识，crash reporter 中的 Images 中也会有这个，去检测 dsym 文件和 crash 文件是否匹配，系统库是否找的正确。<br>LC_SEGMENT_64： 将该段(64位)映射到进程地址空间中<br>LC_SEGMENT： 将该段(32位)映射到进程地址空间中<br>LC_SYMTAB：载入符号表地址,可以通过崩溃地址找到崩溃符号（方法）。<br>LC_DYSYMTAB：载入动态符号表地址<br>LC_LOAD_DYLINKER：load_dylinker, 调用dyld（usr/lib/dyld） 动态连接器加载动态库<br>LC_VERSION_MIN_MACOSX/LC_VERSION_MIN_IPHONEOS：确定二进制文件要求的最低操作系统版本<br>LC_SOURCE_VERSION：构建该二进制文件使用的源代码版本<br>LC_MAIN：设置程序主线程的入口地址和栈大小, 在可执行文件中。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * The entry_point_command is a replacement for thread_command.</span><br><span class="line"> * It is used for main executables to specify the location (file offset)</span><br><span class="line"> * of main().  If -stack_size was used at link time, the stacksize</span><br><span class="line"> * field will contain the stack size need for the main thread.</span><br><span class="line"> */</span><br><span class="line">struct entry_point_command &#123;</span><br><span class="line">    uint32_t  cmd;	/* LC_MAIN only used in MH_EXECUTE filetypes */</span><br><span class="line">    uint32_t  cmdsize;	/* 24 */</span><br><span class="line">    uint64_t  entryoff;	/* file (__TEXT) offset of main() */</span><br><span class="line">    uint64_t  stacksize;/* if not zero, initial stack size */</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><br>LC_ENCRYPTION_INFO_64：获取加密信息<br>LC_LOAD_DYLIB：加载额外的动态库路径，包含三方库<br>LC_FUNCTION_STARTS：定义一个函数起始地址表,使调试器和其他程序易于看到一个地址是否在函数内<br>LC_DATA_IN_CODE：定义在代码段内的非指令的表<br>LC_ID_DYLIB：只在 dylib 中加载，指定了 dylib的ID，版本和兼容版本<br>LC_CODE_SIGNATURE：获取应用签名信息<br>LC_DYLD_INFO_ONLY：加载动态链接库信息（重定向地址、弱引用绑定、懒加载绑定、开放函数等的偏移值等信息）<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * The dyld_info_command contains the file offsets and sizes of </span><br><span class="line"> * the new compressed form of the information dyld needs to </span><br><span class="line"> * load the image.  This information is used by dyld on Mac OS X</span><br><span class="line"> * 10.6 and later.  All information pointed to by this command</span><br><span class="line"> * is encoded using byte streams, so no endian swapping is needed</span><br><span class="line"> * to interpret it. </span><br><span class="line"> */</span><br><span class="line">struct dyld_info_command &#123;</span><br><span class="line">   uint32_t   cmd;		/* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */</span><br><span class="line">   uint32_t   cmdsize;		/* sizeof(struct dyld_info_command) */</span><br><span class="line"></span><br><span class="line">    uint32_t   rebase_off;	/* file offset to rebase info  */</span><br><span class="line">    uint32_t   rebase_size;	/* size of rebase info   */</span><br><span class="line">   </span><br><span class="line">    uint32_t   bind_off;	/* file offset to binding info   */</span><br><span class="line">    uint32_t   bind_size;	/* size of binding info  */</span><br><span class="line">    </span><br><span class="line">    uint32_t   weak_bind_off;	/* file offset to weak binding info   */</span><br><span class="line">    uint32_t   weak_bind_size;  /* size of weak binding info  */</span><br><span class="line">    </span><br><span class="line">    uint32_t   lazy_bind_off;	/* file offset to lazy binding info */</span><br><span class="line">    uint32_t   lazy_bind_size;  /* size of lazy binding infs */</span><br><span class="line">    </span><br><span class="line">    uint32_t   export_off;	/* file offset to lazy binding info */</span><br><span class="line">    uint32_t   export_size;	/* size of lazy binding infs */</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/dyld-info.png" alt=""></p>
<h4 id="Data"><a href="#Data" class="headerlink" title="Data"></a>Data</h4><p>这个区域提供了各个段（Segment）和节（Section）在可执行文件中的位置和大小。这个区域完整的描述克可执行文件中的全部内容。</p>
<blockquote>
<p>存放数据：代码、字符常量、类、方法等<br>可以拥有多个segment，每个segment可以有零到多个section。每个段都有一段虚拟地址映射到进程的地址空间</p>
</blockquote>
<p>segment</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * The segment load command indicates that a part of this file is to be</span><br><span class="line"> * mapped into the task&apos;s address space.  The size of this segment in memory,</span><br><span class="line"> * vmsize, maybe equal to or larger than the amount to map from this file,</span><br><span class="line"> * filesize.  The file is mapped starting at fileoff to the beginning of</span><br><span class="line"> * the segment in memory, vmaddr.  The rest of the memory of the segment,</span><br><span class="line"> * if any, is allocated zero fill on demand.  The segment&apos;s maximum virtual</span><br><span class="line"> * memory protection and initial virtual memory protection are specified</span><br><span class="line"> * by the maxprot and initprot fields.  If the segment has sections then the</span><br><span class="line"> * section structures directly follow the segment command and their size is</span><br><span class="line"> * reflected in cmdsize.</span><br><span class="line"> */</span><br><span class="line">struct segment_command &#123; /* for 32-bit architectures */</span><br><span class="line">	uint32_t	cmd;		/* LC_SEGMENT */</span><br><span class="line">	uint32_t	cmdsize;	/* includes sizeof section structs */</span><br><span class="line">	char		segname[16];	/* segment name */</span><br><span class="line">	uint32_t	vmaddr;		/* memory address of this segment */</span><br><span class="line">	uint32_t	vmsize;		/* memory size of this segment */</span><br><span class="line">	uint32_t	fileoff;	/* file offset of this segment */</span><br><span class="line">	uint32_t	filesize;	/* amount to map from the file */</span><br><span class="line">	vm_prot_t	maxprot;	/* maximum VM protection */</span><br><span class="line">	vm_prot_t	initprot;	/* initial VM protection */</span><br><span class="line">	uint32_t	nsects;		/* number of sections in segment */</span><br><span class="line">	uint32_t	flags;		/* flags */</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">/*</span><br><span class="line"> * The 64-bit segment load command indicates that a part of this file is to be</span><br><span class="line"> * mapped into a 64-bit task&apos;s address space.  If the 64-bit segment has</span><br><span class="line"> * sections then section_64 structures directly follow the 64-bit segment</span><br><span class="line"> * command and their size is reflected in cmdsize.</span><br><span class="line"> */</span><br><span class="line">struct segment_command_64 &#123; /* for 64-bit architectures */</span><br><span class="line">	uint32_t	cmd;		/* LC_SEGMENT_64 */</span><br><span class="line">	uint32_t	cmdsize;	/* includes sizeof section_64 structs */</span><br><span class="line">	char		segname[16];	/* segment name */</span><br><span class="line">	uint64_t	vmaddr;		/* memory address of this segment */ 段的虚拟内存地址</span><br><span class="line">	uint64_t	vmsize;		/* memory size of this segment */ 为这个段分配的虚拟内存大小</span><br><span class="line">	uint64_t	fileoff;	/* file offset of this segment */ 段在文件中起始地址</span><br><span class="line">	uint64_t	filesize;	/* amount to map from the file */ 段大小</span><br><span class="line">	vm_prot_t	maxprot;	/* maximum VM protection */</span><br><span class="line">	vm_prot_t	initprot;	/* initial VM protection */</span><br><span class="line">	uint32_t	nsects;		/* number of sections in segment */ section个数</span><br><span class="line">	uint32_t	flags;		/* flags */ 标志位</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>对于每一个段，将文件中相对应的内容加载到内存中：从偏移量为fileoff处加载filesize字节到虚拟内存地址vmaddr处的vmsize字节。每一个段的页面都根据initprot进行初始化，initprot指定了如何通过读／写／执行位初始化页面保护级别。段的保护设置可以动态改变，但是不能超过maxprot中指定的值（iOS中，+x 和+w 是互斥的）。</p>
<p>section<br>section分为两种</p>
<blockquote>
<p><strong>TEXT 代码段
</strong>DATA 数据段</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * A segment is made up of zero or more sections. </span><br><span class="line"> */</span><br><span class="line">struct section &#123; /* for 32-bit architectures */</span><br><span class="line">	char		sectname[16];	/* name of this section */</span><br><span class="line">	char		segname[16];	/* segment this section goes in */</span><br><span class="line">	uint32_t	addr;		/* memory address of this section */</span><br><span class="line">	uint32_t	size;		/* size in bytes of this section */</span><br><span class="line">	uint32_t	offset;		/* file offset of this section */</span><br><span class="line">	uint32_t	align;		/* section alignment (power of 2) */</span><br><span class="line">	uint32_t	reloff;		/* file offset of relocation entries */</span><br><span class="line">	uint32_t	nreloc;		/* number of relocation entries */</span><br><span class="line">	uint32_t	flags;		/* flags (section type and attributes)*/</span><br><span class="line">	uint32_t	reserved1;	/* reserved (for offset or index) */</span><br><span class="line">	uint32_t	reserved2;	/* reserved (for count or sizeof) */</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">struct section_64 &#123; /* for 64-bit architectures */</span><br><span class="line">	char		sectname[16];	/* name of this section */</span><br><span class="line">	char		segname[16];	/* segment this section goes in */</span><br><span class="line">	uint64_t	addr;		/* memory address of this section */</span><br><span class="line">	uint64_t	size;		/* size in bytes of this section */</span><br><span class="line">	uint32_t	offset;		/* file offset of this section */</span><br><span class="line">	uint32_t	align;		/* section alignment (power of 2) */</span><br><span class="line">	uint32_t	reloff;		/* file offset of relocation entries */</span><br><span class="line">	uint32_t	nreloc;		/* number of relocation entries */</span><br><span class="line">	uint32_t	flags;		/* flags (section type and attributes)*/</span><br><span class="line">	uint32_t	reserved1;	/* reserved (for offset or index) */</span><br><span class="line">	uint32_t	reserved2;	/* reserved (for count or sizeof) */</span><br><span class="line">	uint32_t	reserved3;	/* reserved */</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>常见section作用<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/section_use.png" alt=""></p>
<ul>
<li>每个 section 的起始位置 + 大小就是下个 section 的启始位置</li>
<li>cstring 包含了所有OC的硬编码字符串，nslog 中的字符串也在cstring中，但是不包含含中文的字符串，这些字符串显示的存储在数据段中。不过同样的字符只会存储一次。</li>
<li>ustring 中包含了带中文的硬编码字符串，其他同cstring。</li>
<li><code>__stubs</code>会到 <code>__DATA</code> 段的<code>__la_symbol_ptr</code>中找到函数的入口地址。</li>
</ul>
<h4 id="Loader-Info-（链接信息）"><a href="#Loader-Info-（链接信息）" class="headerlink" title="Loader Info （链接信息）"></a>Loader Info （链接信息）</h4><p>一个完整的用户级 MachO 文件的末端是一系列链接信息。其中包含了动态加载器用来链接可执行文件或者依赖所需使用的符号表、字符串表、dynamic loader info（地址修正，bind）。</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>上一篇文章中提到了如何自建一个 Crash 平台，其中通过对系统库 (MachO) 的结构解析来寻找崩溃符号。这篇文章就具体讲讲 MachO 文件的结构分析。</p>
<p>iOS中，我们平时看见的 MachO 文件你肯定不陌生，包括静态库(.a)、dSym (yourAppName.dSym)、系统动态库 (/usr/lib/libobjc.A.dylib)、可执行文件等。具体类型下面会讲到。</p>
<p>MachO 二进制文件可以根据前四字节的magic_num来判断是不是 Fat (包含一个或多个架构，有 Fat_Header), 每个架构同样是的 MachO文件。可以这样比喻，相当于对一个或多个文件用文件夹压缩了下。zip 包相当于 Fat，文件是 Thin。每个文件的内部结构式一致的。</p>
<h4 id="Fat"><a href="#Fat" class="headerlink" title="Fat"></a>Fat</h4><p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/macho-analysis/fat.png" alt=""><br>可以看到 Fat 多了 Fat_Header信息, 信息中包含架构数，每个架构的基本信息。<br>Fat 可以通过lipo -thin 命令分解出 thin。 thin 也可以合并成 Fat。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">//分解</span><br><span class="line">lipo BICrashAnalyzeDemo -thin arm64 -output crashAnalyzeDemoARM64</span><br><span class="line">lipo BICrashAnalyzeDemo -thin armv7 -output crashAnalyzeDemoARMV7</span><br><span class="line">//合并</span><br><span class="line">lipo crashAnalyzeDemoARM64 crashAnalyzeDemoARMV7 -create -output BICrashAnalyzeDemo</span><br></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[如何自建 Crash 平台]]></title>
    <link href="http://yoursite.com/2019/02/20/iOS-crash-report/"/>
    <id>http://yoursite.com/2019/02/20/iOS-crash-report/</id>
    <published>2019-02-20T14:54:15.000Z</published>
    <updated>2023-10-08T07:17:36.971Z</updated>
    <content type="html"><![CDATA[<p>【译】<a href="https://www.apteligent.com/technical-resource/symbolicating-an-ios-crash-report/?partner_code=GDC_so_symbolicateios" target="_blank" rel="noopener">Symbolicating an iOS Crash Report</a></p>
<p>通常，当您收到来自iTunes连接的崩溃报告或提供移动崩溃收集和报告的第三方服务（如Apteligent）时，该服务将负责为您提供符号化后的崩溃。如果你没有上传符号，你可能会发现自己有一个非符号化的崩溃，没有别的东西可以继续。这样的崩溃文件对于调试可能影响大量用户的问题并不是非常有用。</p>
<p>在这种情况下，您必须通过将回溯堆栈地址解析为符号来对崩溃报告进行符号化，以获取有关崩溃的有用信息。</p>
<p>幸运的是，完全有可能手动符号化崩溃报告。本文将概述您需要的信息，向您展示如何解释崩溃报告，并查看OSX和XCode上可用的一些工具来符号化崩溃。</p>
<p>崩溃报告中只有两个部分与符号化异常跟踪相关。第一个是 Exception Backtrace 部分。这显示了崩溃时应用程序的调用堆栈。此特定崩溃日志片段显示了我们的 ApteligentExampleApp 应用程序内部崩溃的回溯。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Last Exception Backtrace:</span><br><span class="line">0   CoreFoundation 0x000000018708b100 0x186f80000 + 1093888</span><br><span class="line">1   libobjc.A.dylib            0x00000001939441fc 0x19393c000 + 33276</span><br><span class="line">2   CoreFoundation 0x000000018708b040 0x186f80000 + 1093696</span><br><span class="line">3   ApteligentExampleApp 0x000000010003acc4 0x10002c000 + 60612</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>第二部分是崩溃报告底部的 Binary Images 部分，它为您提供了更多有用的信息。 本节列出了崩溃时加载的二进制文件。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Binary Images:</span><br><span class="line">0x10002c000 - 0x1000dffff ApteligentExampleApp arm64  &lt;3759a98e880336108b1a799afa3c1adc&gt; /var/mobile/Applications/46FB38F8-0E69-459F-B96A-CEEA21B77D55/ApteligentExampleApp.app/ApteligentExampleApp</span><br></pre></td></tr></table></figure></p>
<p>不幸的是，这个崩溃报告是未符号化的。 我们可以看到我们的应用程序崩溃的点（异常回溯中的第3行），但它缺少有助于开发人员调试问题的函数名称，函数参数和行号等详细信息。 为了将崩溃报告中的各种地址转换为可读的地址，我们需要将这些地址映射到符号。 为此，我们需要调试符号（dSYM文件）和符号化工具以及从崩溃报告本身收集的信息。</p>
<h3 id="收集所需信息"><a href="#收集所需信息" class="headerlink" title="收集所需信息"></a>收集所需信息</h3><p>这是一个需要符号化的示例行。 我们知道这是程序失败的重点，但地址本身并没有告诉我们任何有用的东西。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">3 ApteligentExampleApp 0x000000010003acc4  0x10002c000 + 60612</span><br></pre></td></tr></table></figure></p>
<p>异常回溯中的这一行为您提供堆栈地址，应用程序虚拟内存中的二进制加载地址以及偏移量。 最后一个值只是堆栈地址和加载地址之间的差异。<br>更靠近崩溃报告的底部，您将看到 Binary Images 部分。 通常，崩溃的应用程序将位于列表的顶部。 此条目将为您提供加载地址（再次），此崩溃的dSYM UUID以及应用程序崩溃的系统体系结构。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Binary Images:</span><br><span class="line">0x10002c000 - 0x1000dffff +ApteligentExampleApp arm64 &lt;3759a98e880336108b1a799afa3c1adc&gt;</span><br></pre></td></tr></table></figure>
<p>现在我们可以收集所有需要的东西来符号化这一行。 使用此数据，您可以符号化特定崩溃中的任何堆栈地址。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%88%9B%E5%BB%BAcrash%E5%B9%B3%E5%8F%B0/symbollist.png" alt=""></p>
<h3 id="验证符号文件"><a href="#验证符号文件" class="headerlink" title="验证符号文件"></a>验证符号文件</h3><p>dSYM 文件是一个 ELF 文件，其中包含应用程序的 DWARF 调试信息（以及其他内容）。 如果在XCode中设置了“带有 dSYM 文件的 DWARF ”选项，则编译器会生成 dSYM 文件，并将其存储在您的构建中。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%88%9B%E5%BB%BAcrash%E5%B9%B3%E5%8F%B0/dsym.png" alt=""></p>
<p>如果要表示特定的崩溃，则需要找到匹配的 dSYM 文件。 最好使用某种归档机制将每个版本的 dSYM 和应用程序二进制文件存储到应用商店，因为将崩溃的 dSYM UUID 与正确的 dSYM 文件进行匹配非常重要。 如果 UUID 不完全匹配，则符号化结果不可靠。 崩溃报告将告诉您符号化所需的 dSYM。 如果您不确定您的 dSYM 是否与崩溃匹配，您可以使用 dwarfdump 检查 UUID。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">dwarfdump -u ApteligentExampleApp.dSYM</span><br><span class="line">UUID: 3759A98E-8803-3610-8B1A-799AFA3C1ADC (arm64) </span><br><span class="line">ApteligentExampleApp.dSYM</span><br></pre></td></tr></table></figure>
<h3 id="获取偏移量"><a href="#获取偏移量" class="headerlink" title="获取偏移量"></a>获取偏移量</h3><p>对于某些工具，您可能需要提供偏移量而不是加载地址。 如果给它一个加载地址（0x10002c000）和一个堆栈地址（0x10003acc4），ATOS 将为你处理偏移计算。 但是，dwarfdump 和 lldb 采用文件地址（0x10000ECC4），因此您需要考虑为这些工具设置偏移量。<br>从 dSYM 获取偏移量的一种方法是使用“otool”，它可以与 OSX 上的 XCode 开发人员工具一起使用。<br>您需要查找 LC_SEGMENT_64（arm64）或 LC_SEGMENT（armv7，armv7s）段和“vmaddr”条目。 对于iOS，对于32位通常为0x4000，对于64位架构通常为0x100000000，但这可能会发生变化。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">otool -l ApteligentExampleApp.dSYM &gt; ApteligentExampleApp.otool.output</span><br></pre></td></tr></table></figure></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Load command 3</span><br><span class="line">cmd LC_SEGMENT_64</span><br><span class="line">cmdsize 1032</span><br><span class="line">segname __TEXT</span><br><span class="line">vmaddr 0x0000000100000000</span><br></pre></td></tr></table></figure>
<p>现在我们已经编译好了所有需要的信息，可以开始符号化堆栈了.</p>
<h3 id="符号化崩溃报告"><a href="#符号化崩溃报告" class="headerlink" title="符号化崩溃报告"></a>符号化崩溃报告</h3><h4 id="使用-ATOS-符号化。"><a href="#使用-ATOS-符号化。" class="headerlink" title="使用 ATOS 符号化。"></a>使用 ATOS 符号化。</h4><p>ATOS是Apple的控制台符号工具。 它将数字地址转换为二进制图像中的符号化字符串。</p>
<p>这是用于在OSX上快速获取符号化输出的最简单工具。 现在您已经从崩溃文件中收集了所有信息，您只需要插入地址并构建信息，您就应该获得符号化的行。 ATOS可以一次处理多个地址，因此如果您愿意，可以从堆栈跟踪中输入每个堆栈地址。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">atos -arch &lt;architecture&gt; -o &lt;binary filename&gt; -l &lt;load address&gt; &lt;stack address 1&gt; &lt;stack address 2&gt; ...</span><br><span class="line">对应</span><br><span class="line">atos -arch arm64 -o ApteligentExampleApp.dSYM -l 0x10002c000 0x000000010003acc4</span><br><span class="line">-[ApteligentExampleClass buggyFunction] (in ApteligentExampleApp.dSYM) (ApteligentExampleClass.m:181)</span><br></pre></td></tr></table></figure></p>
<h4 id="使用-lldb-符号化"><a href="#使用-lldb-符号化" class="headerlink" title="使用 lldb 符号化"></a>使用 lldb 符号化</h4><p>lldb 是 OSX 上 XCode 中的默认调试器，可用于表示崩溃中的行。 lldb 再次包含在 OSX 的XCode 中，还有适用于 Linux，FreeBSD 和 Windows 的端口。 您可以从 <a href="http://lldb.llvm.org/download.html" target="_blank" rel="noopener">llvm 项目站点</a> 获取它。</p>
<p>如果您想了解有关lldb可以执行的操作的更多信息，<a href="http://lldb.llvm.org/symbolication.html" target="_blank" rel="noopener">可以点击此处阅读</a>。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">(lldb) target create --arch arm64 ApteligentExampleApp.dSYM</span><br><span class="line">Current executable set to ApteligentExampleApp.dSYM&apos; (arm64).</span><br><span class="line">(lldb) image lookup --address 0x10000ECC4</span><br><span class="line">Address: ApteligentExampleApp.dSYM[0x000000010000ecc4] (ApteligentExampleApp.dSYM.__TEXT.__text + 29916)</span><br><span class="line">Summary: ApteligentExampleApp.dSYM`-[ApteligentExampleClass buggyFunction] + 68 at ApteligentExampleClass.m:181</span><br></pre></td></tr></table></figure>
<h4 id="使用-Dwarfdump-符号化"><a href="#使用-Dwarfdump-符号化" class="headerlink" title="使用 Dwarfdump 符号化"></a>使用 Dwarfdump 符号化</h4><p>Dwarfdump 是一个从 EL F对象转储 DWARF 调试信息的实用程序 - 对于 iOS，这通常是一个dSYM 文件。 Dwarfdump 是一个非常冗长的工具，通常用于调试 DWARF 调试信息生成器（如XCode 中的编译器）或验证符号化工具（如 ATOS）的输出。为了简单地表示崩溃转储，使用此工具是完全矫枉过正的，但有时您可能想要进一步深入了解DWARF调试信息。<br>Dwarfdump 使用文件地址（File Address 0x10000ECC4）来定位匹配的子程序“Debug Information Entry”。此条目具有 DWARF 属性，可为您提供有关子程序的信息。对于 Objective C，此子程序条目通常表示类中的函数，您可以获取行号，文件名以及类/函数名称等信息。<br>Dwarfdump 的一个版本随 XCode 开发人员工具一起提供，但您也可以从 <a href="https://www.prevanders.net/dwarf.html" target="_blank" rel="noopener">libdwarf 项目页面</a> 获取它.<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">dwarfdump --lookup 0x10000ECC4 --arch arm64 ApteligentExampleApp.dSYM</span><br><span class="line">----------------------------------------------------------------------</span><br><span class="line">File: ApteligentExampleApp.dSYM (arm64)</span><br><span class="line">----------------------------------------------------------------------</span><br><span class="line">Looking up address: 0x000000010000ecc4 in .debug_info... found!</span><br><span class="line"></span><br><span class="line">0x000516d2: Compile Unit: length = 0x00000e9f  version = 0x0002  abbr_offset = 0x00000000  addr_size = 0x08  (next CU at 0x00052575)</span><br><span class="line"></span><br><span class="line">0x000516dd: TAG_compile_unit [99] *</span><br><span class="line">AT_producer( &quot;Apple LLVM version 7.0.2 (clang-700.1.81)&quot; )</span><br><span class="line">AT_language( DW_LANG_ObjC )</span><br><span class="line">AT_name( &quot;/Users/kcrawford/src/apteligent-example-apps/ios/ApteligentExampleApp/ApteligentExampleClass.m&quot; )</span><br><span class="line">AT_stmt_list( 0x00008e5a )</span><br><span class="line">AT_comp_dir( &quot;/Users/kcrawford/src/apteligent-example-apps/ios&quot; )</span><br><span class="line">AT_APPLE_major_runtime_vers( 0x02 )</span><br><span class="line">AT_low_pc( 0x000000010000d704 )</span><br><span class="line">AT_high_pc( 0x000000010000f234 )</span><br><span class="line"></span><br><span class="line">0x00051cbd:     TAG_subprogram [116] *</span><br><span class="line">AT_low_pc( 0x000000010000ec80 )</span><br><span class="line">AT_high_pc( 0x000000010000ecd0 )</span><br><span class="line">AT_frame_base( reg29 )</span><br><span class="line">AT_object_pointer( &#123;0x00051cdb&#125; )</span><br><span class="line">AT_name( &quot;-[ApteligentExampleClass buggyFunction]&quot; )</span><br><span class="line">AT_decl_file( &quot;/Users/kcrawford/src/apteligent-example-apps/ios/ApteligentExampleApp/ApteligentExampleClass.m&quot; )</span><br><span class="line">AT_decl_line( 179 )</span><br><span class="line">AT_prototyped( 0x01 )</span><br><span class="line">Line table dir : &apos;/Users/kcrawford/src/apteligent-example-apps/ios/ApteligentExampleApp&apos;</span><br><span class="line">Line table file: &apos;ApteligentExampleClass.m&apos; line 181, column 1 with start address 0x000000010000ecc4</span><br><span class="line">Looking up address: 0x000000010000ecc4 in .debug_frame... not found.</span><br><span class="line"></span><br></pre></td></tr></table></figure></p>
<p>注意：Xcode 10.2 之后不能指定架构，否则输出为空信息。如果发现输出为空，使用<br>dwarfdump –lookup 0x10000ECC4  ApteligentExampleApp.dSYM 。然后再从输出中找到你要的架构下的信息。</p>
<h4 id="symbolicatecrash"><a href="#symbolicatecrash" class="headerlink" title="symbolicatecrash"></a>symbolicatecrash</h4><p>到目前为止，我们已经研究了在崩溃中表示特定地址的工具，或者至多是 ATOS 中的一系列地址。 为了简化此过程，Apple 发布了一个带有 XCode 的脚本，可以完整地加速崩溃报告的符号化过程。 如果您有 dSYM，app 二进制文件和崩溃报告，这可能是最简单的符号化方法。 您不必担心任何地址 - 此脚本将解析整个故障转储文件并使用 ATOS 将所有地址解析为符号。</p>
<ul>
<li><p>定位系统中的 “symbolicatecrash”</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /Applications/Xcode.app</span><br><span class="line">find . -name symbolicatecrash</span><br></pre></td></tr></table></figure>
</li>
<li><p>如果不存在则导出 DEVELOPER_DIR 环境变量</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export DEVELOPER_DIR=&quot;/Applications/Xcode.app/Contents/Developer&quot;</span><br></pre></td></tr></table></figure>
</li>
<li><p>拷贝 .app二进制文件、crash 报告、.dSYM 文件到临时文件(ex. ~/tmp)</p>
</li>
<li>执行如下脚本<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash -v ApteligentExampleApp.crash ApteligentExampleApp.app.dSYM/</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p>如果一切顺利，脚本应该符号化整个崩溃文件并将结果输出到终端窗口。 此脚本不会执行任何您无法使用 ATOS 或其他工具手动执行的操作，但它可以更快地为您提供所需的内容。</p>
<h3 id="开始实现自己的符号化平台"><a href="#开始实现自己的符号化平台" class="headerlink" title="开始实现自己的符号化平台"></a>开始实现自己的符号化平台</h3><h4 id="使用脚本批量处理，使用dwarfdump符号化自己App的-崩溃"><a href="#使用脚本批量处理，使用dwarfdump符号化自己App的-崩溃" class="headerlink" title="使用脚本批量处理，使用dwarfdump符号化自己App的 崩溃"></a>使用脚本批量处理，使用dwarfdump符号化自己App的 崩溃</h4><p><a href="http://qiniu.xuyanlan.com/test.zip" target="_blank" rel="noopener">下载测试脚本</a>后 cd 到 test 目录执行命令：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python sym.py BICrashAnalyzeDemo.crash  BICrashAnalyzeDemo.app.dSYM</span><br></pre></td></tr></table></figure></p>
<p>如果使用 dwarfdump 或者 lldb 命令，计算出来的File Address 地址为 0x0000000100006734 (stack address - load address + Slide Value（0x0000000100000000))。<br>Slide Value（32位虚拟地址）: 0x00004000<br>Slide Value（64位虚拟地址）: 0x0000000100000000 test中使用这个<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">dwarfdump --lookup 0x0000000100006734  --arch arm64 BICrashAnalyzeDemo.app.dSYM/Contents/Resources/DWARF/BICrashAnalyzeDemo </span><br><span class="line"></span><br><span class="line">--arch arm64可省略</span><br></pre></td></tr></table></figure></p>
<p>cd 到 test 目录执行如下命令，导出foo.crash中可看到符号化的非系统崩溃<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python analysis.py BICrashAnalyzeDemo.crash</span><br></pre></td></tr></table></figure></p>
<h4 id="使用atosl符号化系统的崩溃"><a href="#使用atosl符号化系统的崩溃" class="headerlink" title="使用atosl符号化系统的崩溃"></a>使用atosl符号化系统的崩溃</h4><p>系统崩溃需要找出崩溃设备所对应的系统库文件。<br>参考文章:</p>
<ul>
<li><a href="https://juejin.im/post/5ab7dece6fb9a028ba1f7218" target="_blank" rel="noopener">iOS Crash日志分析必备：符号化系统库方法</a></li>
<li><a href="http://turingh.github.io/2016/05/24/nlist-Mach-O%E6%96%87%E4%BB%B6%E9%87%8D%E5%AE%9A%E5%90%91%E4%BF%A1%E6%81%AF%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90/" target="_blank" rel="noopener">nlist-Mach-O文件重定向信息数据结构分析</a></li>
<li><a href="https://www.jianshu.com/p/54d842db3f69" target="_blank" rel="noopener">趣探 Mach-O：文件格式分析</a></li>
</ul>
<p>但是 dwarfdump 是基于 debug_info和 debug_frame解析，而系统库是没有这些的，可以使用 atosl，不过 facebook  的atosl 有些 bug，可能都安装不了，我自己实现了一个。</p>
<h4 id="实现自己的-macho-解析工具"><a href="#实现自己的-macho-解析工具" class="headerlink" title="实现自己的 macho 解析工具"></a>实现自己的 macho 解析工具</h4><p><a href="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%88%9B%E5%BB%BAcrash%E5%B9%B3%E5%8F%B0/atosl.zip" target="_blank" rel="noopener">demo 地址</a></p>
<p>如下部分代码：是分析 macho 文件的代码，得到解析后的结构。根据 loadadress(crash 中的) -  vmaddress(segment vmaddr)得到 slide，再使用 stackadress - slide 可以得到文件地址，再从解析出来 symbol table string 中找到地址所在的方法。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br></pre></td><td class="code"><pre><span class="line">int parse_load_command(char *data, long *offset, struct load_command *lc, struct thin_macho *tm, uint32_t magic_number)&#123;</span><br><span class="line">    int result = 0;</span><br><span class="line">    switch (lc-&gt;cmd)&#123;</span><br><span class="line">        case LC_UUID:</span><br><span class="line">        &#123;</span><br><span class="line">            struct uuid_command command = &#123;0&#125;;</span><br><span class="line">            memcpy(&amp;command, data + *offset, sizeof(struct uuid_command));</span><br><span class="line">            int i = 0;</span><br><span class="line">            </span><br><span class="line">            FilePrint( &quot;-LC-UUID :&quot;);</span><br><span class="line">            while(i &lt; 16)&#123;</span><br><span class="line">                tm-&gt;uuid[i] = command.uuid[i];</span><br><span class="line">                FilePrint( &quot;%u&quot;,tm-&gt;uuid[i]);</span><br><span class="line">                i++;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            FilePrint( &quot;\n&quot;);</span><br><span class="line">            debug(&quot;uuid cmdsize = %u,  lc cmdsize = %u&quot;,command.cmdsize,lc-&gt;cmdsize);</span><br><span class="line">        &#125;</span><br><span class="line">            break;</span><br><span class="line">        case LC_SEGMENT:</span><br><span class="line">        &#123;</span><br><span class="line">            struct segment_command command = &#123;0&#125;;</span><br><span class="line">            memcpy(&amp;command, data + *offset, sizeof(struct segment_command));</span><br><span class="line">            if(strcmp(command.segname, &quot;__DWARF&quot;) == 0)&#123;//暂时不处理app 代码 crash</span><br><span class="line">                debug(&quot; is __DWARF skip &quot;);</span><br><span class="line">//                *offset += lc-&gt;cmdsize;</span><br><span class="line">//                return 0;</span><br><span class="line">            &#125;</span><br><span class="line">            if (strcmp(command.segname, &quot;__TEXT&quot;) == 0) &#123;</span><br><span class="line">                tm-&gt;vmaddr32 = command.vmaddr;</span><br><span class="line">            &#125;</span><br><span class="line">            int sectionOff = sizeof(struct segment_command);</span><br><span class="line">            for (int i = 0; i&lt;command.nsects; i++) &#123;</span><br><span class="line">                struct dwarf_section_t *dwarf_s = &#123;0&#125;;</span><br><span class="line">                dwarf_s = malloc(sizeof(struct dwarf_section_t));</span><br><span class="line">                if (dwarf_s == NULL)&#123;</span><br><span class="line">                    printf( &quot;Can not malloc dwarf_s&quot;);</span><br><span class="line">                    return -1;</span><br><span class="line">                &#125;</span><br><span class="line">                memset(dwarf_s, 0, sizeof(struct dwarf_section_t));</span><br><span class="line">                memcpy(&amp;dwarf_s-&gt;mach_section, data + *offset + sectionOff, sizeof(dwarf_s-&gt;mach_section));</span><br><span class="line">                sectionOff += sizeof(dwarf_s-&gt;mach_section);</span><br><span class="line">               </span><br><span class="line">                struct dwarf_section_t *dwarf_sec = tm-&gt;dwarf_section;</span><br><span class="line">                if(!dwarf_sec) &#123;</span><br><span class="line">                    tm-&gt;dwarf_section = dwarf_s;</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    while (dwarf_sec) &#123;</span><br><span class="line">                        if(dwarf_sec-&gt;next == NULL) &#123;</span><br><span class="line">                            dwarf_sec-&gt;next = dwarf_s;</span><br><span class="line">                            break;</span><br><span class="line">                        &#125; else &#123;</span><br><span class="line">                            dwarf_sec = dwarf_sec-&gt;next;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                tm-&gt;section_count ++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">            break;</span><br><span class="line">        case LC_SEGMENT_64:</span><br><span class="line">        &#123;</span><br><span class="line">            struct segment_command_64 command = &#123;0&#125;;</span><br><span class="line">            memcpy(&amp;command, data + *offset, sizeof(struct segment_command_64));</span><br><span class="line">            if(strcmp(command.segname, &quot;__DWARF&quot;) == 0)&#123;//暂时不处理app 代码 crash</span><br><span class="line">                debug(&quot; is __DWARF skip &quot;);</span><br><span class="line">//                *offset += lc-&gt;cmdsize;</span><br><span class="line">//                return 0;</span><br><span class="line">            &#125;</span><br><span class="line">            if (strcmp(command.segname, &quot;__TEXT&quot;) == 0) &#123;</span><br><span class="line">                tm-&gt;vmaddr64 = command.vmaddr;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            int sectionOff = sizeof(struct segment_command_64);</span><br><span class="line">            for (int i = 0; i&lt;command.nsects; i++) &#123;</span><br><span class="line">                struct dwarf_section_64_t *dwarf_s = &#123;0&#125;;</span><br><span class="line">                dwarf_s = malloc(sizeof(struct dwarf_section_64_t));</span><br><span class="line">                if(dwarf_s == NULL) &#123;</span><br><span class="line">                    printf(&quot;no memory malloc dwarf_s \n&quot;);</span><br><span class="line">                    return -1;</span><br><span class="line">                &#125;</span><br><span class="line">                memset(dwarf_s, 0, sizeof(struct dwarf_section_64_t));</span><br><span class="line">                memcpy(&amp;dwarf_s-&gt;mach_section, data + *offset + sectionOff, sizeof(dwarf_s-&gt;mach_section));</span><br><span class="line">                sectionOff += sizeof(dwarf_s-&gt;mach_section);</span><br><span class="line">                struct dwarf_section_64_t *dwarf_sec = tm-&gt;dwarf_section_64;</span><br><span class="line">                if(!dwarf_sec) &#123;</span><br><span class="line">                    tm-&gt;dwarf_section_64 = dwarf_s;</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    while (dwarf_sec) &#123;</span><br><span class="line">                        if(dwarf_sec-&gt;next == NULL) &#123;</span><br><span class="line">                            dwarf_sec-&gt;next = dwarf_s;</span><br><span class="line">                            break;</span><br><span class="line">                        &#125; else &#123;</span><br><span class="line">                            dwarf_sec = dwarf_sec-&gt;next;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                tm-&gt;section_count ++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">            break;</span><br><span class="line">        case LC_SYMTAB:</span><br><span class="line">        &#123;</span><br><span class="line">            struct symtab_command command = &#123;0&#125;;</span><br><span class="line">            memcpy(&amp;command, data + *offset, sizeof(struct symtab_command));</span><br><span class="line">            tm-&gt;nsyms = command.nsyms;</span><br><span class="line">            tm-&gt;strsize = command.strsize;</span><br><span class="line">            tm-&gt;stroff = command.stroff;</span><br><span class="line">            tm-&gt;strings = data + command.stroff;</span><br><span class="line">            if(magic_number == MH_MAGIC_64)&#123;</span><br><span class="line">                tm-&gt;symbollist = malloc(command.nsyms * sizeof(struct symbol_t));</span><br><span class="line">                if(tm-&gt;symbollist == NULL) &#123;</span><br><span class="line">                    printf( &quot;no memory symbollist \n&quot;);</span><br><span class="line">                    return -1;</span><br><span class="line">                &#125;</span><br><span class="line">                memset(tm-&gt;symbollist, &apos;\0&apos;, command.nsyms * sizeof(struct symbol_t));</span><br><span class="line">                struct symbol_t *current = tm-&gt;symbollist;</span><br><span class="line">                int i = 0;</span><br><span class="line">                uint32_t listOffset = 0;</span><br><span class="line">                for (; i &lt; command.nsyms; i++) &#123;</span><br><span class="line">                    memcpy(&amp;current-&gt;sym.sym64, data + command.symoff + listOffset, sizeof(struct nlist_64));</span><br><span class="line">                    uint32_t n_strx = current-&gt;sym.sym64.n_un.n_strx;</span><br><span class="line">                    if(n_strx &lt; 0)&#123;</span><br><span class="line">                        current-&gt;name = &quot;&quot;;</span><br><span class="line">                    &#125;else if(n_strx &gt; command.strsize)&#123;</span><br><span class="line">                        current-&gt;name = &quot;bad string index&quot;;</span><br><span class="line">                    &#125;else &#123;</span><br><span class="line">                        current-&gt;name = tm-&gt;strings + n_strx;</span><br><span class="line">                    &#125;</span><br><span class="line">                   </span><br><span class="line">                    listOffset += sizeof(struct nlist_64);</span><br><span class="line">                    current++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                uint32_t listOffset = 0;</span><br><span class="line">                tm-&gt;symbollist = malloc(command.nsyms * sizeof(struct symbol_t));</span><br><span class="line">                if(tm-&gt;symbollist == NULL)&#123;</span><br><span class="line">                    printf( &quot;no memory symbollist \n&quot;);</span><br><span class="line">                    return -1;</span><br><span class="line">                    </span><br><span class="line">                &#125;</span><br><span class="line">                memset(tm-&gt;symbollist, &apos;\0&apos;, command.nsyms * sizeof(struct symbol_t));</span><br><span class="line">                struct symbol_t *current = tm-&gt;symbollist;</span><br><span class="line">                int i = 0;</span><br><span class="line">                for (i = 0; i &lt; command.nsyms; i++) &#123;</span><br><span class="line">                    memcpy(&amp;current-&gt;sym.sym32, data + command.symoff + listOffset, sizeof(struct nlist));</span><br><span class="line">                    if(current-&gt;sym.sym32.n_un.n_strx &lt; 0)&#123;</span><br><span class="line">                        current-&gt;name = &quot;&quot;;</span><br><span class="line">                    &#125;else if(current-&gt;sym.sym32.n_un.n_strx &gt; command.strsize)&#123;</span><br><span class="line">                        current-&gt;name = &quot;bad string index&quot;;</span><br><span class="line">                    &#125;</span><br><span class="line">                    else &#123;</span><br><span class="line">                        current-&gt;name = tm-&gt;strings + current-&gt;sym.sym32.n_un.n_strx;</span><br><span class="line">                    &#125;</span><br><span class="line">                    listOffset += sizeof(struct nlist);</span><br><span class="line">                    current++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            result = 0;</span><br><span class="line">            </span><br><span class="line">        &#125;</span><br><span class="line">            break;</span><br><span class="line">        default:</span><br><span class="line">            break;</span><br><span class="line">    &#125;</span><br><span class="line">    *offset += lc-&gt;cmdsize;</span><br><span class="line">    return result;</span><br><span class="line">&#125;</span><br><span class="line">//parse</span><br><span class="line">int parse_macho(struct thin_macho *tm) &#123;</span><br><span class="line">    char *macho_str = tm-&gt;data;</span><br><span class="line">    int num_load_cmds = 0;</span><br><span class="line">    long offset = 0;</span><br><span class="line">    size_t header_size = 0;</span><br><span class="line">    uint32_t magic_number = 0;</span><br><span class="line">    memcpy(&amp;magic_number, macho_str, sizeof(uint32_t));</span><br><span class="line">    switch (magic_number) &#123;</span><br><span class="line">        case MH_MAGIC:</span><br><span class="line">        &#123;</span><br><span class="line">            struct mach_header mh = &#123;0&#125;;</span><br><span class="line">            header_size = sizeof(struct mach_header);</span><br><span class="line">            memcpy(&amp;mh, macho_str + offset, header_size);</span><br><span class="line">            num_load_cmds = mh.ncmds;</span><br><span class="line">            tm-&gt;cputype = mh.cputype;</span><br><span class="line">            tm-&gt;cpusubtype = mh.cpusubtype;</span><br><span class="line">        &#125;</span><br><span class="line">            break;</span><br><span class="line">        case MH_MAGIC_64:</span><br><span class="line">        &#123;</span><br><span class="line">            struct mach_header_64 mh64 = &#123;0&#125;;</span><br><span class="line">            header_size = sizeof(struct mach_header_64);</span><br><span class="line">            memcpy(&amp;mh64, macho_str + offset, header_size);</span><br><span class="line">            num_load_cmds = mh64.ncmds;</span><br><span class="line">            tm-&gt;cputype = mh64.cputype;</span><br><span class="line">            if(tm-&gt;cputype == CPU_TYPE_ARM64 &amp;&amp; tm-&gt;cpusubtype == CPU_SUBTYPE_ARM64_ALL) &#123;</span><br><span class="line">                tm-&gt;is_64 = 1;</span><br><span class="line">            &#125;</span><br><span class="line">            tm-&gt;cpusubtype = mh64.cpusubtype;</span><br><span class="line">        &#125;</span><br><span class="line">            break;</span><br><span class="line">        case MH_CIGAM:</span><br><span class="line">            printf(&quot;TODO: MH_CIGAM\n&quot;);</span><br><span class="line">            break;</span><br><span class="line">        case MH_CIGAM_64:</span><br><span class="line">            printf(&quot;TODO: MH_CIGAM_64\n&quot;);</span><br><span class="line">            break;</span><br><span class="line">        case FAT_MAGIC:</span><br><span class="line">        case FAT_CIGAM:</span><br><span class="line">            printf( &quot;fat in fat?\n&quot;);</span><br><span class="line">            break;</span><br><span class="line">        default:</span><br><span class="line">            debug(&quot;not found magic_num = %u&quot;,magic_number);</span><br><span class="line">            printf( &quot;magic_number invalid.&quot;);</span><br><span class="line">            break;</span><br><span class="line">    &#125;</span><br><span class="line">    offset += header_size;</span><br><span class="line">    struct load_command lc = &#123;0&#125;;</span><br><span class="line">    int i = 0;</span><br><span class="line">    </span><br><span class="line">    while (i &lt; num_load_cmds) &#123;</span><br><span class="line">        memcpy(&amp;lc, macho_str + offset, sizeof(struct load_command));</span><br><span class="line">        int lc_result = parse_load_command(macho_str, &amp;offset, &amp;lc, tm, magic_number);</span><br><span class="line">        if(lc_result == -1) &#123;</span><br><span class="line">            return -1;</span><br><span class="line">        &#125;</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">//    sortSymbolAddr(tm);print part</span><br><span class="line">    InsertSort(tm-&gt;symbollist,tm-&gt;nsyms, tm-&gt;is_64);</span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">int parse_thin(FILE *fp,uint32_t magic_num, struct target_file *tf) &#123;</span><br><span class="line">    tf-&gt;numofarchs = 1;</span><br><span class="line">    tf-&gt;thin_machos = malloc(1 * sizeof(struct thin_macho*));</span><br><span class="line">    if (tf-&gt;thin_machos == NULL)&#123;</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line">    memset(tf-&gt;thin_machos, &apos;\0&apos;, 1 * sizeof(struct thin_macho*));</span><br><span class="line">    fseek(fp, 0L, SEEK_END);</span><br><span class="line">    long int size = ftell(fp);</span><br><span class="line">    fseek(fp, 0L, SEEK_SET);</span><br><span class="line">    tf-&gt;thin_machos[0] = malloc(sizeof(struct thin_macho));</span><br><span class="line">    if(tf-&gt;thin_machos[0] == NULL) &#123;</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line">    memset(tf-&gt;thin_machos[0], &apos;\0&apos;, sizeof(struct thin_macho));</span><br><span class="line">    tf-&gt;thin_machos[0]-&gt;data = malloc(size);</span><br><span class="line">    if(tf-&gt;thin_machos[0]-&gt;data == NULL)&#123;</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line">    memset(tf-&gt;thin_machos[0]-&gt;data, &apos;\0&apos;, size);</span><br><span class="line">    long numofbytes = 0;</span><br><span class="line">    numofbytes = fread(tf-&gt;thin_machos[0]-&gt;data, sizeof(char), size, fp);</span><br><span class="line">    assert(numofbytes == size);</span><br><span class="line">    if(numofbytes == size)&#123;</span><br><span class="line">        int result = parse_macho(tf-&gt;thin_machos[0]);</span><br><span class="line">        if(result == -1) &#123;</span><br><span class="line">            return -1;</span><br><span class="line">        &#125;</span><br><span class="line">        return 0;</span><br><span class="line">    &#125;else&#123;</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int parse_fat(FILE *fp,uint32_t magic_num, struct target_file *tf) &#123;</span><br><span class="line">    long rc = 0;</span><br><span class="line">    struct fat_header fh = &#123;0&#125;;</span><br><span class="line">    uint32_t nfat_arch = 0;</span><br><span class="line">    debug(&quot;sizeof(struct fat_header) is = %lu&quot;,sizeof(struct fat_header));</span><br><span class="line">    if((rc = fread(&amp;fh, sizeof(struct fat_header), 1, fp)) != 0) &#123;</span><br><span class="line">        if(magic_num == FAT_CIGAM)&#123;</span><br><span class="line">            FilePrint(&quot;-magic_num :  FAT_CIGAM\n&quot;);</span><br><span class="line">            uint32_endian_convert(&amp;fh.nfat_arch);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            FilePrint(&quot;-magic_num :  FAT_MAGIC\n&quot;);</span><br><span class="line">        &#125;</span><br><span class="line">        nfat_arch = fh.nfat_arch;</span><br><span class="line">        FilePrint(&quot;-arch count : %u \n&quot;,nfat_arch);</span><br><span class="line">    &#125;</span><br><span class="line">    tf-&gt;numofarchs = nfat_arch;</span><br><span class="line">    tf-&gt;thin_machos = malloc(sizeof(struct thin_macho *));</span><br><span class="line">    if(tf-&gt;thin_machos == NULL) &#123;</span><br><span class="line">        printf( &quot;parse_fat Can not malloc thin_machos*&quot;);</span><br><span class="line">        return -1;</span><br><span class="line">    &#125;</span><br><span class="line">    memset(tf-&gt;thin_machos, &apos;\0&apos;, nfat_arch * sizeof(struct thin_macho *));</span><br><span class="line">    uint32_t i = 0;</span><br><span class="line">    struct fat_arch fa = &#123;0&#125;;</span><br><span class="line">    while (i &lt; nfat_arch) &#123;</span><br><span class="line">        tf-&gt;thin_machos[i] = malloc(sizeof(struct thin_macho));</span><br><span class="line">        if(tf-&gt;thin_machos[i] == NULL) &#123;</span><br><span class="line">            printf( &quot;parse_fat Can not malloc thin_machos[%d]&quot;,i);</span><br><span class="line">            return -1;</span><br><span class="line">        &#125;</span><br><span class="line">        memset(tf-&gt;thin_machos[i], &apos;\0&apos;, sizeof(struct thin_macho));</span><br><span class="line">        debug(&quot;sizeof(struct fat_arch) is = %lu  \n sizeof(char) = %lu&quot;,sizeof(struct fat_arch),sizeof(char));</span><br><span class="line">        if((rc = fread(&amp;fa, sizeof(struct fat_arch), 1, fp)) == 1) &#123;</span><br><span class="line">            if(magic_num == FAT_CIGAM)&#123;</span><br><span class="line">                integer_t_endian_convert(&amp;fa.cputype);</span><br><span class="line">                integer_t_endian_convert(&amp;fa.cpusubtype);</span><br><span class="line">                uint32_endian_convert(&amp;fa.offset);</span><br><span class="line">                uint32_endian_convert(&amp;fa.size);</span><br><span class="line">                uint32_endian_convert(&amp;fa.align);</span><br><span class="line">            &#125;</span><br><span class="line">            tf-&gt;thin_machos[i]-&gt;data = malloc(fa.size);</span><br><span class="line">            if (tf-&gt;thin_machos[i]-&gt;data == NULL)&#123;</span><br><span class="line">                printf( &quot;Can not malloc data&quot;);</span><br><span class="line">                return -1;</span><br><span class="line">            &#125;</span><br><span class="line">            memset(tf-&gt;thin_machos[i]-&gt;data, &apos;\0&apos;, fa.size);</span><br><span class="line">            long cur_pos = ftell(fp);</span><br><span class="line">            fseek(fp, fa.offset, SEEK_SET);</span><br><span class="line">            long numofbytes = 0;</span><br><span class="line">            numofbytes = fread(tf-&gt;thin_machos[i]-&gt;data, sizeof(char), fa.size, fp);</span><br><span class="line">            if(numofbytes != fa.size) &#123;</span><br><span class="line">                printf( &quot;parse_fat read macho data error. i = %u \n&quot;,i);</span><br><span class="line">                return -1;</span><br><span class="line">            &#125;</span><br><span class="line">            fseek(fp, cur_pos, SEEK_SET);//获取完 data。回到原处</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            printf( &quot;read fat arch error\n&quot;);</span><br><span class="line">            return -1;</span><br><span class="line">        &#125;</span><br><span class="line">        int result = parse_macho(tf-&gt;thin_machos[i]);</span><br><span class="line">        if(result == -1)&#123;</span><br><span class="line">            return -1;</span><br><span class="line">        &#125;</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">struct target_file *parse_file(const char *filename)&#123;</span><br><span class="line">    FILE *fp = fopen(filename, &quot;rb&quot;);</span><br><span class="line">    if (fp == NULL)&#123;</span><br><span class="line">        printf( &quot;Can not open file %s for read.\n&quot;, filename);</span><br><span class="line">        return NULL;</span><br><span class="line">    &#125;</span><br><span class="line">    struct target_file *tf = malloc(sizeof(struct target_file));</span><br><span class="line">    if (tf == NULL)&#123;</span><br><span class="line">        printf( &quot;Can not malloc target_file&quot;);</span><br><span class="line">        return NULL;</span><br><span class="line">    &#125;</span><br><span class="line">    //申请内存</span><br><span class="line">    memset(tf, &apos;\0&apos;, sizeof(struct target_file));</span><br><span class="line">    long rc = 0;</span><br><span class="line">    uint32_t magic_num = 0;</span><br><span class="line">    int parse_result = -1;</span><br><span class="line">    //前四字节为魔数</span><br><span class="line">    if((rc = fread(&amp;magic_num, sizeof(uint32_t), 1, fp)) != 0)</span><br><span class="line">    &#123;</span><br><span class="line">        fseek(fp, 0L, SEEK_SET);</span><br><span class="line">        switch (magic_num) &#123;</span><br><span class="line">            case MH_MAGIC:</span><br><span class="line">                debug(&quot;MH_MAGIC&quot;);</span><br><span class="line">                parse_result = parse_thin(fp,MH_MAGIC,tf);</span><br><span class="line">                break;</span><br><span class="line">            case MH_MAGIC_64:</span><br><span class="line">                debug(&quot;MH_MAGIC_64&quot;);</span><br><span class="line">                parse_result = parse_thin(fp,MH_MAGIC_64,tf);</span><br><span class="line">                break;</span><br><span class="line">            case MH_CIGAM_64://macho 和 平台编码不一致</span><br><span class="line">                debug(&quot;MH_CIGAM_64&quot;);</span><br><span class="line">                printf(&quot;MH_CIGAM_64: %x\n&quot;, MH_CIGAM_64);</span><br><span class="line">                break;</span><br><span class="line">            case FAT_MAGIC://大端序</span><br><span class="line">                debug(&quot;FAT_MAGIC&quot;);</span><br><span class="line">                parse_result = parse_fat(fp,FAT_MAGIC,tf);</span><br><span class="line">                break;</span><br><span class="line">            case FAT_CIGAM:</span><br><span class="line">                debug(&quot;FAT_CIGAM&quot;);</span><br><span class="line">                parse_result = parse_fat(fp,FAT_CIGAM,tf);</span><br><span class="line">                break;</span><br><span class="line">            default:</span><br><span class="line">                debug(&quot;not found magic_num = %u&quot;,magic_num);</span><br><span class="line">                printf( &quot;magic_number invalid.&quot;);</span><br><span class="line">                break;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    fclose(fp);</span><br><span class="line">    if(parse_result == -1)&#123;</span><br><span class="line">        return NULL;</span><br><span class="line">    &#125;</span><br><span class="line">    return tf;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int symbolicate(const char* arch, const char *executable, const char *loadAddr, char *addresses[])&#123;</span><br><span class="line">    char *filename = strrchr(executable, &apos;/&apos;);</span><br><span class="line">    if(filename == NULL)&#123;</span><br><span class="line">        filename = (char *)executable;</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        filename = filename + 1;</span><br><span class="line">    &#125;</span><br><span class="line">    //开始读取二进制文件</span><br><span class="line">    debug(&quot;about to analysis file.&quot;);</span><br><span class="line">    struct target_file *tf = parse_file(executable);</span><br><span class="line">    int result = findAddressInMacho(tf, arch,loadAddr, addresses);</span><br><span class="line">    free_target_file(tf);</span><br><span class="line">    return result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>请自行替换文件地址再运行。可以从 result.txt 中看到解析后的结构。可以看到和 machoview 解析出来的结构一致。</p>
<p>接下来：<br>1：对 symbol table string 列表的 value 排序，symbol table string中包含方法名。<br>2：使用上面提到的文件地址去排好序的 value 中查找，查找到小于 value 的 index，则崩溃的方法名存在于 index-1 项。<br>可以看到，找到的系统崩溃和 symbolicatecrash 符号化出来的 output.crash 中一致。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%88%9B%E5%BB%BAcrash%E5%B9%B3%E5%8F%B0/find.png" alt=""></p>
<h4 id="后续：自建crash-分析平台"><a href="#后续：自建crash-分析平台" class="headerlink" title="后续：自建crash 分析平台"></a>后续：自建crash 分析平台</h4><p>自建 crash 分析平台的路还有很长，目前我也刚走到第三步。分为：<br>OS 平台：Ubuntu<br>1：安装 Ubuntu 虚拟机18.04LTS版本，只有这个版本才支持 dwarfdump。apt-get 安装 llvm-dwarfdump<br>使用命令：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">llvm-dwarfdump -lookup 0x0000000100006734   BICrashAnalyzeDemo.app.dSYM/Contents/Resources/DWARF/BICrashAnalyzeDemo</span><br></pre></td></tr></table></figure><br>结果<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%88%9B%E5%BB%BAcrash%E5%B9%B3%E5%8F%B0/ubantu.png" alt=""><br>可以看到和 macox 上 dwarfdump 结果差不多，上面的脚本改改能继续用来符号化 AppName 的崩溃。</p>
<p>2：安装自己实现的 atosl，符号化系统崩溃。<br>3：脚本批量符号化 crash 文件。<br>4：crash 分类<br>5：crash 额外信息添加</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>【译】<a href="https://www.apteligent.com/technical-resource/symbolicating-an-ios-crash-report/?partner_code=GDC_so_symbolicateios" target="_blank" rel="noopener">Symbolicating an iOS Crash Report</a></p>
<p>通常，当您收到来自iTunes连接的崩溃报告或提供移动崩溃收集和报告的第三方服务（如Apteligent）时，该服务将负责为您提供符号化后的崩溃。如果你没有上传符号，你可能会发现自己有一个非符号化的崩溃，没有别的东西可以继续。这样的崩溃文件对于调试可能影响大量用户的问题并不是非常有用。</p>
<p>在这种情况下，您必须通过将回溯堆栈地址解析为符号来对崩溃报告进行符号化，以获取有关崩溃的有用信息。</p>
<p>幸运的是，完全有可能手动符号化崩溃报告。本文将概述您需要的信息，向您展示如何解释崩溃报告，并查看OSX和XCode上可用的一些工具来符号化崩溃。</p>
<p>崩溃报告中只有两个部分与符号化异常跟踪相关。第一个是 Exception Backtrace 部分。这显示了崩溃时应用程序的调用堆栈。此特定崩溃日志片段显示了我们的 ApteligentExampleApp 应用程序内部崩溃的回溯。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Last Exception Backtrace:</span><br><span class="line">0   CoreFoundation 0x000000018708b100 0x186f80000 + 1093888</span><br><span class="line">1   libobjc.A.dylib            0x00000001939441fc 0x19393c000 + 33276</span><br><span class="line">2   CoreFoundation 0x000000018708b040 0x186f80000 + 1093696</span><br><span class="line">3   ApteligentExampleApp 0x000000010003acc4 0x10002c000 + 60612</span><br></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[block 探索]]></title>
    <link href="http://yoursite.com/2019/02/11/block/"/>
    <id>http://yoursite.com/2019/02/11/block/</id>
    <published>2019-02-10T21:31:14.000Z</published>
    <updated>2023-10-08T07:19:20.828Z</updated>
    <content type="html"><![CDATA[<h3 id="block-转-c-源码"><a href="#block-转-c-源码" class="headerlink" title="block 转 c++ 源码"></a>block 转 c++ 源码</h3><p>如下代码 .h .m文件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">//TestClang.h</span><br><span class="line">@interface TestClang : NSObject</span><br><span class="line">+ (void)testBlcok;</span><br><span class="line">@end</span><br><span class="line"></span><br><span class="line">//TestClang.m</span><br><span class="line">#import &quot;TestClang.h&quot;</span><br><span class="line">static int numGlobel = 29;</span><br><span class="line">@implementation TestClang</span><br><span class="line">+ (void)testBlcok &#123;</span><br><span class="line">    //没有截获局部变量 __NSGlobalBlock__</span><br><span class="line">    void(^block1)(void) = ^&#123;</span><br><span class="line">        NSLog(@&quot;just a block&quot;);</span><br><span class="line">            &#125;;</span><br><span class="line">    NSLog(@&quot; block1 = %@&quot;, block1);</span><br><span class="line">    block1();</span><br><span class="line">    static int numStatic = 12;</span><br><span class="line">    int num = 10;</span><br><span class="line">    __block int numBlock = 19;</span><br><span class="line">    __block int numBlock2 = 30;</span><br><span class="line">    __block int numBlockTest = 30;</span><br><span class="line">        void(^block2)(void) = ^&#123;</span><br><span class="line">        NSLog(@&quot;just a block === %d, numStatic = %d numGlobel = %d  numBlock=%d numBlock2=%d numBlockTest = %d&quot;, num,numStatic,numGlobel,numBlock,numBlock2,numBlockTest);</span><br><span class="line">            &#125;;</span><br><span class="line">    num = 33;</span><br><span class="line">    numStatic = 121;</span><br><span class="line">    numGlobel = 129;</span><br><span class="line">    numBlock = 22222;</span><br><span class="line">    block2();</span><br><span class="line">    NSLog(@&quot;block2 = %@&quot;, block2);</span><br><span class="line">&#125;</span><br><span class="line">@end</span><br></pre></td></tr></table></figure></p>
<blockquote>
<p>执行：xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc TestClang.m<br>不要引用其他头文件，以免导出报Error<br>目录下生成了一个TestClang.cpp文件</p>
</blockquote>
<ul>
<li>testBlock 对应的方法变为：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">//_C_ 表示为类方法 _I_ 为实例方法</span><br><span class="line">static void _C_TestClang_testBlcok(Class self, SEL _cmd) &#123;</span><br><span class="line"></span><br><span class="line">    void(*block1)(void) = ((void (*)())&amp;__TestClang__testBlcok_block_impl_0((void *)__TestClang__testBlcok_block_func_0, &amp;__TestClang__testBlcok_block_desc_0_DATA));</span><br><span class="line">    NSLog((NSString *)&amp;__NSConstantStringImpl__var_folders_nc_qvb_bh854tz1y0p1hdk5y6km0000gn_T_TestClang_cf514e_mi_1, block1);</span><br><span class="line">    ((void (*)(__block_impl *))((__block_impl *)block1)-&gt;FuncPtr)((__block_impl *)block1);</span><br><span class="line">    static int numStatic = 12;</span><br><span class="line">    int num = 10;</span><br><span class="line">    __attribute__((__blocks__(byref))) __Block_byref_numBlock_0 numBlock = &#123;(void*)0,(__Block_byref_numBlock_0 *)&amp;numBlock, 0, sizeof(__Block_byref_numBlock_0), 19&#125;;</span><br><span class="line">    __attribute__((__blocks__(byref))) __Block_byref_numBlock2_1 numBlock2 = &#123;(void*)0,(__Block_byref_numBlock2_1 *)&amp;numBlock2, 0, sizeof(__Block_byref_numBlock2_1), 30&#125;;</span><br><span class="line">    __attribute__((__blocks__(byref))) __Block_byref_numBlockTest_2 numBlockTest = &#123;(void*)0,(__Block_byref_numBlockTest_2 *)&amp;numBlockTest, 0, sizeof(__Block_byref_numBlockTest_2), 30&#125;;</span><br><span class="line"></span><br><span class="line">        void(*block2)(void) = ((void (*)())&amp;__TestClang__testBlcok_block_impl_1((void *)__TestClang__testBlcok_block_func_1, &amp;__TestClang__testBlcok_block_desc_1_DATA, num, &amp;numStatic, (__Block_byref_numBlock_0 *)&amp;numBlock, (__Block_byref_numBlock2_1 *)&amp;numBlock2, (__Block_byref_numBlockTest_2 *)&amp;numBlockTest, 570425344));</span><br><span class="line">    num = 33;</span><br><span class="line">    numStatic = 121;</span><br><span class="line">    numGlobel = 129;</span><br><span class="line">    (numBlock.__forwarding-&gt;numBlock) = 22222;</span><br><span class="line">    ((void (*)(__block_impl *))((__block_impl *)block2)-&gt;FuncPtr)((__block_impl *)block2);</span><br><span class="line">    NSLog((NSString *)&amp;__NSConstantStringImpl__var_folders_nc_qvb_bh854tz1y0p1hdk5y6km0000gn_T_TestClang_cf514e_mi_3, block2);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h4 id="block-变量定义"><a href="#block-变量定义" class="headerlink" title="block 变量定义"></a>block 变量定义</h4><p>OC 代码中定义了了block1 和 block2 两个 block 变量，我们看 block1 变量定义<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">void(*block1)(void) = ((void (*)())&amp;__TestClang__testBlcok_block_impl_0((void *)__TestClang__testBlcok_block_func_0, &amp;__TestClang__testBlcok_block_desc_0_DATA));</span><br></pre></td></tr></table></figure></p>
<p>上述定义代码中，可以发现，block1 定义中调用了 <code>__TestClang__testBlcok_block_impl_0</code> 并且将 <code>__TestClang__testBlcok_block_func_0</code> 函数 和 <code>__TestClang__testBlcok_block_desc_0_DATA</code> 地址赋值给了 block1。（你可能注意到了命名规则 <code>__类名__方法名__block__impl_第几个block</code>，desc 和 func 类似）。<br>block1() 的调用简化后就是 block1-&gt;FuncPtr 了。<br>那么我们来看一下 <code>__TestClang__testBlcok_block_impl_0</code> 结构体的内部结构<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">struct __block_impl &#123;</span><br><span class="line">  void *isa;</span><br><span class="line">  int Flags;</span><br><span class="line">  int Reserved;</span><br><span class="line">  void *FuncPtr;</span><br><span class="line">&#125;;</span><br><span class="line">struct __TestClang__testBlcok_block_impl_0 &#123;</span><br><span class="line">  struct __block_impl impl;</span><br><span class="line">  struct __TestClang__testBlcok_block_desc_0* Desc;</span><br><span class="line">  __TestClang__testBlcok_block_impl_0(void *fp, struct __TestClang__testBlcok_block_desc_0 *desc, int flags=0) &#123;</span><br><span class="line">    impl.isa = &amp;_NSConcreteStackBlock;//用于初始化 __block_impl 结构体的isa成员</span><br><span class="line">    impl.Flags = flags;</span><br><span class="line">    impl.FuncPtr = fp;</span><br><span class="line">    Desc = desc;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<p>结构体实现了一个同名的构造函数，也就是说将 <code>__TestClang__testBlcok_block_impl_0</code> 结构体的地址赋值给了 block1 变量；<br><code>__TestClang__testBlcok_block_impl_0</code> 的构造函数参数有 （void*）<code>__TestClang__testBlcok_block_func_0</code> 、<code>__TestClang__testBlcok_block_desc_0_DATA</code> flags=0。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">static void __TestClang__testBlcok_block_func_0(struct __TestClang__testBlcok_block_impl_0 *__cself) &#123;</span><br><span class="line"></span><br><span class="line">        NSLog((NSString *)&amp;__NSConstantStringImpl__var_folders_nc_qvb_bh854tz1y0p1hdk5y6km0000gn_T_TestClang_61ed5f_mi_0);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">static struct __TestClang__testBlcok_block_desc_0 &#123;</span><br><span class="line">  size_t reserved;</span><br><span class="line">  size_t Block_size;</span><br><span class="line">&#125; __TestClang__testBlcok_block_desc_0_DATA = &#123; 0, sizeof(struct __TestClang__testBlcok_block_impl_0)&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>我们可以看到 <code>__TestClang__testBlcok_block_desc_0</code> 中存储着两个参数，reserved 和 Block_size，并且 reserved 赋值为0而 Block_size 则存储着 <code>__TestClang__testBlcok_block_impl_0</code> 的占用空间大小。最终将 <code>__TestClang__testBlcok_block_desc_0</code> 结构体的地址传入 <code>__TestClang__testBlcok_block_impl_0</code> 中赋值给 Desc。<br>然而 block1 只是一个最简单的 block ，没有截获任何变量。运行时打印 block1 会发现 block1 是 <code>__NSGlobalBlock__</code> 类型（原因：只要block没有引用栈或堆上的数据，编译器则会吧 block1 优化为  <code>__NSGlobalBlock__</code> 类型）。<br>Block即为 OC 对象。</p>
<p>下面我们来看下截获了外部值的 block。</p>
<h4 id="截获局部变量、静态变量、全局静态变量、-block-变量的-block"><a href="#截获局部变量、静态变量、全局静态变量、-block-变量的-block" class="headerlink" title="截获局部变量、静态变量、全局静态变量、__block 变量的 block"></a>截获局部变量、静态变量、全局静态变量、__block 变量的 block</h4><p>block2 变量的定义<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">        void(*block2)(void) = ((void (*)())&amp;__TestClang__testBlcok_block_impl_1((void *)__TestClang__testBlcok_block_func_1, &amp;__TestClang__testBlcok_block_desc_1_DATA, num, &amp;numStatic, (__Block_byref_numBlock_0 *)&amp;numBlock, (__Block_byref_numBlock2_1 *)&amp;numBlock2, (__Block_byref_numBlockTest_2 *)&amp;numBlockTest, 570425344));</span><br></pre></td></tr></table></figure><br>block2 变量的声明看起来比较复杂，对应 OC 源码分析：</p>
<ul>
<li>可以看到 <code>__TestClang__testBlcok_block_impl_1</code> 中的结构体成员变多了,那就是截获的自动变量<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">struct __TestClang__testBlcok_block_impl_1 &#123;</span><br><span class="line">  struct __block_impl impl;</span><br><span class="line">  struct __TestClang__testBlcok_block_desc_1* Desc;</span><br><span class="line">  int num;</span><br><span class="line">  int *numStatic; //静态局部变量地址截获， 对于全局的静态变量，可以直接使用，不需要截获为结构题成员变量</span><br><span class="line">  __Block_byref_numBlock_0 *numBlock; // by ref 声明了 __block 的局部变量 (命名规则：__Block_byref_变量名_第几个ref）</span><br><span class="line">  __Block_byref_numBlock2_1 *numBlock2; // by ref 声明了 __block 的局部变量</span><br><span class="line">  __Block_byref_numBlockTest_2 *numBlockTest; // by ref 声明了 __block 的局部变量</span><br><span class="line">  __TestClang__testBlcok_block_impl_1(void *fp, struct __TestClang__testBlcok_block_desc_1 *desc, int _num, int *_numStatic, __Block_byref_numBlock_0 *_numBlock, __Block_byref_numBlock2_1 *_numBlock2, __Block_byref_numBlockTest_2 *_numBlockTest, int flags=0) : num(_num), numStatic(_numStatic), numBlock(_numBlock-&gt;__forwarding), numBlock2(_numBlock2-&gt;__forwarding), numBlockTest(_numBlockTest-&gt;__forwarding) &#123;</span><br><span class="line">    impl.isa = &amp;_NSConcreteStackBlock;</span><br><span class="line">    impl.Flags = flags;</span><br><span class="line">    impl.FuncPtr = fp;</span><br><span class="line">    Desc = desc;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p>接下来同样看下<strong>TestClang</strong>testBlcok_block_impl_1 构造函数的参数</p>
<ul>
<li><code>__TestClang__testBlcok_block_func_1</code> block2 的 FuncPtr<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">static void __TestClang__testBlcok_block_func_1(struct __TestClang__testBlcok_block_impl_1 *__cself) &#123;</span><br><span class="line">  __Block_byref_numBlock_0 *numBlock = __cself-&gt;numBlock; // bound by ref</span><br><span class="line">  __Block_byref_numBlock2_1 *numBlock2 = __cself-&gt;numBlock2; // bound by ref</span><br><span class="line">  __Block_byref_numBlockTest_2 *numBlockTest = __cself-&gt;numBlockTest; // bound by ref</span><br><span class="line">  int num = __cself-&gt;num; // bound by copy</span><br><span class="line">  int *numStatic = __cself-&gt;numStatic; // bound by copy</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        NSLog((NSString *)&amp;__NSConstantStringImpl__var_folders_nc_qvb_bh854tz1y0p1hdk5y6km0000gn_T_TestClang_d96c9e_mi_2, num,(*numStatic),numGlobel,(numBlock-&gt;__forwarding-&gt;numBlock),(numBlock2-&gt;__forwarding-&gt;numBlock2),(numBlockTest-&gt;__forwarding-&gt;numBlockTest));</span><br><span class="line">            &#125;</span><br></pre></td></tr></table></figure>
<code>__self</code>相当于C++实例方法中指向实例自身的变量this，或是Objevtive-C 实例方法中指向对象自身的变量self，即<code>__this</code>为指向Block值的变量</li>
<li><p><code>__TestClang__testBlcok_block_desc_1_DATA</code> </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">static struct __TestClang__testBlcok_block_desc_1 &#123;</span><br><span class="line">  size_t reserved;</span><br><span class="line">  size_t Block_size;</span><br><span class="line">  void (*copy)(struct __TestClang__testBlcok_block_impl_1*, struct __TestClang__testBlcok_block_impl_1*);</span><br><span class="line">  void (*dispose)(struct __TestClang__testBlcok_block_impl_1*);</span><br><span class="line">&#125; __TestClang__testBlcok_block_desc_1_DATA = &#123; 0, sizeof(struct __TestClang__testBlcok_block_impl_1), __TestClang__testBlcok_block_copy_1, __TestClang__testBlcok_block_dispose_1&#125;;</span><br></pre></td></tr></table></figure>
</li>
<li><p>新增了<code>__TestClang__testBlcok_block_copy_1</code> 和 <code>__TestClang__testBlcok_block_dispose_1</code>方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">static void __TestClang__testBlcok_block_copy_1(struct __TestClang__testBlcok_block_impl_1*dst, struct __TestClang__testBlcok_block_impl_1*src) &#123;_Block_object_assign((void*)&amp;dst-&gt;numBlock, (void*)src-&gt;numBlock, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&amp;dst-&gt;numBlock2, (void*)src-&gt;numBlock2, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&amp;dst-&gt;numBlockTest, (void*)src-&gt;numBlockTest, 8/*BLOCK_FIELD_IS_BYREF*/);&#125;</span><br><span class="line"></span><br><span class="line">static void __TestClang__testBlcok_block_dispose_1(struct __TestClang__testBlcok_block_impl_1*src) &#123;_Block_object_dispose((void*)src-&gt;numBlock, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src-&gt;numBlock2, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src-&gt;numBlockTest, 8/*BLOCK_FIELD_IS_BYREF*/);&#125;</span><br></pre></td></tr></table></figure>
<p><code>__TestClang__testBlcok_block_copy_1</code> 函数中所使用的<code>_Block_object_assign</code>函数将对象类型对象复制给 Block 用结构体的成员变量 arc 并持有该对象，调用<code>_Block_object_assign</code>函数相当于retain函数，将对象赋值在对象类型的结构体成员变量中。<br><code>__TestClang__testBlcok_block_dispose_1</code> 函数中使用 <code>_Block_object_dispose</code> 函数释放赋值在 Block 用结构体成员变量 arc 中的对象。调用 <code>_Block_object_dispose</code>函数相当于调用release函数，释放赋值在对象类型结构体中的对象。</p>
</li>
<li><p>带<code>__block</code>的局部变量变为了</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">//查看__Block_byref的定义</span><br><span class="line"></span><br><span class="line">struct __Block_byref_numBlock_0 &#123;</span><br><span class="line">  void *__isa; //(void*)0</span><br><span class="line">__Block_byref_numBlock_0 *__forwarding; //指向结构体地址</span><br><span class="line"> int __flags; //0</span><br><span class="line"> int __size; //byref 大小</span><br><span class="line"> int numBlock; //byref 截获的值</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">__attribute__((__blocks__(byref))) __Block_byref_numBlock_0 numBlock = &#123;(void*)0,(__Block_byref_numBlock_0 *)&amp;numBlock, 0, sizeof(__Block_byref_numBlock_0), 19&#125;;</span><br><span class="line">__attribute__((__blocks__(byref))) __Block_byref_numBlock2_1 numBlock2 = &#123;(void*)0,(__Block_byref_numBlock2_1 *)&amp;numBlock2, 0, sizeof(__Block_byref_numBlock2_1), 30&#125;;</span><br><span class="line">__attribute__((__blocks__(byref))) __Block_byref_numBlockTest_2 numBlockTest = &#123;(void*)0,(__Block_byref_numBlockTest_2 *)&amp;numBlockTest, 0, sizeof(__Block_byref_numBlockTest_2), 30&#125;;</span><br><span class="line">通过上面的拆解，那么改变 __block 修饰的变量的值会变为下面这种形式</span><br><span class="line">(numBlock.__forwarding-&gt;numBlock) = 22222;</span><br></pre></td></tr></table></figure>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/block/block_ref.jpg" alt=""></p>
</li>
</ul>
<h3 id="block-底层关系图解"><a href="#block-底层关系图解" class="headerlink" title="block 底层关系图解"></a>block 底层关系图解</h3><p>先拿网络上的图吧，懒得画。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/block/block.jpeg" alt=""></p>
<h3 id="block-的三种类型"><a href="#block-的三种类型" class="headerlink" title="block 的三种类型"></a>block 的三种类型</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">__NSGlobalBlock__ （ _NSConcreteGlobalBlock ）</span><br><span class="line">__NSStackBlock__ （ _NSConcreteStackBlock ）</span><br><span class="line">__NSMallocBlock__ （ _NSConcreteMallocBlock ）</span><br></pre></td></tr></table></figure>
<p>上面提到block1是 <code>__NSGlobalBlock__</code>类型，原因是因为没有截获堆上或栈上的变量。测试如下代码<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">- (void)testBlcok &#123;</span><br><span class="line">    //没有截获局部变量 __NSGlobalBlock__</span><br><span class="line">    void(^blockA)(void) = ^&#123;</span><br><span class="line">        NSLog(@&quot;just a block&quot;);</span><br><span class="line">            &#125;;</span><br><span class="line">    NSLog(@&quot;%@&quot;, blockA);</span><br><span class="line">    //原因：只要block literal里没有引用栈或堆上的数据，那么这个block会自动变为__NSGlobalBlock__类型，这是编译器的优化</span><br><span class="line">    // int value = 10; 改为 const int value = 10; 则会为__NSGlobalBlock__ ，因为加上const 的 value存储在常量区</span><br><span class="line">    //截获了局部变量 __NSMallocBlock__</span><br><span class="line">    int value = 10;</span><br><span class="line">        void(^blockB)(void) = ^&#123;</span><br><span class="line">        </span><br><span class="line">        NSLog(@&quot;just a block === %d&quot;, value);</span><br><span class="line">            &#125;;</span><br><span class="line">    NSLog(@&quot;%@&quot;, blockB);</span><br><span class="line">    //一般并不会这么使用</span><br><span class="line">    // __NSStackBlock__</span><br><span class="line">    void(^ __weak blockC)() = ^&#123;</span><br><span class="line">        NSLog(@&quot;just a block === %d&quot;, value);</span><br><span class="line">            &#125;;</span><br><span class="line">    </span><br><span class="line">    NSLog(@&quot;%@&quot;, blockC);</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>类型定义：<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/block/type.png" alt=""></p>
<h3 id="Block在内存中的存储"><a href="#Block在内存中的存储" class="headerlink" title="Block在内存中的存储"></a>Block在内存中的存储</h3><p>数据段中的  <code>__NSGlobalBlock__</code>直到程序结束才会被回收，不过我们很少使用到<code>__NSGlobalBlock__</code>类型的block，因为这样使用block并没有什么意义。<br><code>__NSStackBlock__类</code>型的block存放在栈中，我们知道栈中的内存由系统自动分配和释放，作用域执行完毕之后就会被立即释放，而在相同的作用域中定义block并且调用block似乎也多此一举。<br><code>__NSMallocBlock__</code>是在平时编码过程中最常使用到的。存放在堆中需要我们自己进行内存管理。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/block/block-store.jpeg" alt=""></p>
<h4 id="Block-超出作用域还能存在的理由-amp-forwarding-的作用"><a href="#Block-超出作用域还能存在的理由-amp-forwarding-的作用" class="headerlink" title="Block 超出作用域还能存在的理由 &amp; __forwarding 的作用"></a>Block 超出作用域还能存在的理由 &amp; __forwarding 的作用</h4><p>配置在全局变量上的 Block, 从变量作用域外也可以通过指针安全的使用，但是设置在栈上的 Block，如果其所属的变量作用域结束，该Block就被废弃。如果<code>__block</code>（如：<strong>Block_byref_numBlock_0） 变量也会配置在栈上，则该`</strong>block`变量也会被废弃。将 Block 复制到堆上那么变量作用域结束时不受影响，</p>
<blockquote>
<p>impl.isa = &amp;_NSConcreteMallocBlock;<br>而 <code>__block</code> 结构体成员变量 <code>__forwarding</code> 可以实现无论 <code>__block</code> 变量配置在栈上还是堆上都能够正确的访问 <code>__block</code> 变量。通过 Block的复制，<code>__block</code>也一起复制到堆上，就可以同时访问栈上和堆上的<code>__block</code>，只要栈上的结构体实例成员变量 <code>__forwarding</code> 指向堆上的结构体实例。<br>在堆上的 Block 持有 <code>__block</code>变量，<code>__block</code>变量的引用计数 +1， <code>__block</code> 可以被多个 Block 持有。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/block/copy.png" alt=""></p>
</blockquote>
]]></content>
    <summary type="html">
    <![CDATA[<h3 id="block-转-c-源码"><a href="#block-转-c-源码" class="headerlink" title="block 转 c++ 源码"></a>block 转 c++ 源码</h3><p>如下代码 .h .m文件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">//TestClang.h</span><br><span class="line">@interface TestClang : NSObject</span><br><span class="line">+ (void)testBlcok;</span><br><span class="line">@end</span><br><span class="line"></span><br><span class="line">//TestClang.m</span><br><span class="line">#import &quot;TestClang.h&quot;</span><br><span class="line">static int numGlobel = 29;</span><br><span class="line">@implementation TestClang</span><br><span class="line">+ (void)testBlcok &#123;</span><br><span class="line">    //没有截获局部变量 __NSGlobalBlock__</span><br><span class="line">    void(^block1)(void) = ^&#123;</span><br><span class="line">        NSLog(@&quot;just a block&quot;);</span><br><span class="line">            &#125;;</span><br><span class="line">    NSLog(@&quot; block1 = %@&quot;, block1);</span><br><span class="line">    block1();</span><br><span class="line">    static int numStatic = 12;</span><br><span class="line">    int num = 10;</span><br><span class="line">    __block int numBlock = 19;</span><br><span class="line">    __block int numBlock2 = 30;</span><br><span class="line">    __block int numBlockTest = 30;</span><br><span class="line">        void(^block2)(void) = ^&#123;</span><br><span class="line">        NSLog(@&quot;just a block === %d, numStatic = %d numGlobel = %d  numBlock=%d numBlock2=%d numBlockTest = %d&quot;, num,numStatic,numGlobel,numBlock,numBlock2,numBlockTest);</span><br><span class="line">            &#125;;</span><br><span class="line">    num = 33;</span><br><span class="line">    numStatic = 121;</span><br><span class="line">    numGlobel = 129;</span><br><span class="line">    numBlock = 22222;</span><br><span class="line">    block2();</span><br><span class="line">    NSLog(@&quot;block2 = %@&quot;, block2);</span><br><span class="line">&#125;</span><br><span class="line">@end</span><br></pre></td></tr></table></figure></p>
<blockquote>
<p>执行：xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc TestClang.m<br>不要引用其他头文件，以免导出报Error<br>目录下生成了一个TestClang.cpp文件</p>
</blockquote>
<ul>
<li>testBlock 对应的方法变为：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">//_C_ 表示为类方法 _I_ 为实例方法</span><br><span class="line">static void _C_TestClang_testBlcok(Class self, SEL _cmd) &#123;</span><br><span class="line"></span><br><span class="line">    void(*block1)(void) = ((void (*)())&amp;__TestClang__testBlcok_block_impl_0((void *)__TestClang__testBlcok_block_func_0, &amp;__TestClang__testBlcok_block_desc_0_DATA));</span><br><span class="line">    NSLog((NSString *)&amp;__NSConstantStringImpl__var_folders_nc_qvb_bh854tz1y0p1hdk5y6km0000gn_T_TestClang_cf514e_mi_1, block1);</span><br><span class="line">    ((void (*)(__block_impl *))((__block_impl *)block1)-&gt;FuncPtr)((__block_impl *)block1);</span><br><span class="line">    static int numStatic = 12;</span><br><span class="line">    int num = 10;</span><br><span class="line">    __attribute__((__blocks__(byref))) __Block_byref_numBlock_0 numBlock = &#123;(void*)0,(__Block_byref_numBlock_0 *)&amp;numBlock, 0, sizeof(__Block_byref_numBlock_0), 19&#125;;</span><br><span class="line">    __attribute__((__blocks__(byref))) __Block_byref_numBlock2_1 numBlock2 = &#123;(void*)0,(__Block_byref_numBlock2_1 *)&amp;numBlock2, 0, sizeof(__Block_byref_numBlock2_1), 30&#125;;</span><br><span class="line">    __attribute__((__blocks__(byref))) __Block_byref_numBlockTest_2 numBlockTest = &#123;(void*)0,(__Block_byref_numBlockTest_2 *)&amp;numBlockTest, 0, sizeof(__Block_byref_numBlockTest_2), 30&#125;;</span><br><span class="line"></span><br><span class="line">        void(*block2)(void) = ((void (*)())&amp;__TestClang__testBlcok_block_impl_1((void *)__TestClang__testBlcok_block_func_1, &amp;__TestClang__testBlcok_block_desc_1_DATA, num, &amp;numStatic, (__Block_byref_numBlock_0 *)&amp;numBlock, (__Block_byref_numBlock2_1 *)&amp;numBlock2, (__Block_byref_numBlockTest_2 *)&amp;numBlockTest, 570425344));</span><br><span class="line">    num = 33;</span><br><span class="line">    numStatic = 121;</span><br><span class="line">    numGlobel = 129;</span><br><span class="line">    (numBlock.__forwarding-&gt;numBlock) = 22222;</span><br><span class="line">    ((void (*)(__block_impl *))((__block_impl *)block2)-&gt;FuncPtr)((__block_impl *)block2);</span><br><span class="line">    NSLog((NSString *)&amp;__NSConstantStringImpl__var_folders_nc_qvb_bh854tz1y0p1hdk5y6km0000gn_T_TestClang_cf514e_mi_3, block2);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[武功山游记]]></title>
    <link href="http://yoursite.com/2019/01/01/wugongshan/"/>
    <id>http://yoursite.com/2019/01/01/wugongshan/</id>
    <published>2018-12-31T18:14:59.000Z</published>
    <updated>2022-02-27T05:04:29.028Z</updated>
    <content type="html"><![CDATA[<h2 id="续：2018年12月31日，雪后，不一样的武功山"><a href="#续：2018年12月31日，雪后，不一样的武功山" class="headerlink" title="续：2018年12月31日，雪后，不一样的武功山~"></a>续：2018年12月31日，雪后，不一样的武功山~</h2><p>一直想去看冬季的武功山，期待满山白雪皑皑，雪景连接云海。何等壮观。我提出冬季上武功山露营，一家人心痒痒，但是担心大雪封山，临回家才确定上山。但是考虑到露营太冷，改为山脚景区泡温泉，次日上山。抵达景区温泉还下着雪，就着雪泡温泉的感觉真是冰火两重天。泡够时间就 “落荒而逃” ~</p>
<p>次日9点到达武功山脚下，买好门票缆车票。得知山中 -6℃ ，又买好口罩手套等。脚踩防滑链，踏着积雪，向着被白雪覆盖的武功山前进。<br><a id="more"></a></p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123101.jpg" alt=""></p>
<p>做为难得一见雪景的南方人，看到雪就开始兴奋的拍拍拍。沉迷于雪景，粉色和雪景很搭~ 以为大雪封山行程阻塞，遂没带凹造型的纱裙。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123102.jpg" alt=""></p>
<p>乘坐一级索道进入山中，入眼银装素裹，是从未见过的风景。不负严寒，不负此行。山中雾大，拍出的美十不及一。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123103.jpg" alt=""><br><!-- ![](https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123104.jpg) --><br><img src="/2019/01/01/wugongshan/123104.jpg"><br><!-- ![](https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123105.jpg) --><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123106.jpg" alt=""><br><img src="/2019/01/01/wugongshan/123107.jpg"><br><!-- ![](https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123107.jpg) --><br><!-- ![](https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123108.jpg) --><br><img src="/2019/01/01/wugongshan/123108.jpg"><br><img src="/2019/01/01/wugongshan/123109.jpg"><br><!-- ![](https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123109.jpg) --><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123110.jpg" alt=""><br><img src="/2019/01/01/wugongshan/123105.jpg"><br>一路欣赏美景，记录美景，心中还是期待到达山顶，遇见云海。很快就走到了二级索道入口，人不多，不多时便乘上了二级索道。乘着二级索道上冲出云海，娇阳明媚，雪已基本融化，忍不住惊呼。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123112.jpg" alt=""></p>
<p>最惊喜的是，最激动的是，又见云海！！！<br>干净大气，美不胜收。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123111.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123114.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123115.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123116.jpg" alt=""></p>
<p>自恋一下，哈哈。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/123117.jpg" alt=""></p>
<p>制作了一个简单的视频。</p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/1331m.mov"><br>Your browser does not support the video tag.<br></video>


<h2 id="续：2017五一三天徒步武功山，全程40公里。"><a href="#续：2017五一三天徒步武功山，全程40公里。" class="headerlink" title="续：2017五一三天徒步武功山，全程40公里。"></a>续：2017五一三天徒步武功山，全程40公里。</h2><p>出发：晚上从上海坐卧铺到萍乡有6趟车，到达萍乡7~9点。在车厢里你就会发现非常多一起去武功山得驴友，在车厢里就认识了几个妹子，其中有一个还是领队。后来的行程中也一直遇到，非常有缘。出发的气氛就很high。</p>
<p>第一天：上山10公里<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/51wgs1.jpg" alt=""></p>
<p>第二天：山顶20+公里行走<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/51wgs3.jpg" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/51wgs2.jpg" alt=""></p>
<p>第三天：上金顶后下山、晚上乘坐卧铺回上海</p>
<p>住宿：可自己重装背帐篷或者租，也有一些青旅。我们一行四人正好住一间青旅，晚上没有信号，就看夜景+打牌，哈哈</p>
<p>上次看到了日出日落，这次徒步没有特意去看~ 可惜这次没有云海相伴。</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/51wgs4.jpg" alt=""></p>
<h2 id="初识武功山"><a href="#初识武功山" class="headerlink" title="初识武功山"></a>初识武功山</h2><p> 2016年国庆回江西发现一个风景美人不多的游玩好地方 – 武功山</p>
<p>云海很美，乘坐一级索道拍摄的风景,冲出云海的刹那很壮观<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgs.JPG" alt=""><br>半山腰风景<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgs1.JPG" alt=""><br>你可以选择坐二级索道上山顶，去坐索道还有一段路，路上风景也值得一走。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgsm.JPG" alt=""><br>到了山顶，山顶上只有几度，很冷，当时山下是穿短袖的。山顶上有帐篷租，很便宜。有些简易小摊位可以解决食物问题(泡面啥的，比较简陋)，<br>当然你也可以自己背些吃的和保暖衣服(必备)以及帐篷。第二天一大早快到日出的时候就会被叫醒去看日出O(∩_∩)O~</p>
<p><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgs3.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgs4.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgs5.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgs6.JPG" alt=""><br>清晨的风景，草长的好的时候可以滑草，没体验到，遗憾<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgs7.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%e6%ad%a6%e5%8a%9f%e5%b1%b1/wgs8.JPG" alt=""><br><br><br>随便一拍都是美图，值得一登山顶，住一晚上，然后看日出。我们是从萍乡上去的，<br>还有一个路线是从安福上去(据说风景更好)。<br>总的来说性价比很高，我是乘坐索道和租帐篷以及吃饭从上山到下山大概不到500RMB。<br>当然如果你是徒步自己被帐篷和食物上去就不用花钱啦~不过主峰有1918米.</p>
<p>感觉去的很值，非常美，据说春秋去更佳，山顶上到时候会有雪，有机会还要再去(<em>^__^</em>)……</p>
]]></content>
    <summary type="html">
    <![CDATA[<h2 id="续：2018年12月31日，雪后，不一样的武功山"><a href="#续：2018年12月31日，雪后，不一样的武功山" class="headerlink" title="续：2018年12月31日，雪后，不一样的武功山~"></a>续：2018年12月31日，雪后，不一样的武功山~</h2><p>一直想去看冬季的武功山，期待满山白雪皑皑，雪景连接云海。何等壮观。我提出冬季上武功山露营，一家人心痒痒，但是担心大雪封山，临回家才确定上山。但是考虑到露营太冷，改为山脚景区泡温泉，次日上山。抵达景区温泉还下着雪，就着雪泡温泉的感觉真是冰火两重天。泡够时间就 “落荒而逃” ~</p>
<p>次日9点到达武功山脚下，买好门票缆车票。得知山中 -6℃ ，又买好口罩手套等。脚踩防滑链，踏着积雪，向着被白雪覆盖的武功山前进。<br></p>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[on-demand 资源下发]]></title>
    <link href="http://yoursite.com/2018/12/07/%E8%B5%84%E6%BA%90%E4%B8%8B%E5%8F%91/"/>
    <id>http://yoursite.com/2018/12/07/资源下发/</id>
    <published>2018-12-06T20:44:10.000Z</published>
    <updated>2023-10-08T07:19:22.901Z</updated>
    <content type="html"><![CDATA[<h2 id="Apple-On-Demand-Resource"><a href="#Apple-On-Demand-Resource" class="headerlink" title="Apple On-Demand Resource"></a>Apple On-Demand Resource</h2><p> Xcode 可以给资源打 tag，被打 tag 的资源在打包时不会打到包内。需要时下载，测试时可以使用自己的服务器，但是发布到 AppStore 时需要托管到苹果的服务器。<br><a href="https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/index.html#//apple_ref/doc/uid/TP40015083-CH2-SW1" target="_blank" rel="noopener">苹果的 On-Demand Resource 文档</a></p>
<img src="/2018/12/07/资源下发/1.png" title="Xcode 设置tag">
<img src="/2018/12/07/资源下发/2.png" title="也可以直接点击资源设置 tag">
<h2 id="自建资源下发模块"><a href="#自建资源下发模块" class="headerlink" title="自建资源下发模块"></a>自建资源下发模块</h2><p>了解到了苹果 On-Demand Resource 机制，我们可以结合Resource Tags 下的资源不会打包到 ipa 包的特性。设计一套自己的下发机制。<br><a id="more"></a></p>
<h3 id="自动打包上传"><a href="#自动打包上传" class="headerlink" title="自动打包上传"></a>自动打包上传</h3><p>给需要下发的资源打上 tag 标签，xcode 打包时运行脚本上传 tag 包，生成 tag 配置信息 plist 文件，打包完成手动检查 md5 的一致性。<br><img src="/2018/12/07/资源下发/upload.jpg"></p>
<h3 id="按需下载模块设计"><a href="#按需下载模块设计" class="headerlink" title="按需下载模块设计"></a>按需下载模块设计</h3><img src="/2018/12/07/资源下发/download.jpg">
<p>接口和使用参照了苹果的 ondemand 相关接口</p>
<p>上面其实是第一版…. 中间面临新的需求，比如需要保持一个下载进度，让 download view 能随时取到。</p>
<h3 id="自动生成-plist"><a href="#自动生成-plist" class="headerlink" title="自动生成 plist"></a>自动生成 plist</h3><p>使用自带的 PlistBuddy 命令可以把 plist 更新<br><a href="http://www.mengyueping.com/2018/08/13/mac_PlistBuddy/" target="_blank" rel="noopener">PlistBuddy参考</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">echo &quot;start on-demand resource module&quot;</span><br><span class="line">GTagRootPath=&apos;./On-DemandResources&apos; //放 tag 资源包的路径，这里面我放的是已经打包好了 zip包，并标记为 tag resource</span><br><span class="line">echo On-DemandResources path = $GTagRootPath</span><br><span class="line">GTagPlistPath=&apos;./yourconfig.plist&apos; //plist 路径，先生成一个在工程里</span><br><span class="line">echo plist path = $&#123;GTagPlistPath&#125;</span><br><span class="line">GVerion=$(/usr/libexec/PlistBuddy -c &quot;Print CFBundleShortVersionString&quot; &quot;$INFOPLIST_FILE&quot;)</span><br><span class="line">echo version = $&#123;GVerion&#125;</span><br><span class="line">GbundleId=$&#123;PRODUCT_BUNDLE_IDENTIFIER&#125;</span><br><span class="line">echo bundleID = $&#123;GbundleId&#125;</span><br><span class="line"></span><br><span class="line">/usr/libexec/PlistBuddy -c &quot;Set :bundleID $GbundleId&quot; $GTagPlistPath</span><br><span class="line">/usr/libexec/PlistBuddy -c &quot;Set :version $GVerion&quot; $GTagPlistPath</span><br><span class="line">/usr/libexec/PlistBuddy -c &quot;Delete :tags&quot; $GTagPlistPath</span><br><span class="line">/usr/libexec/PlistBuddy -c &quot;Add :tags array&quot; $GTagPlistPath</span><br><span class="line"></span><br><span class="line">index=0</span><br><span class="line">for file in $GTagRootPath/*</span><br><span class="line">do</span><br><span class="line">if test -f $file</span><br><span class="line">then</span><br><span class="line">echo $file 是文件</span><br><span class="line">md5String=$(md5 -q $file)</span><br><span class="line">name=$(basename $file .zip)</span><br><span class="line">size=$(ls -l $file | awk &apos;&#123;print $5&#125;&apos;)</span><br><span class="line">echo size is $size</span><br><span class="line">/usr/libexec/PlistBuddy -c &apos;Add :tags: dict&apos; test.plist</span><br><span class="line">/usr/libexec/PlistBuddy -c &quot;Add :tags:$index:name string $name&quot; $GTagPlistPath</span><br><span class="line">/usr/libexec/PlistBuddy -c &quot;Add :tags:$index:size integer $size&quot; $GTagPlistPath</span><br><span class="line">echo $md5String</span><br><span class="line">/usr/libexec/PlistBuddy -c &quot;Add :tags:$index:md5 string $md5String&quot; $GTagPlistPath</span><br><span class="line">index=$((index+1))</span><br><span class="line">fi</span><br><span class="line">if test -d $file</span><br><span class="line">then</span><br><span class="line">echo $file 是目录</span><br><span class="line">fi</span><br><span class="line">done</span><br></pre></td></tr></table></figure>
<h3 id="check-和-上传脚本"><a href="#check-和-上传脚本" class="headerlink" title="check 和 上传脚本"></a>check 和 上传脚本</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">echo &quot;start ondemand resource archive ......&quot;</span><br><span class="line"># check resource getInfo</span><br><span class="line">echo start get resource info</span><br><span class="line">curl -X POST \</span><br><span class="line">http://your.com/xxx \</span><br><span class="line">-H &apos;Postman-Token: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW&apos; \</span><br><span class="line">-F version=1.0.0 \</span><br><span class="line">-F &apos;tagNames=[&quot;test&quot;]&apos;</span><br><span class="line"></span><br><span class="line">curl -X POST \</span><br><span class="line">http://your.com/xxxx \</span><br><span class="line">-H &apos;Content-Type:multipart/form-data; boundary=----ioqwiokjlfdlk198kdskl8oiidfn9mfdjmji9fd&apos; \</span><br><span class="line">-F version=1.0.0.10 \</span><br><span class="line">-F tagName=test \</span><br><span class="line">-F data=@/Users/xxx/test.zip</span><br><span class="line"></span><br><span class="line">echo &quot;end ondemand resource archive ......&quot;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
]]></content>
    <summary type="html">
    <![CDATA[<h2 id="Apple-On-Demand-Resource"><a href="#Apple-On-Demand-Resource" class="headerlink" title="Apple On-Demand Resource"></a>Apple On-Demand Resource</h2><p> Xcode 可以给资源打 tag，被打 tag 的资源在打包时不会打到包内。需要时下载，测试时可以使用自己的服务器，但是发布到 AppStore 时需要托管到苹果的服务器。<br><a href="https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/index.html#//apple_ref/doc/uid/TP40015083-CH2-SW1" target="_blank" rel="noopener">苹果的 On-Demand Resource 文档</a></p>
<img src="/2018/12/07/资源下发/1.png" title="Xcode 设置tag">
<img src="/2018/12/07/资源下发/2.png" title="也可以直接点击资源设置 tag">
<h2 id="自建资源下发模块"><a href="#自建资源下发模块" class="headerlink" title="自建资源下发模块"></a>自建资源下发模块</h2><p>了解到了苹果 On-Demand Resource 机制，我们可以结合Resource Tags 下的资源不会打包到 ipa 包的特性。设计一套自己的下发机制。<br></p>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="iOS" scheme="http://yoursite.com/categories/iOS/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[xcodeproj]]></title>
    <link href="http://yoursite.com/2018/11/30/xcodeproj/"/>
    <id>http://yoursite.com/2018/11/30/xcodeproj/</id>
    <published>2018-11-30T14:53:20.000Z</published>
    <updated>2018-12-07T02:59:38.000Z</updated>
    <content type="html"><![CDATA[<p>xcodeproj</p>
<p>xcodeproj 实际上是一个文件夹，打开后可以看到 project.pbxproj, xcuserdata, project.xcworkspace. </p>
<ul>
<li>project.pbxproj: “在 project 里的 project”，是 xcodeproj 最主要的文件</li>
<li>project.xcworkspace: 一个子文件夹，存储了用户 workspace 的相关信息</li>
<li>xcuserdata: 另外一个文件夹，里面也是用户相关的一些信息<a id="more"></a>
</li>
</ul>
<p>project.pbxproj 是 Next style 的 plist，描述了一个树状结构，最顶层是 RootElement.</p>
<ul>
<li>Root Element<ul>
<li>PBXBuildFile</li>
<li>PBXBuildPhase<ul>
<li>PBXAppleScriptBuildPhase</li>
<li>PBXCopyFilesBuildPhase</li>
<li>PBXFrameworksBuildPhase</li>
<li>PBXHeadersBuildPhase</li>
<li>PBXResourcesBuildPhase</li>
<li>PBXShellScriptBuildPhase</li>
<li>PBXSourcesBuildPhase</li>
</ul>
</li>
<li>PBXContainerItemProxy</li>
<li>PBXFileElement<ul>
<li>PBXFileReference</li>
<li>PBXGroup</li>
<li>PBXVariantGroup</li>
</ul>
</li>
<li>PBXTarget<ul>
<li>PBXAggregateTarget</li>
<li>PBXLegacyTarget</li>
<li>PBXNativeTarget</li>
</ul>
</li>
<li>PBXProject</li>
<li>PBXTargetDependency</li>
<li>XCBuildConfiguration</li>
<li>XCConfigurationList</li>
</ul>
</li>
</ul>
<p>除根节点外每一个节点都有一个 reference，节点间通过引用这个 reference 表明包含的关系.</p>
<p>下面是一个 pbxproj 文件的内容示例，objects 里包含有所有的子节点， rootObject 存储的是 PBXProject 的 reference.<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">// !$*UTF8*$!</span><br><span class="line">&#123;</span><br><span class="line">    archiveVersion = 1;</span><br><span class="line">    classes = &#123;</span><br><span class="line">    &#125;;</span><br><span class="line">    objectVersion = 45;</span><br><span class="line">    objects = &#123;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    &#125;;</span><br><span class="line">    rootObject = 0867D690FE84028FC02AAC07 /* Project object */;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>PBXProject 则包含了如配置，target 等信息，其中 mainGroup 就是我们打开项目看到的文件层级列表中最顶层的那个节点<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">29B97313FDCFA39411CA2CEA /* Project object */ = &#123;</span><br><span class="line">        isa = PBXProject;</span><br><span class="line">        buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject &quot;XXX&quot; */;</span><br><span class="line">        compatibilityVersion = &quot;Xcode 2.4&quot;;</span><br><span class="line">        developmentRegion = English;</span><br><span class="line">        hasScannedForEncodings = 1;</span><br><span class="line">        knownRegions = (</span><br><span class="line">                English,</span><br><span class="line">                Japanese,</span><br><span class="line">                French,</span><br><span class="line">                German,</span><br><span class="line">                en,</span><br><span class="line">        );</span><br><span class="line">        mainGroup = 29B97314FDCFA39411CA2CEA /* XXX*/;</span><br><span class="line">        projectDirPath = &quot;&quot;;</span><br><span class="line">        projectRoot = &quot;&quot;;</span><br><span class="line">        targets = (</span><br><span class="line">             8D1107260486CEB800E47090 /* XXX*/,</span><br><span class="line">        );</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<p>PBXGroup 的 children 字段的值是一个 reference 数组, reference 可以指向另外一个 PBXGroup，也可以指向一个文件节点 PBXFileReference<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">015554500155562000000001 /* SlimmingResouces */ = &#123;</span><br><span class="line">	isa = PBXGroup;</span><br><span class="line">	children = (</span><br><span class="line">		01555120015554C000000001 /* Help.zip */,</span><br><span class="line">	);</span><br><span class="line">	name = SlimmingResouces;</span><br><span class="line">	sourceTree = &quot;&lt;group&gt;&quot;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<p>添加一个文件的过程：</p>
<ul>
<li>创建一个 PBXFileReference 对象 fileRef</li>
<li>创建或者查找到一个 PBXGroup，将 fileRef 加入</li>
<li>用 fileRef 创建一个 PBXBuildFile buildFile</li>
<li>找到待加入的 PBXNativeTarget target</li>
<li>找到 target 的 PBXResourcesBuildPhase 将 fileRef 加入<br></li>
</ul>
<h2 id="links"><a href="#links" class="headerlink" title="links"></a>links</h2><p><a href="http://www.monobjc.net/xcode-project-file-format.html" target="_blank" rel="noopener">http://www.monobjc.net/xcode-project-file-format.html</a></p>
<p><a href="https://github.com/google/zopfli" target="_blank" rel="noopener">https://github.com/google/zopfli</a></p>
<p><a href="https://github.com/google/brotli" target="_blank" rel="noopener">https://github.com/google/brotli</a></p>
]]></content>
    <summary type="html">
    <![CDATA[<p>xcodeproj</p>
<p>xcodeproj 实际上是一个文件夹，打开后可以看到 project.pbxproj, xcuserdata, project.xcworkspace. </p>
<ul>
<li>project.pbxproj: “在 project 里的 project”，是 xcodeproj 最主要的文件</li>
<li>project.xcworkspace: 一个子文件夹，存储了用户 workspace 的相关信息</li>
<li>xcuserdata: 另外一个文件夹，里面也是用户相关的一些信息</li></ul>]]>
    
    </summary>
    
      <category term="iOS" scheme="http://yoursite.com/tags/iOS/"/>
    
      <category term="工具" scheme="http://yoursite.com/categories/%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[冲绳四日三晚游记]]></title>
    <link href="http://yoursite.com/2018/09/12/%E5%86%B2%E7%BB%B3%E5%9B%9B%E6%97%A5%E4%B8%89%E6%99%9A%E6%B8%B8%E8%AE%B0/"/>
    <id>http://yoursite.com/2018/09/12/冲绳四日三晚游记/</id>
    <published>2018-09-11T18:06:30.000Z</published>
    <updated>2023-10-15T08:34:52.783Z</updated>
    <content type="html"><![CDATA[<p>行程时间： 9月7日 - 9月10日<br>提前一个月的时间定了吉祥航空的往返航班，2700左右/人。出发时下午5点，回程是下午7点。<br>行程酒店：美国村门巴公寓酒店，酒店外就是沙滩。<br>冲绳9月份是台风季，天气说不准，最好提前做下攻略。</p>
<h3 id="Day-1"><a href="#Day-1" class="headerlink" title="Day 1"></a>Day 1</h3><p>启程，第一天出发的时间是北京时间5点，到冲绳大概2个小时，不过因为冲绳是早一个小时的，所以到冲绳是当地时间晚上8点了。预订的接机去美国村的酒店，400RMB。<br>抵达冲绳从机场出来就感觉非常干净，各种建筑街道啥的都非常干净，各种基础设施做的也很贴心。约的日本的司机来接机的，人很有礼貌，会一点英语, 大概花了50分钟左右到美国村。入住之后就去觅食，找到了上一波推荐去的一家叫 xx食堂的店，吃了猪豚骨饭，另外在冲绳大众点评也挺好用(<em>^__^</em>)。<br>美国村夜景<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9701.JPG" alt=""></p>
<a id="more"></a>
<h3 id="Day-2"><a href="#Day-2" class="headerlink" title="Day 2"></a>Day 2</h3><p>第二天的清晨景色~ 天气很好~ 开心~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9803.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9804.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9802.JPG" alt=""></p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/IMG_0323.mov"><br></video>

<p>第二天安排：部濑名海中公园 -&gt; 残波狎 -&gt; 美军基地展望台</p>
<p>第一天晚上休整完已经是冲绳的10点了，然后发现第二天的行程还没定！只能在酒店附近瞎晃一天的样子，而且看起来第二天天气应该不错，觉得还是应该出去玩儿，这个时候多亏之前认真做了攻略，记得在马蜂窝上有一个当天都可以订包车行程的项目，咨询在线客服发现客服9点已经下班了，不管三七二十一就直接下单了。还好下单完几分钟后就有客服妹子加了微信，跟她约好了第二天的行程和司机接送时间，十分钟搞定！包车8小时1049RMB，日文司机。其实这个非常划算，比自己打车便宜很多，打车去部濑名还中公园一个小时就得要500多RMB，而且从那边回来是很难打到车的，墙裂推荐包车！！！<br>主要游玩点时间图<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9801.jpg" alt=""><br>这个图上的时间非常准确，google地图上显示的堵车在我来说根本不叫堵车，非常友好，哈哈哈。<br>幸好约好了包车行程，否则一天在美国村玩儿真是有点浪费啦，司机9店准时在酒店门口等着，是一个50多岁的大叔，人非常好，一直用 google 翻译跟我们聊天，而且他还去过两次上海，不过是三十多年前，会说几个中文词，行程很愉快。包车这块没有其他的附加费用。</p>
<h4 id="部濑名海中公园"><a href="#部濑名海中公园" class="headerlink" title="部濑名海中公园"></a>部濑名海中公园</h4><p>推荐游玩时间：3-5小时<br>网上很多人推荐必玩的项目，玻璃底船和海中展望塔，买了套票2060日元。这边还有很多其他项目，之前做攻略的时候想玩的浮潜和海上拖拽伞，都比单次报团去参加便宜很多，而且美国村有的项目还不接送。所以能在海中公园的行程中包含也是非常不错的选择。可惜我没有安排去其他活动，其实时间上来说完全够的，遗憾~~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9810.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9805.JPG" alt=""><br>第一张图就是海底展望塔啦，最后一张图就是坐玻璃底船拍到的，船有点晃，行程20分钟，就是直接看海里的鱼，还能看到珊瑚之类的。其实海中展望塔也只是去海底透过玻璃窗口看海里的景色，不过也算值得票价拉。而且周边的景色很好~2个小时很快就过去了，在点评上看了一下附近的美食，有很多家，找了一家评价不错的餐厅吃饭，出了公园司机大叔就开着车来接我们去吃饭了，我感觉他应该是很多时间都在车上的，真的很负责~<br>午餐：味道很好 3700日元左右<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9807.JPG" alt=""></p>
<h4 id="残波狎"><a href="#残波狎" class="headerlink" title="残波狎"></a>残波狎</h4><p>推荐游玩时间：1-2小时<br>残波狎景色很好，可以沿着2KM长的海岸线慢慢的走，吹吹风。快到的时候下起了大雨，司机大叔让我们在车里等几分钟，几分钟之后雨就停了，不过司机大叔还是给了我们两把伞，冲绳的出租车里面都备有伞，所以如果是包车，完全不需要自己带伞的哦。<br><img src="/2018/09/12/冲绳四日三晚游记/9806.JPG"></p>
<h4 id="美军基地展望台"><a href="#美军基地展望台" class="headerlink" title="美军基地展望台"></a>美军基地展望台</h4><p>emm… 这次行程最想去的是上面的两个地方，这个美军基地只是顺便来看下，来了之后发现是在美军基地对面的一个楼上，有一个望远镜，投币100日元可以看两分钟对面~ 感觉可以把时间放在海中公园的游玩上，不是很有意思就在楼上喝了一杯咖啡，休息了一下就返回酒店啦~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9808.JPG" alt=""></p>
<h4 id="美国村日落海滩"><a href="#美国村日落海滩" class="headerlink" title="美国村日落海滩"></a>美国村日落海滩</h4><p>预计回程是5点，早了二十多分钟回来，趁着天早去酒店旁边的沙滩玩儿，游游泳，看日落，不过天气不是很适合看日落，云层很厚，不过坐着吹吹海风还是不错哦~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9811.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9809.JPG" alt=""></p>
<h4 id="药妆店购物"><a href="#药妆店购物" class="headerlink" title="药妆店购物"></a>药妆店购物</h4><p>美国村附近的药妆店很多，人也巨多，帮朋友带了些药妆，购物到快十点~ 泪目。</p>
<h3 id="Day-3"><a href="#Day-3" class="headerlink" title="Day 3"></a>Day 3</h3><p>第三天安排：万座毛 -&gt; 美丽水族馆 -&gt; 古宇利岛<br>和同事一行9人包了一辆车，10小时行程，因为还有两个同事在那霸市区，多了一个小时车程，而且水族馆和古宇利岛比较远，所以安排的是10小时的包车行程，行程包车2088RMB，水族馆的门票在水族馆附近的便利店买的打折票，折后1660日元，原价1860日元，去的朋友可以问问司机或者知道的人看看可以在哪个便利店买打折票哦~不是所有的便利店都会打折哒。算下来人均333RMB。如果是报大巴团的话是199RMB含门票，行程和游玩点游玩时间差不多。只不过包车比较自由啦~</p>
<h4 id="万座毛"><a href="#万座毛" class="headerlink" title="万座毛"></a>万座毛</h4><p>游玩时间：20分钟<br>万座毛如果天气阳光好的话去看非常漂亮，我们比较幸运的是赶在大雨来临之前到了万座毛，只不过万座毛也就是和拍拍照看一看，活动范围只是一个大概几百平米的草坪而已啦~ 不过我们真的太幸运，天气巨好，海水巨蓝。附上美图~<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9901.JPG" alt=""><br>离开不到几分钟就开始下大雨了，所以万座毛这个地方还是非常看天气的，运气好的话就能看到美景哦~</p>
<h4 id="海洋博公园"><a href="#海洋博公园" class="headerlink" title="海洋博公园"></a>海洋博公园</h4><p>游玩时间：3-4小时<br>之前没有去过任何海族馆，进到海水族馆很兴奋，看到很多没见过的五颜六色的鱼、海龟、海牛…黑潮海水族馆前的玻璃面板看着各种 中午的午饭也是在海水族馆解决的，稍微错开一点饭点去吃的话是不错的选择，我们在看完1：00的海豚表演之后去吃的，就是在黑潮海水族馆旁边的咖啡馆吃的哦，不过贴着玻璃的座位需要预订的，怎么预定没有提前了解，有需要的话可以提前了解下。价格也不贵的，基本是600日元以下的主食、饮料。意面味道还不错~。海族馆可以反复进出，不过你如果想在出口进入的话可以让工作人员给你的手上盖个海豚章，就可以从出口处进去啦，黑潮海水族馆离出口比较近，所以我们吃饭的时候就是从出口进入的。<br>海水族馆的各种小可爱<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9902.JPG" alt=""><br>海豚表演，海豚很听话，很可爱，但是看的时候莫名心疼啊<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9903.JPG" alt=""></p>
<h4 id="古宇利岛"><a href="#古宇利岛" class="headerlink" title="古宇利岛"></a>古宇利岛</h4><p>游玩时间：45分钟<br>古宇利岛是冲绳的一个离岛，有一个桥能直接去岛上而闻名，可以在岛上的沙滩上玩一玩，游游泳，游乐设施相对比较简陋。我们去的时候下雨了，大部分人没带泳衣之类的，所以只能撑伞在海边走一走。在桥上看的时候海水很蓝，如果是大晴天的话应该会非常美了，可惜这次没有那么幸运惹~哼唧唧 o(╥﹏╥)o，附上两张对比图。<br>我去的时候的景色<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9904.JPG" alt=""><br>人家去的时候的景色<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9905.jpg" alt=""></p>
<h4 id="DFS购物"><a href="#DFS购物" class="headerlink" title="DFS购物"></a>DFS购物</h4><p>同行同事住在 DFS 附近，我们正好就到 DFS 下车，帮自己和朋友买了些护肤品~ (<em>^__^</em>)</p>
<h4 id="美国村"><a href="#美国村" class="headerlink" title="美国村"></a>美国村</h4><p>住的地方附近也很有特色，很喜欢那种感觉，很舒服的小镇。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/1.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/2.JPG" alt=""></p>
<h3 id="Day-4"><a href="#Day-4" class="headerlink" title="Day 4"></a>Day 4</h3><p>下午7点的飞机，没有特别的行程安排，10点退房之后就打车去了永旺商场，出租车的司机师傅的中文很好，在跟着中国留学生学中文，聊了一路。买了些小东西和特产，在一个宫崎骏电影的小玩具店里买到很多喜欢的手指玩偶，还有一些糕点。<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/91001.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/91002.JPG" alt=""><br>买完特产想直接坐轻轨去机场，打算走路去最近的轻轨站台，结果。。。拖着行李走了一个小时，后来又回到了买特产的商场（永旺）打车打机场。不推荐使用google map 来查乘车路线，可以下载日本的交通类APP。<br>花了半小时走到废弃的站台,在某个不知名的村里迷路了很久<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/91003.JPG" alt=""><br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/91004.JPG" alt=""><br>那霸机场真的很小，只有两个区可以值机，而且只能在起飞前两小时开始值机，去早了也只能等了。</p>
<h3 id="The-End"><a href="#The-End" class="headerlink" title="The End"></a>The End</h3><p>行前必备品清单：</p>
<ul>
<li>4G 电话卡，淘宝就可以买了，网速不错，流量超了会限速的很厉害。不过你正常用不看视频直播之类的还行，而且酒店一般会有 WIFI。</li>
<li>插座转接口，有的酒店会提供，但是我去的公寓酒店并没有，还好自己带了一个。冲绳是两孔的插座，所以我带的是两孔转三孔的，然后带了三孔排插。</li>
<li>少量现金，我们三个人换了5W日元，结果回来的时候还有1W6没用完，基本是用来在餐厅吃饭和打车。很多超市和便利店都可以用支付宝或者微信，其实打车也可以用微信的哦，不过是蓝色车顶的那种。</li>
<li>防晒用品</li>
</ul>
<p>结算：<br>机票：8236RMB 酒店：4751RMB 包车：2400RMB 打车+吃饭：2500RMB 购物+随手礼：5500RMB<br>总计花费：23387RMB</p>
<p>冲绳 地图<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/3.JPG" alt=""></p>
<p>合集视频</p>
<video controls="controls" height="360" width="640"><br>  <source src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/vue_video_cache_filtered_spliced.mov"><br></video>
]]></content>
    <summary type="html">
    <![CDATA[<p>行程时间： 9月7日 - 9月10日<br>提前一个月的时间定了吉祥航空的往返航班，2700左右/人。出发时下午5点，回程是下午7点。<br>行程酒店：美国村门巴公寓酒店，酒店外就是沙滩。<br>冲绳9月份是台风季，天气说不准，最好提前做下攻略。</p>
<h3 id="Day-1"><a href="#Day-1" class="headerlink" title="Day 1"></a>Day 1</h3><p>启程，第一天出发的时间是北京时间5点，到冲绳大概2个小时，不过因为冲绳是早一个小时的，所以到冲绳是当地时间晚上8点了。预订的接机去美国村的酒店，400RMB。<br>抵达冲绳从机场出来就感觉非常干净，各种建筑街道啥的都非常干净，各种基础设施做的也很贴心。约的日本的司机来接机的，人很有礼貌，会一点英语, 大概花了50分钟左右到美国村。入住之后就去觅食，找到了上一波推荐去的一家叫 xx食堂的店，吃了猪豚骨饭，另外在冲绳大众点评也挺好用(<em>^__^</em>)。<br>美国村夜景<br><img src="https://xylbolg-1256213633.cos.ap-shanghai.myqcloud.com/%E5%86%B2%E7%BB%B3/9701.JPG" alt=""></p>]]>
    
    </summary>
    
      <category term="游记" scheme="http://yoursite.com/tags/%E6%B8%B8%E8%AE%B0/"/>
    
      <category term="游记" scheme="http://yoursite.com/categories/%E6%B8%B8%E8%AE%B0/"/>
    
  </entry>
  
</feed>
