2015年12月17日木曜日

レガシーな Objective-C プロジェクトを Swift なプロジェクトに変換する

ここで言うレガシーなObjective-Cプロジェクトの定義とは
  • iOS 7時代 (Xcode 5) より前に作成されたプロジェクトである
  • Swiftのコードを一行も含んでいない
  • IOS_DEPLOYMENT_TARGETが8.0よりも小さい (7.xをサポートしている)
とします。

こんな由緒正しいiOSのプロジェクトを未だにメンテしている人もなかなかいないのかと思いますが、もしいらっしゃいましたらそんな方のためにSwiftなプロジェクトに変換していく方法をメモしておきます。

■前提条件

まずIOS_DEPLOYMENT_TARGETを8.0以上にしましょう。IOS_DEPLOYMENT_TARGETを8.0以上にすることでdynamic frameworkおよびclang moduleが使えるようになるため、Objective-CとSwiftの間の垣根が非常に低くなります。

■ケース1: 根っこはObjective-Cのまま、Swiftのファイルを追加

普通にSwiftのファイルを追加したらXcodeが上手いことしてくれます。具体的には

clang moduleを有効にしてdyldを使えるようにする設定
CLANG_ENABLE_MODULES = YES;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";

Swift化されるターゲットごとにブリッジングヘッダを作成
SWIFT_OBJC_BRIDGING_HEADER = "myapp/myapp-Bridging-Header.h";

Releaseビルド以外に対して最適化レベル設定
SWIFT_OPTIMIZATION_LEVEL = "-Onone";

が自動的に行われるので後はせいぜいブリッジングヘッダに使ってるObjective-Cのヘッダを書き込む程度ですみます。

■ケース2: 根っこからSwiftにする

「根っこからSwift」とは要するにmain.m(C言語のmain関数)を持たないアプリにしたいということですが、これも実は思ったより簡単にできます。
  • AppDelegate.swiftファイルを作成する
  • 既存のObjective-Cで書かれたAppDelegateを継承て新しくswiftなAppDelegateを作成して、@UIApplicationMainアノテーションを付ける
  • main.mを消す
参考: http://stackoverflow.com/questions/31309249/how-to-convert-objective-c-appdelegate-to-swift

たったのこれだけでプロジェクトの根っこがSwiftな状態になります。簡単ですね。

■さらにSwift化をすすめる

とりあえずAppDelegate.swiftを作ることで根っこはSwiftになるのですが、せっかくだからAppDelegateをまるごとSwiftにしてしまいたいものです。ここでもし、
  • UIWindowをAppDelegate内部でカスタマイズしている
  • UIApplicationをmain.mでカスタマイズしている
などの場合はそのままだとSwiftで対応するのがちょっと面倒です。

まずUIApplicationについてはInfo.plistのNSPrincipalClassを変更することで任意のカスタムクラスに差し替える事が可能です。参考はこちら: http://stackoverflow.com/questions/31642956/how-to-detect-all-touches-in-swift-2

UIWindowについてはMain.storyboardを使わなければ勝手にUIWindowを差し込まれることはなくなるので問題ないのですが、それでは困ると言う場合はawakeFromNibとかを実装してその中で
@UIApplicationMain
class MyAppDelegate: NSObject, UIResponder, UIApplicationDelegate {
  var window: UIWindow?
  override func awakeFromNib() {
    super.awakeFromNib()
    guard let defaultWindow = self.window else {
      fatalError("Something is wrong")
    }
    let window = MySuperDuperWindow()
    window.rootViewController = defaultWindow.rootViewController
    self.window = window
  }
}
とかすれば動くと思います。多分。

2015年12月1日火曜日

pyspa Advent Calendar 2015: 2015 GOTY for Programmers

この記事は pyspa Advent Calendar 2015: 12/1 分の記事です。先鋒を担当いたしますakisuteこと小野と申します。よろしくお願いします。なんか3年前にも先鋒をやったような・・・

さて2015年も終わりに近づいてきたということで、本来はGame of the Year for akisuteというタイトルで私的に今年最高だったゲームについて延々と無駄に語ろうかと思っていたのですけれども、Qiitaでは利用規約には一切明記がありませんがプログラマーに役立たない記事は投稿できないという制約がございまして、大変遺憾ながらタイトルを無理やりプログラマーに役立ちますように変更させていただいた次第であります。

しかしながらご安心ください。今年2015年はプログラマー向けゲームの大豊作当たり年でございます。プログラマーにこれからなりたい人向け、プログラマーに育成したいお子さん向け、ガチのプログラマー勢向けのゲームを多数ご用意させていただきましたので最後までお楽しみいただければと思います。

それではまえがきはこの程度にいたしまして・・・いよいよ各賞の発表です!

■お子さんのプログラマー教育向け賞: CodeSpells


あなたのお子さんを将来プログラマーに育てたいと考えているのであればこのようなゲームはいかがでしょうか?というわけで自分で魔法を自由にプログラミングして使えるゲーム、CodeSpellsがノミネートです。こちらのゲーム自体はかなり昔から発表されていたのですが、Steam Early Accessが始まったのが今年のようなので今年のノミネートとさせていただきました。

ゲーム自体はオープンワールドのゲームで目的等はまだ特になく開発段階という感じですが、このゲームのキモである魔法を自由にプログラミングして行使する点は現在でもバッチリ遊べます。各魔法にはイベント・アクション・それから分岐とループの制御構造が用意されていて、例えば
  • 光の玉を正面に発射する
  • if 光の玉が何かに接触する
  • 自分自身を接触地点にワープ
とか、
  • for 100回ループ
  • 炎をi*3.6度方向に発射
とかいろいろ工夫して魔法が作れるようになっています。大人が遊ぶにはちょっとパズル要素がないとか目的がないとか現段階では問題点が多いのですが、子供が想像力を働かせて遊ぶという当初の目的上であれば自由に地面を隆起させたり石を吹き飛ばしたり津波を起こしたりして遊べるのではないかなと思います!

■プログラマー初心者向け賞: Human Resource Machine


World of GooやLittle Infernoなどで有名なTomorrow Corporationの新作、Human Resource Machineが今年のプログラマー初心者向け賞にノミネートしました!このゲームはどんな命令でもただ言われたとおりに働くただのサラリーマンのおっさんに簡単な命令セットを与えて目的を達成する、つまり
おっさんをプログラミングするゲーム
です!新しいな。

Tomorrow Corporationらしい独特の世界観がただただ言われたとおりに動くおっさんを彩り、一風変わった雰囲気を味わうことができます。この世界観だけでもこのゲームをやる価値があります。

さてこのおっさんを操るプログラミング言語(仮におっさん言語と呼ぶことにする)ですが、一般的なアセンブリ言語の命令セットと同じような感じになっています。またレジスタ兼RAMの代わりとして床のカーペット上の区画を自由に使うことができるようになっていて、ちょっと工夫すればこんなふうにおっさんにバブルソートをさせる事ができます!

おっさん言語のコーディングはビジュアライズされていて、各命令ラベルをドラッグアンドドロップするだけで快適にコーディングが楽しめます。コメントラベルも手書きで好きなように書けるようになっていて、プログラミングをやったことがないカジュアル層の人でも無理なく遊べます。反面ガチ勢の人には簡単すぎるかもしれません。一応命令セット数とステップ数の最適化チャレンジが用意されているので、ガチ勢の人はそちらで楽しんでみてはいかがでしょうか?

■自動化大賞: Factorio


皆様
プログラマーにとって
最も邪悪なことは何でしょうか?

手作業です!

皆様
プログラマーにとって
最も素晴らしいことは何でしょうか?

自動化です!

自動化こそ正義!
自動化こそ未来!
今こそゲームも自動化されるべき!!

ということで今年最も自動化が進んだゲームに送られる自動化大賞には、スーパーマリオメーカーを差し置いてFactorioがノミネートされました!

Factorioについて解説する前に少し前置きをさせてください。
皆様はMinecraftというゲームをご存知でしょうか?
今やスーパーマリオに肩を並べ、Minecraft系という一大ジャンルを作り上げた超有名作だと思います。

しかしながら私このMinecraftというゲームが大嫌いでして。

例えば鉄の武器が欲しいじゃないですか。
なので一生懸命地面を掘って鉄を探して、鉄が見つかったら今度は石炭を探して、持って帰ってきて自分でかまどにぶち込んで鉄にして、今度は鉄を自分で並べて装備を作って、また鉄が足りなくなったら自分で地下に潜って掘ってって繰り返して。

めんどくせえんだよ!!!!!!!
人力手作業で作業するからスケールしねえし!!!!!
自動で掘らせろや!!!!!!!!!

って思っちゃうんですね(´・_・`)

Factorioも基本的にはMinecraftのようなゲームです。
ある宇宙飛行士がとある惑星に不時着してしまって帰れなくなっちゃうんですね。
そこで現地の素材を集めて加工してサバイバルして、最終的にはロケットを作って打ち上げて星から脱出するのが目的のゲームです。
というわけで最初は木を斧で切り倒したり、鉄鉱石をツルハシで掘ったりするんですね。

違うのはここからです。
手作業で鉄を掘るとかめんどくさいじゃないですか。そもそもロケットを作るには膨大な料の鉄が必要だというのに手で掘るとか現実的じゃないですよね。
そこで主人公は鉄を掘るためのツルハシ・・・じゃなくて、なんと自動砕石機を作るんですね。
それから自動で材料を運ぶためのベルトコンベアと、自動で材料を加工してものを作成する機械と、自動で機械に燃料や材料を投下するロボットアームを作るんですね。
あとは砕石機からベルトコンベアで材料を運んで、ロボットアームで機械に投下すれば、自分が寝てても別の所を探検しててもエイリアンと銃撃戦を繰り広げてても勝手に新しい製品がドンドン製造されていくんですね。

なんて賢いんだこの主人公は!!!!!!!!!!

って思っちゃいますよね\(^o^)/

そう、このゲームは
自動化こそが正義
スケールする生産こそが正義
生産効率の最適化のために機械の配置を試行錯誤するのが正義

まさにプログラマーのプログラマーによるプログラマーのためのゲームなのです!

こうして最初は1秒間に1個しかつくれなかった鉄が、工場生産効率の改善とスケールメリットのおかげで最後には1秒に1万個以上生産できるようになります。ヤバい。
こうなってくるともう最適化欲求がドンドン高まってきて、そのうちロケットを打ち上げることが目標だったのが、1秒間に何発ロケットを打ち上げられるかみたいなゲームになってきて、資源を惑星の片っ端から鉄道で運搬し、邪魔するエイリアンを片っ端から銃弾爆薬戦闘ロボットの波で蹂躙し森林を伐採して燃やし・・・どっちが悪役かわからなくなってきましたね(´・_・`)

さらにこのゲームはゲーム内のオブジェクトがすべてコンソールからluaスクリプトで操作ができ、luaスクリプトを書くことで自由にMODをプログラミングすることができます。そのため究極的にはパソコンの前で座って眺めているだけで自動的に主人公が工場を作ってロケットを打ち上げてゲームをクリアするMODをプログラミングすることも理論上可能なわけです!

自動化万歳!!

■GOTY for Programmers: TIS-100


しかしながらそのFactorioを差し置いて、今年のGOTY for ProgrammersはこのTIS-100で決まりです!名前からしてプログラマーっぽいこのゲーム、簡単に説明しますと
  • 主人公(=あなた)の叔父に当たる人物が最近、原因不明の死を遂げた
  • 叔父は生前プログラマーで、遺品整理していたらTIS-100という1980年代製の謎のコンピュータが出てきた
  • TIS-100の中には叔父の生前のメッセージや日記などが残っていた
  • 主人公であるあなたはTIS-100のプログラムを復活させてすべての謎を解き明かしていく
という設定になっております。地味、地味の極地ですね。プログラマー以外眼中にない、まさにプログラマーの・プログラマーによる・プログラマーのためのゲームとしか言いようがありません。

この作品の焦点となるTIS-100コンピュータですが、通常の1980年台のコンピュータとは全く異なるユニークな分散コンピューティングアーキテクチャを採用しています。
  • 各プログラムは横に4つx縦に3つ、最大12個の「ノード」と、複数の「入力ポート」「出力ポート」で構成される
  • 各ノードは最大上下左右4つの「ポート」で結ばれている
  • 各ノードにはアセンブリ言語を用いて命令を記述できる
  • 各ポート間はGo言語のチャンネルのような仕様を用いて入力・出力を行ったり、各ノード間の協調動作を行ったりすることができる。
  • 各ノードの命令はすべて並列で実行される
このアセンブリ言語とモダンな言語の特性を併せ持ったアーキテクチャがTIS-100でのプログラミングの最大の面白さです。うまくコーディングしてノード間を並列させれば実行ステップ数を劇的に減らしたり、複数ノード間でちょっとしたmap-reduceのような作りさえ実現することができます。

しかしながらですね、このTIS-100
異常なほど難しい
んです

開発者Zachtronicsの前作はSpaceChemというゲームでして、こちらのゲームも後半異常な難易度になってくるのですが、本作TIS-100も負けず劣らず難易度が凄まじいことになります。主に難易度が高くなる原因として、TIS-100コンピュータのアーキテクチャ上の絶妙な制約があげられます。例えばこんな感じです:
  • 1ノード内にたったの15行しか書けない。ちなみに空行、ラベル行、およびコメントも1行として数えられます。
  • 1行に確か20文字だか30文字前後しか書けない。ラベル名等は極端に短くする必要がある。
  • 1ノードにレジスタが2つしかなく、そのうち片方は計算レジスタとして使用できずただ値を一時保存するしかできない。さらにこの一時保存レジスタと値をやり取りするだけで追加の1命令が必要。
  • INCL/DECL命令がない。
  • 上記制約が合わさって1ノード内でループ構造を作るのが著しく難しく、汎用のどこでも通用するループ構造を構成するのが難しい。
というわけで普通の手続き的なコードを書くことができず、まさにTIS-100のためのまったく新しいコーディングの仕方を考えていく必要があってとにかく辛いです。実際私は最後までクリア出来ていません(´・_・`)

このただでさえ高い難易度に加え、さらに各問題には最適化チャレンジというものがありまして、使用したノード数・使用した行数・実行ステップ数の3つの点からプログラムの最適化を全世界のプログラマと競うことができるようになっています。おかげで世界中でドハマりするプログラマーが続出。ついにハッカーズマニュアルやらC言語製のシミュレータ、果てはWeb上で動作するTIS-100用コードエディタがまでが登場してしまいました。捗る!

ぜひ我こそはというガチ勢のプログラマの皆様、年末年始のお休みはこのTIS-100の謎を解き明かすのに使ってみてはいかがでしょうか?

それでは皆様、来年もHappy Gaming!

2015年11月29日日曜日

iOS でヒラギノフォントが明示的に指定された時に描画サイズの計算が正しくならない問題を修正する

タイトルからして出落ち感が少々ありますが・・・

iOSのフォントサイズ計算には長年修正されないバグというか仕様がございまして、「ヒラギノフォント(ヒラギノ角ゴシック、ヒラギノ明朝等)」が明示的に[UIFont fontWithName:size:]で指定されたとき、そのフォントを使ったUILabelやUITextViewなどの描画サイズの計算が正しくならない問題があります。iOS 6からiOS 9.1現在に至るまでずっとなので今後も直ることはないと思います。

詳細についてはこちらの記事が詳しいです。
http://qiita.com/yusuga/items/2be8c55ca561bba44702
一番下のリンク先の記事でも同様の問題が訴えられていまして、それぞれ対策が記載されていますので合わせてご参照ください。

でまぁ、対処法としてはいくつかあります。
  • UIControl.contentVerticalAlignmentFillにする
  • sizeThatFitsおよびintrinsicContentSizeの実装を差し替える
  • ヒラギノフォントを明示的に指定するのをやめる。システムフォントを使えばいいじゃない\(^o^)/
  • システムが提供するヒラギノフォントを使うのをやめて、モリサワさんなどからまともなヒラギノフォントを買ってきてそちらを使う

今回は私が実際に使っているsizeThatFitsの実装を差し替える方法を紹介したいと思います。といってもまぁ結構簡単です。以下の様な実装になっています。

見ての通り、フォントファミリーがヒラギノ系であったら、
  • widthはceilする
  • heightはもともとの高さにfontのdescenderをfabsしてから足したうえでceilする
だけです。簡単でしょう? Apple爆発しろ