Objective-CにおけるDate型の扱い
概要
なぜか最近仕事でObjective-Cを書いています。 サーバからmsのtimestampを受け取っているにも関わらず、時系列順に表示されなかったので調べてみました。
Types in Objective-C
まずはじめにハマったのが、msはint型には収まりません。JavaScriptの住人なのでここで時間をロスしました。
Types in objective-c on iPhone - Stack Overflow
この記事によると32bit端末ではlong型が4bytes扱い、64bit端末ではlong型が8bytes扱いだそうです。
Device一覧: https://en.wikipedia.org/wiki/List_of_iOS_devices
WikipediaによるとiPhone5S以降は64bitなので、long型を使うことにしました。(良いのかは不明)
Primitive sizes: The size of a char is: 1. The size of short is: 2. The size of int is: 4. The size of long is: 8. The size of long long is: 8. The size of a unsigned char is: 1. The size of unsigned short is: 2. The size of unsigned int is: 4. The size of unsigned long is: 8. The size of unsigned long long is: 8. The size of a float is: 4. The size of a double is 8. Ranges: CHAR_MIN: -128 CHAR_MAX: 127 SHRT_MIN: -32768 SHRT_MAX: 32767 INT_MIN: -2147483648 INT_MAX: 2147483647 LONG_MIN: -9223372036854775808 LONG_MAX: 9223372036854775807 ULONG_MAX: 18446744073709551615 LLONG_MIN: -9223372036854775808 LLONG_MAX: 9223372036854775807 ULLONG_MAX: 18446744073709551615
Dateの扱い
Objective-CではDateがmsをサポートしていないため、以下のように書かなければなりません。
NSNumber *timestamp = responseDict[@"timestamp"]; long ts = [timestamp doubleValue]; // coredataへ保存 NSDate *date = [NSDate dateWithTimeIntervalSince1970:ts/1000]; // ms -> s
JavaScriptの住人にとっては何だこれっていうレベルです。Swift知りませんが、きっとここらへんは改善されてるはず…祈
このプロジェクトではCoreDataにDate
型で保存していたため、secondまでしかサポートされておらず時系列順にソートすると残念な結果になったので、しれっとInteger 64
型に書き換えて保存するようにしました。
まとめ
せっかくならSwift書きたいです。
ソース
Google Cloud Platform上にContainer Clusterを作成する
概要
Google Cloud PlatformのContainer Cluster(Kubernetes)のセットアップからデプロイまでの手順です。プロジェクトの作成手順は省略します。
Container Clusterの作成
メニューから [Container Engine] -> [Container Clusters] を開き、[CREATE CLUSTER]をクリック。必要な項目を有効にして作成してください。 このプロジェクトではCloud SQLを利用しているので、Cloud SQLをEnabledにしています。
作成後、画面にConnectと出てくるので、メニューに従って実行します。
$ gcloud container clusters get-credentials production \ --zone {指定したzone} --project {project-id} $ kubectl proxy
gcloudのSDKはこちらからダウンロード可能です。
Cloud SDK | Google Cloud Platform
installの際にインストール不足のものはエラーとして表示されるので、それに従ってinstallして再度上のコマンドを実行すれば完了です。
http://localhost:8001/ui/にアクセスしKubernetesの画面が見れれば準備は完了です。
Redisのデプロイ
プロジェクトでredisを使用しているためredisのDockerイメージをデプロイします。Dockerをinstallしていない方はこちらからインストールできます。
$ docker pull redis:alpine $ docker tag redis:alpine gcr.io/{project-id}/redis:alpine $ gcloud docker push gcr.io/{project-id}/redis:alpine
これでRedisのデプロイは完了です。
コンテナの配置
後は先程のブラウザ上でコンテナを配置するだけです。
右上の[CREATE]をクリックし、必要に応じて入力してください。コンテナイメージのパスは [Compute Engine] -> [VM instances] のところのInstance templeteをコピペすれば完了です。 ポッド数は必要に応じて増やしてください。また今回のRedisは外部アクセスを許可していないので内部アクセスのみにしてあります。
他にNode.jsのDocker Imageもデプロイしましたがまた別の機会に作成手順と一緒に書きたいと思います。
感想
Docker & Kubernetes初日でしたが、同僚の手助けの下、数時間でNode.js/Redis Clusterの動作確認ができました。とても便利なので今後も使っていきたいです。
ソース
Pokemon Goのちょっと得する情報
カナダで一足先にプレイしています。ポケモン効果はすごいです、家から基本出なかったカナディアンの友達が毎日外出してます。
さて色々損してきたのでさくっと箇条書きでまとめときます。
- ピカチュウをゲットする
- 最初の三匹を選ばずに待っていると現れます。
- サークルは小さいほうが捕まえやすい
- サークル内にヒットすると捕まりやすくなるのと経験値がもらえる
- 足あとは少ないほど近い
- Lv30までは「ほしのすな」・「アメ」は使わない
- 卵のレベル上限はLv20
- 卵の内訳
- 有料ボックスは5km以上の卵に使ったほうがコスパいい
- 2kmは無限ボックスで孵化させましょう。
- Lv上限は現在40
- イーブイの進化のさせ方 (アニメ参照)
- リネームする (最初は大文字だよ、日本語対応してるかわからない)
- Sparkyが欲しい場合Jolteon
- Vaporeonが欲しい場合Rainer
- Flareonが欲しい場合Pyro
- 再起動する
- 進化させる
- リネームする (最初は大文字だよ、日本語対応してるかわからない)
- ズバット
- 玉の浪費なので高CPは逃げる
- カーブが気持ち射程長い気がしないでもない
- カーブはボールをくるくるすれば投げられる
- ラズベリーはポケモンが逃げにくくなる
- レアポケモンに使いましょう
- ボールを投げるごとにあげましょう
- ボールを投げた後にフリーズする
- いくら待っても結果は変わりません、再起動してください。
- googleアカウントをカナダにしてカナダドルでクレカ切るとお得
- 14,500コインでおそらく1,200円くらいお得ですかね(81円換算)
- Lure moduleのピンクのところをクリックするとアイテム使ってくれてる人がわかる
- 見てあげてください
- 個体値 *追記 6/23
- サイズがでかいほうが高い
- 進化はギリギリまでさせてはいけない
- ポッポマラソン *追記 6/23
- ポッポの入手が簡単
- 進化させると経験値が入る
- しあわせタマゴで経験値2倍
- ミューツー、ファイヤー、サンダー、フリーザーは伝説
- ミュウは(ry
Have a good Pokemon life!
リンク
- Master Balls, levelcap en zeldzame Pokémon gevonden in Pokémon GO broncode | Power Unlimited
- Pokemon GO Guide: How To Control Eevee’s Evolution - Page 2
- How to collect and hatch eggs in incubators to get the best rare Pokémon in Pokémon Go | WIRED UK
- https://twitter.com/PokemonGo_Hints/status/756827691714695168
- https://twitter.com/PokemonGo_Hints/status/756827955267969024
GAEからCloud Storageに画像をアップロードする
はじめに
Google App Engine (GAE)のGolangのAPI仕様がガラッと変わったようなので、古いバージョンを使っている方はここを参考に書き換えてください。
GitHub - golang/appengine: Go App Engine packages
Goのversionは1.6.2を使用しています。
画像のアップロード
実際はリクエストを受け取り、その画像をCloud Storageにアップロードしていますが、 実行しやすいようにローカルファイルとテストを使用しています。
package storage import ( "fmt" "google.golang.org/appengine" "google.golang.org/appengine/aetest" "google.golang.org/appengine/blobstore" "google.golang.org/appengine/image" "google.golang.org/appengine/log" "google.golang.org/cloud/storage" "io" "net/http" "os" "testing" ) const ( bucket = "<bucket path>" image = "<image path>" ) func Upload(req *http.Request) (string, error) { ctx := appengine.NewContext(req) client, err := storage.NewClient(ctx) file, err := os.Open(image) userName := "tester" fileName := "test.jpg" mimeType := "image/jpeg" filePath := fmt.Sprintf("%s/%s", userName, fileName) blobWriter := client.Bucket(Bucket).Object(filePath).NewWriter(ctx) blobWriter.ContentType = mimeType io.Copy(blobWriter, file) err = blobWriter.Close() blobPath := fmt.Sprintf("/gs/%s/%s", Bucket, filePath) blobKey, err := blobstore.BlobKeyForFile(ctx, blobPath) // crop opts := image.ServingURLOptions{Secure: false, Crop: true} url, err := image.ServingURL(ctx, blobKey, &opts) log.Infof(ctx, "url", url) return url.String(), err } func TestUpload(t *testing.T) { inst, err := aetest.NewInstance(nil) req, err := inst.NewRequest("GET", "/gophers", nil) url, err := Upload(req) fmt.Printf("test", url, err) }
ローカルで実行すると画像は正常にアップロードされますが、ServingURLがうまく働いてくれないようでhttp://localhost/_ah/img/encoded_gs_file:...
のようなパスになってしまいます。
GAE上だと正常に動いているのですがテストを書く上でとても不便ですね。。
Image.Decodeでunknown formatになったときの対処法
いまいち条件がわかりませんが、一度Decodeされたデータは再度Decodeしようとするとunknown format
になるようです。
Goのバージョンは1.6.2を使用しています。
依存ファイルは以下のとおりです。
import ( "bytes" "image" "image/gif" "image/jpeg" "image/png" "io" "os" )
ダメな例
まずはダメな例です。
file, _ := os.Open("./image.jpg") img, _ := jpeg.Decode(file) conf, encode, err := image.DecodeConfig(file) // unknown format
Decode
でもDecodeConfig
でも再現します。これは困ります。
対処法1
ファイルを開き直すことで解決します。
file, _ := os.Open("./image.jpg") img, _ := jpeg.Decode(file) file, _ = os.Open("./image.jpg") conf, encode, err := image.DecodeConfig(file)
気持ち悪いです。
対処法2
別の方法を模索したところ、EncodeしてDecodeし直すと解決することがわかりました。
file, _ := os.Open("./image.jpg") img, _ := jpeg.Decode(file) buf := new(bytes.Buffer) jpeg.Encode(buf, img, nil) conf, encode, err := image.DecodeConfig(file)
気持ち悪さは半減しましたが、まだ気持ち悪いです。
対処法3
事前にいじれる場合はio.Copy
でコピーすると良さそうです。
file, _ := os.Open("./image.jpg") buf := new(bytes.Buffer) io.Copy(buf, file) img, encode, _ := image.Decode(file) conf, encode, err := image.DecodeConfig(buf)
まとめ
最終的にio.Copy
に落ち着きました。
func Decode(file io.Reader) { buf := new(bytes.Buffer) io.Copy(buf, file) img, encode, err := image.Decode(file) conf, encode, err := image.DecodeConfig(buf) ... }
ソース
Benchmark: Async vs Neo-Async
What is Neo-Async?
Neo-Async is meant to be used as a drop-in replacement for the Async library. It implements the same API, and adds extra functionality. I have been trying to make it faster and safer than Async.
https://github.com/suguru03/neo-async
It doesn't mean I don't like Async. I would like to improve both libraries and to showcase some performance optimizations that might be implemented by other Node.js libraries. I especially admire @aearly for their good work.
Benchmark
Recently, Async has been released v2.0.0-rc.6 and I released Neo-Async v2.0.0-rc.1.
So I would like to check performance using benchmark and func-comparator.
How to check
I made a repository to measure performance between Async and Neo-Async.
https://github.com/suguru03/async-benchmark
$ git clone git@github.com:suguru03/async-benchmark.git $ cd async-benchmark $ npm install $ node . // It might take more than one hour...
Environment
- Ubuntu v12.04
- Node.js v6.2.1
- async v2.0.0-rc.6
- neo-async v2.0.0-rc.1
- benchmark v2.1.0
- func-comparator v0.7.1
Result
Neo-Async is 1.27 ~ 10.7 times faster than Async.
The value is the ratio (Neo-Async/Async) of the average speed.
Collections
function | benchmark | func-comparator |
---|---|---|
each | 3.71 | 2.54 |
eachSeries | 2.14 | 1.90 |
eachLimit | 2.14 | 1.88 |
eachOf | 3.30 | 2.50 |
eachOfSeries | 1.97 | 1.83 |
eachOfLimit | 2.02 | 1.80 |
map | 4.20 | 4.11 |
mapSeries | 2.40 | 3.65 |
mapLimit | 2.64 | 2.66 |
mapValues | 5.71 | 5.32 |
mapValuesSeries | 3.82 | 3.23 |
mapValuesLimit | 3.10 | 2.38 |
filter | 8.11 | 8.76 |
filterSeries | 5.79 | 4.86 |
filterLimit | 4.00 | 3.32 |
reject | 9.47 | 9.52 |
rejectSeries | 7.39 | 4.64 |
rejectLimit | 4.54 | 3.49 |
detect | 6.67 | 6.37 |
detectSeries | 3.54 | 3.73 |
detectLimit | 2.38 | 2.62 |
reduce | 4.13 | 3.23 |
reduceRight | 4.23 | 3.24 |
transform | 5.30 | 5.17 |
sortBy | 2.24 | 2.37 |
some | 6.39 | 6.10 |
someSeries | 5.37 | 4.66 |
someLimit | 3.39 | 2.84 |
every | 6.85 | 6.27 |
everySeries | 4.53 | 3.90 |
everyLimit | 3.36 | 2.75 |
concat | 9.18 | 9.35 |
concatSeries | 7.49 | 6.09 |
Control Flow
funciton | benchmark | func-comparator |
---|---|---|
parallel | 7.54 | 5.45 |
series | 3.29 | 2.41 |
waterfall | 5.12 | 4.27 |
whilst | 1.96 | 1.95 |
doWhilst | 2.07 | 1.96 |
until | 2.10 | 1.99 |
doUntil | 1.98 | 2.04 |
during | 10.7 | 7.09 |
doDuring | 5.98 | 6.03 |
queue | 1.83 | 1.75 |
priorityQueue | 1.79 | 1.75 |
times | 3.84 | 3.65 |
race | 1.45 | 1.27 |
auto | 3.23 | 3.50 |
retry | 9.43 | 6.78 |
Future Prospects
I'm not done with performance optimization; the control flow functions are my main focus right now. I will release v2.0.0 when satisfied with performance.
And I'm keeping in mind this issue.
I believe the asyncFlowF
library doesn't implement the same interface as Async, but I would like Neo-Async to be beat it benchmark anyways. ;)
Even as many JavaScript users switch to Promises, callback style is far from dead, so I want to keep improving Async and Neo-Async.
Next time, I will write about the difference between Async and Neo-Async. :)
Reference
Golang始めました
始めたきっかけ
Node.jsエンジニアとして採用されましたが、Node.js/Go/Javaの3つのサーバが建っていたため、最近ではGoのコードも修正したりしてます。
導入
Node.js出身の僕としては、やはりnvmのようなパッケージ管理モジュールを入れたいものです。gvm、gobrewというモジュールがありますが、よく使われてそうなgvmには最新バージョンが入っていなかったためgobrewを使ってみることにしました。
GitHub - moovweb/gvm: Go Version Manager
gobrewの設定
gobrewをダウンロードします。
$ git clone git://github.com/cryptojuice/gobrew.git ~/.gobrew
次に.bashrc
または.zshrc
に追加します。
GO_VERSION=1.6.2 export PATH=$HOME/.gobrew/bin:$PATH eval "$(gobrew init -)" gobrew use $GO_VERSION ECHO "Now using golang $GO_VERSION"
nvm好きの僕としては最後の一行だけは忘れずに追加していただきたいものです。
後はインストールするだけです。
$ gobrew install 1.6.2 $ gobrew use 1.6.2
以上です。
$GOPATH
の設定
実際に使用する際は、作業ディレクトリに移動し$GOPATH
を設定します。
$ gobrew workspace set
またnvm同様、
$ gobrew use {VERSION}
と簡単にバージョンを変えられるのでとても便利です。