2017.5.7

 • 

这两周在路上花了不少时间,事情呢做了一些,但都不怎么顺。

面试&笔试

回长沙考期中,顺便去腾讯面试。当天长沙下雨,立珊专线还堵了一下,迟了半小时到。面试官大致问了以下内容:

  • KVO / KVC,问了下概念。
  • 线程相关,问我平时用什么。我说 NSOperation 和 GCD,然后问我用过 NSThread 没,我说没怎么用过。
  • 问 Atomic / Noatomic 的含义
  • 让我写一个线程安全的 Singleton,我用 Dispatch_once 写的,不过貌似写的不全
  • 让我乱序向一个 NSMutableArray 插入 0 - 99 这一百个数字。我问能不能用 NSSortDescriptor 然后在 Block 里面随机返回真假,面试官有些疑惑的样子,一直问我这个排序到底是怎么拍的,复杂度多少,我说这个就是苹果的内部实现,我也不敢打包票。
  • 问项目经验,特别问了 RavenTech 的 EVA VR。听完项目后问我为什么要给 Unity 写 Native WebBroswer to Texutre2D 的 Plugin,我说 Unity 只 pack 了一个 Mono Runtime,至于打包 WebKit 这种事情不是 Unity 关心的,只能自己写。估计面试官以为 Unity 应该有 Universal 的那种浏览器 UI,所以问我这个问题。
  • 问我挂过科没,回答上学期刚挂过。问我为什么有这么多项目经验,我说翘课,除了考试以外都在写项目。面试官说那是挺胆大的,我也不好揣摩意思回答,只能耸耸肩。
  • 最后问我有什么想问的没,我很实诚地回答没有。后来听同学说应该回答有问题的以显示自己很有诚意。

面完当天晚上查,显示没有过。

至于阿里的笔试,Objective-C 的运行环境是用 GNUStep 搭建的,编译也很严格,有 Warning 就不能过。我在本地 Xcode 写好的代码在在线评测上不能用,因为总会有各种类型转换的警告,比如 id 到 NSNumber,最后索性放弃了。

至于感受,校招不关心你有多少 star,自己最好还是多看看算法和数据结构,还有就是面试这种东西,还是要准备下的,不是说仗着自己忙就不去看,很多东西就是在公司用不到忘掉,但临面试看下就又记得的。

Board For GitHub

这是一个去年十月份写的开源小项目,后来有意向把这个软件上架到 Mac App Store,就又加了些功能,例如 NSTouchBar,NSUserNotifcation。结果苹果的审核团队连续五次拒绝了上架请求,原因还是和写 ArtWall 的时候一样:用户体验和用 Safari 类似。不同的是 ArtWall 是货真价实的 Native Application,而 Board For GitHub 是真的 WebView 软件,就更难过审了。
最后决定在 MAS 外分发。原本想用 DevMate 做试用功能集成 + Stripe 付款,后来发现 Paddle 直接把这两个功能都合并在了一个 SDK 里,有 Cocoa 和 JS 的版本,就很方便。但 Paddle 不包括自动升级功能,于是又用了 HockeyApp 做了自动升级。目前的发布版本可见 https://justinfincher.github.io/BoardForGitHub-Landing/

日常

和喵喵一起看了几部电影。周末窝在家里打 Gang Beasts 和 Broforce。学校管的越来越严了,我不在长沙后据说又加了脸部打卡什么的,但我已经没有精力去对付这些事情了。
回学校后发现其实还是暗流涌动,学院里同学平时都是一副不关心工作的样子,暗地里都在忙着找这个那个实习。
差不多两三年来我第一次对自己走的这个方向是不是对的有了怀疑。面试没过让我有点疑惑,在学校呆的一周发现学校有事情要对付,公司因为我回长沙也少发了些工资,和妹子有时候会有误解,让我在四月底有一种什么都不好了的感觉。
银河护卫队 2 的 Soundtrack 挺好听。

2017.4.25

 • 

最近烦心事挺多的。
回长沙考期中,然后和妹子订好了去杭州和北京的票。突然一门课的考试向后推延两天,定的票几乎全废,想要再改签也不容易,毕竟临近五一了。
WWDC 奖学金没过。我认识的两三个国内大学在读的 iOS 开发都没过,反倒是有一些苹果设置教育点的大学里的学员过的比较轻松。在这就不讨论到底公不公平的事情了,只是对于我来说,今年没过意味着短期内是不可能参加 WWDC 了:去年因为在渡鸦写 VR 没提交,今年提交了没过,明年要忙毕设,后年就在正式工作没学生这个身份了。
我从北京回长沙后,我负责的项目只能交给另一个同事来做,而从三月份重构到现在,整个 Unity 工程已经不是那位同事熟悉的样子了。我只能在长沙远程打电话或者发消息告诉同事项目的架构以及工具链如何用。然后 HR 和我说回学校考试算缺勤,扣了一大半工资。虽然知道会有这个规定,但我觉得还是有些寒心的,毕竟考虑到断断续续一年多来在公司加班,下班后留公司干活这些事情。
还是有些令人高兴的事情的,等考完回北京后再写吧。

My WWDC 2017 Playground For Application

 • 

From noreply-appledev@email.apple.com
Your WWDC Scholarship Status
Thank you for applying for a WWDC 2017 Scholarship. We appreciate your hard work and enjoyed seeing your creativity. With an extraordinary number of engaging Swift playground submissions, the selection process was not easy. We regret that we are unable to offer you a scholarship this year.

Now Open Sourced at https://github.com/JustinFincher/WWDC-17-Scholarship-Project

Unity Custom Inspector Value Saving

 • 

最近发现 SetDirty 不太好用,总是没办法让自定义 Inspector 里面的值序列化。然后在
https://docs.unity3d.com/ScriptReference/EditorUtility.SetDirty.html 这里发现有一段话:

NOTE: ''Prior to Unity 5.3, this was the primary method of marking objects as dirty. From 5.3 onwards, with the introduction of Multi-Scene Editing, this function should no-longer be used for modifying objects in scenes. Instead, you should use Undo.RecordObject prior to making changes to the object. This will mark the object's scene as dirty and provide an undo entry in the editor.''

看了下 Undo.RecordObject 的文档,唯一的不好是 EditorGUI.BeginChangeCheck() 和 EditorGUI.EndChangeCheck() 需要一前一后,对于继承的自定义 Custom Editor 有些难办,因此用 Action 把代码变成一个 Code Block:

Helper Code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
#if UNITY_EDITOR

using UnityEditor;
using UnityEditor.SceneManagement;
 
namespace FinGameWorksEditor.Helper
{
	public static class FGWEditorUtility
	{	
		/// <summary>
		/// Helper Function to use the Undo In Custom Inspector
		/// </summary>
		/// <param name="guiCode">code block to write editorguilayout stuff</param>
		/// <param name="targetCode">code block to write updating target variable stuff</param>
		/// <param name="target">the target monobehavior</param>
		public static void RecordUndoBlock(Action guiCode,Action targetCode,UnityEngine.Object target)
		{
			EditorGUI.BeginChangeCheck();
			guiCode();
			if (EditorGUI.EndChangeCheck())
			{
				Undo.RecordObject(target, "Changed");
				targetCode();
			}
		}
	}
}
#endif

Usage Example

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.SceneManagement;
using FinGameWorks.Helper;

#if UNITY_EDITOR
using UnityEditor;
using FinGameWorksEditor;
using FinGameWorksEditor.Helper;
using FinGameWorks.Controller.SceneManagement;

namespace FinGameWorksEditor.Controller.SceneManagement
{
    [CustomEditor(typeof(FGWSceneLoader), true)]
    public class FGWSceneLoaderEditor : FGWMonobehaviorEditorBase
    {
        public List<string> scenesInBuild
        {
            get
            {
                if (_scenesInBuild == null)
                {
                    _scenesInBuild = FGWSceneHelper.getAllSceneNameInBuild();
                }
                return FGWSceneHelper.getAllSceneNameInBuild();;
            }
        }
        private List<string> _scenesInBuild;

        public FGWSceneLoader loader
        {
            get { return (FGWSceneLoader)target; }
        }

        public override void OnInspectorGUI()
        {
	        base.OnInspectorGUI();
	        bool loadSceneOnStart = false;
	        int sceneIndexToLoad = 0;
	        FGWEditorUtility.RecordUndoBlock(()=>
	        {
	        	GUILayout.Label("Parameters",EditorStyles.boldLabel);
		        loadSceneOnStart = EditorGUILayout.Toggle("Load Scene On Start", loader.loadSceneOnStart);
		        sceneIndexToLoad = EditorGUILayout.Popup("Scene To Set",loader.sceneIndexToLoad,scenesInBuild.ToArray());
		        
		        GUILayout.Label("Action",EditorStyles.boldLabel);
		        if (GUILayout.Button("Refresh Scene List"))
		        {
			        _scenesInBuild = null;
			        foreach (var item in scenesInBuild)
			        {
				        Debug.Log(item);
			        }
		        }
	        },
	        ()=>{
	        	loader.loadSceneOnStart = loadSceneOnStart;
		        loader.sceneIndexToLoad = sceneIndexToLoad;
	        },loader);
        }
    }

}


#endif

namespace FinGameWorks.Controller.SceneManagement
{
    public class FGWSceneLoader : MonoBehaviour
	{
		public bool loadSceneOnStart;
        public int sceneIndexToLoad;

        void Start ()
        {
            if (loadSceneOnStart)
            {
                SceneManager.LoadScene(sceneIndexToLoad);
            }
        }

        void Update () {

        }
    }
}

Epoch Dev Blog 8

 • 

事务繁忙,从去年十月份就没怎么再动过。不过最近把 Epoch 升级到了 Unity 5.6,现在 build 了一个 WebGL 的版本,如果有兴趣的同学可以去玩下。
点这里进入 WebPlayer (Hosted on itch.io)

操作:目前的操作是主要面向手机的,网页版或许会比较难以控制。网页加载完成后,鼠标按住上下滑动可以加减速。操控由左下角的摇杆控制。若提示内存不足,请刷新浏览器。推荐电脑端 Chrome。目前 macOS 10.12 + Chrome 57.0.2987.133 (64-bit) 上测试通过。

Fun Fact:目前 Epoch 在手机上的编译大小只有 60 MB,其中一大半都是飞船的贴图和模型 —— 星球部分只有代码,贴图和模型完全是实时生成。

2017.4.10

 • 
女朋友&吃吃喝喝

妹子来北京找我玩。带着妹子见了一些互联网圈子里的朋友和同事。两个人前半段时间在外面吃吃喝喝看电影逛街,后半段就主要窝在家里。中间尝试在家里继续写 Cetacea For Mac,妹子颇有兴趣地看了会,然后就困了。所以就在一起用电脑看 YouTube 和 Bilibili。

Cetacea For Mac

重构了一部分,也加了一部分功能。highlight.js 的基本嵌入工作完成,因此 Preview Panel 的功能趋于可用。Markdown Parser 的正则仍在修改,也在尝试实现一套 Incremental Parser。除此以外,主题功能也在逐步更新中,目前的效果如图。

WWDC Scholarship 2017

我的 Swift Playground 叫 Golf GO。Golf GO 是一个使用了 SceneKit,ModelIO,GamePlayKit 的地图过程生成的高尔夫球游戏。写 WWDC 奖学金申请的体会是 Swift Playground 并不是一个“严肃”的编程平台,包括如果不通过设置 plist 就不能关掉 logging,所有类都要写在一个文件里,各种放在 Single Page Application 模板里运行没问题的代码会在 Playground 里无故出错。除此以外,我也再也不想写 Swift 了。

WebDrop

更新了一个版本:1.0.1,Related Issue:WebDrop/issues/2

电影&音乐

看了攻壳机动队美女和野兽,听了 The Wheel by SOHN

2017.3.30

 • 

三月就快结束了的样子。
辅导员中间让我回学校开会。我在北京自是没办法赶回去。谦哥帮我打了电话给辅导员,但结果也不是很理想。
有趣的是辅导员开了一个就业群,在里面发一些小公司的实习信息。就发布的消息来说,自是我看不上的。有趣的地方就在这里,真正的 BAT 大厂实习,肯定是不会在长沙的,学院拦着不让去。却在学院内部搞什么简历大赛,就业宣讲会,就业自我评测,发一些长沙本地小公司的实习消息,着实让人有一种眼界狭隘的感觉。
我对这个学校,在一点点的摩擦中,算是一点好感都没有了。我妈总是觉得我此话太过,不过我自己的考虑是,第一,如果不在这个学校学计算机,我目前的发展有很大可能会变的更好。第二,我没有拒绝已经进入这个学校的事实,我自己当初选的学校,我可以说后悔,但我绝没有不承认事实,也绝没有自暴自弃,相反我还在积极地通过自己的方式去解决在一个落后的学校里让自己更好地发展的问题。第三,在此基础上,我觉得我算是做到应有的份上了。我不会因为在某个学校就把自己限制在这个学校的羸弱水平上,不会因为学院里的一两句话把自己宝贵的几年浪费掉。我通过自己的一些努力在北京实习,保证自己毕业后立马能转正,职业路途上没靠过一次学校的名声和好处,也不需要别人关照我的毕业去向,这个时候我说一句对学校没什么好感,似乎也没什么问题。还是那句话,我自己做了选择,不管结果好坏,我带着结果继续在埋头苦干,没有逃避,别人还能要求什么呢。

2017.3.25

 • 
天气

北京又开始降温。长沙忽冷忽热能连续两周下雨的天气,我在那快三年来没怎么感冒过,反而在北京的第一个月就感冒了。早上醒来喉咙还有些疼,空气也不是那么好,我是个容易过敏的人,走在路上也总觉得痒痒的。

工作

上一篇说过入职百度前发生了一件事情搞得大家都不愉快。我还在学校的时候发了些消息说想找北京/杭州的实习,后面陆陆续续也聊了很多公司。然后我在入职的那天需要选择究竟是入职百度实习生还是小米实习生。当然选择公司当然不是看起来那么简单,因为两边都有朋友,去哪家另一家的人都会失望吧,即使嘴上说着“你选工作是看你自己未来的”。
目前是跟着之前的公司一起入职了百度。

目前在智能硬件部门的工作也不是很顺利。前司在合并入大厂后,整个工作流还是像之前一样随性,我心目中大厂应该有的单元测试,自动化构建,文档什么的都没有。我觉得最不爽的就是文档这件事情,前年八月底算是要回学校,赶着最后几天做了一套简单的 Unity 内调用 Doxygen 的文档生成系统,把内部项目的脚本大致都标注了下,为此还错过了当时的 Sketch Meetup。然后我回到学校上大三上学期,等寒假后我回来发现文档基本没人读,这个我倒无所谓,但没人更新新写的代码的文档对我要重构的任务来说就很困难了。整个项目中在我上学的时候改了很多 API,作为一个在 Android 上以一个 Activity 存在的 Unity 项目,某些 Native 接口又不是那么好调试,没办法通过调接口来快速明白接口是干什么的,没了文档更是什么都不知道。我只能去问之前负责的一些人,但回答要么很模糊要么就很不耐烦,最后还要因为某些指向不明的接口争执一番。
上周末在公司加班想着重构能 20 号结束,然后我有些空闲时间给 WWDC 奖学金写东西。毕竟去年的 WWDC 奖学金就是因为在渡鸦做 Gear VR 耽误了,今年不能再因为同一家公司耽误这事,明年大四了就准备毕业设计哪还有时间去投奖学金。
然后我明白了只要我不问清楚整个项目每一个代码究竟是干什么用的,这个重构的事情恐怕就是 WWDC 奖学金审核结束了都不一定能完。总会有 API 表现和预期不一样,问之前的 Unity 负责人说是这个时候会被 Android 触发,然后跑在机器上也不一样,问 Android 负责人给的是另一套行为逻辑。讲真我已经受够也没什么热情重构这个项目了,毕竟除了自己会更新文档也没人会去,最后这些代码有没有意义也不清楚,谁都不希望自己写的东西到后面没人用,简历上怎么写都很尴尬。

其他

本周(及上周)面了很多人,包括 XHackertxxFrad Lee羽喵丁一紫米房老板
每日通勤听 Spotify。最近听了 Firing Line -Original Mix
by JackLNDN
/ Vanessa's Tune
by Chris McClenney

2017.3.17

 • 

因为前司之前被收购的原因,去百度入了职。现在算是百度实习生。入职前后还发生了一些事情,搞得大家都不愉快,就不细说了。
前司迁入百度后保留办公地点和大部分人员。我继续做重构。重构大致要做的事情我是这么和妹子比喻的:房主原本有一层楼,然后房主又买下了这栋楼的另一层,让我去把这两层的电路改造为一整套,我就在这两层之间辗转挪移,把两层的接线板都拆下,取而代之一套可以同时控制两层楼的电路系统,这期间还要保证大部分的电路运转正常。
因为重构的原因,个人项目无一继续推进。准备给今年 WWDC Scholarship 写一个 Playground 来着。
阿里的电面回答的不是很好,一个是自己算法的部分比较渣,一个是在介绍项目经验时泛泛而谈,最后还用电脑写了道题,也是磕磕碰碰。
电影看了最终幻想 15,金刚狼 3,和一条狗的使命。