「小学生の漢字 3.0.0」では以下の対応を行い、使い勝手が向上しました。
- iOS 9に対応しました。
- iPadではランドスケープ(横置き)に対応しました。
- 20画以上の中学で習う漢字(常用漢字)の「書き順」を追加しました。
- 「学年別」の検索で「中学生の漢字」と「人名漢字」を追加しました。
- 90%以上のプログラムをSwiftで書き換え、処理速度を向上しました。
「小学生の漢字 3.0.0」では以下の対応を行い、使い勝手が向上しました。
Swiftには#defineがありません。
#ifdef 〜 #else 〜 #endifも#if 〜 #else 〜 #endif に置き換わっています。
何が問題かと言うと、例えば Precompiled Header で下記のように記述する事が出来ません。
#ifdef DEBUG #else #endif
まぁSwiftではヘッダファイルという概念が無いのですからしょうがないと言えばしょうがないのですが。
またこの DEBUG も今まではBuild Configuration をDebugにすればObjective-Cのコンパイルオプションとして -DDEBUG=1 が付くのですが、Swiftのコンパイルではこれは意味が無いようです。
しかしこれはBuild Settingで下記のように”Swift Compiler – Custom Flags”に “Other Swift Flags” に追加すれば解決します(リリース時にはデフォルトで設定しておいてもらいたいですね)。
では例えばデバッグ時にメッセージを出力して出荷時にはそれを自動的に消すには?
Swiftではクラスに属さない「関数」を記述する事も出来るようです。
例えば “GlobalFunction.swift” というようなソースにデバッグ用の関数を定義しておけば、Swiftはヘッダファイルを必要としていないため、自動的に全てのコードの中で参照出来るようになります。
#ifdefの中で#defineとしてマクロを組むのとは違って、リリース版にも何もしないLOG()が呼び出されてしまいますが、無いよりは良いですね。
もしかしたらもっと良い方法があるかもしれませんが…
import Foundation #if DEBUG func LOG(msgs:Any) { println("LOG: \(msgs)") } #else func LOG(msgs:Any) { } #endif
CoreFoundationを含めてiOSやOSXのAPIのほとんどはNSObjectのサブクラスとして提供されています。そのため新しい画面をSwiftで追加する場合にはUIViewControllerのサブクラスを作るしかありません。UIViewも同じですね。
ですのでどうしてもNSObjectとは切っても切れません。言い換えればObjective-Cのクラスとは切り離せません。
とはいえ前回のコラムでも書きましたがSwiftは親クラスを持たないクラスを作成する事が出来ますので、プログラミング言語と実行環境(Framework)が分離出来たと言えると思います。その事はiOSやOSX以外の環境でももしかしたらSwiftが主流の開発言語になる可能性も否定出来ないって事だと思います。
さて、Objective-CのクラスをSwiftで使う方法です。
標準のFrameworkを使うにはimport文でフレームワークを指定してやれば良いだけです。
import UIKit import CoreData
しかし自前のFrameworkやクラスをSwiftで使うためには最初のコラムで紹介しましたように<#ProductModuleName#>-Bridging-Header.h に #import 文を追加してやる必要があります。
このファイルはObjective-CのプロジェクトにSwiftのソースを追加してやる時に自動的に作成されます。
Objective-CのクラスをSwiftで使うための前準備はこれだけです。
しかしSwiftとObjective-Cではクラスのインスタンス化の方法が違うので注意する必要があります。
Swiftではalloc やinit メソッドを呼び出す必要はありません。
var obj:ObjectiveC-Class! obj = ObjectiveC-Class()
これだけです。
これだけでObjective-Cのクラスのallocやinitを自動的に呼び出してくれます。
しかしクラスによってはinitメソッドに引数を持たせているものもあります。標準Frameworkではそのようなinitメソッドは下記のようなフォーマットになっています。
-(id)initWithParam:(classname)parameter;
WithParam が付いています。
Objective-CのこのようなinitメソッドをSwiftから呼び出す場合には、withのあとの文字列をパラメータ名としたクラス・メソッドに置き換えてくれます。
var obj:ObjectiveC-Class! obj = ObjectiveC-Class(param:parameter)
iOSでViewを作るような場合で比較してみます。
CGRect viewRect = CGRectMake(10, 10, 100, 100); UIView* myView = [[UIView alloc] initWithFrame:viewRect];
let viewRect = CGRect(x: 10, y: 10, width: 100, height: 100) let myView = UIView(frame: viewRect)
これは自分で作ったクラスがWith〜が付いたinitメソッドを持っていても、自動的に変換されます。
いきなり出てきたSwiftですので、今はやはり取っ付きにくい部分も多くあります。ですがかなり柔軟な開発言語だと思います。使いこなせるようになれば便利じゃないかと感じています。swiftは現在のところXcode 6.0 betaでしか使えません。beta版のXcodeでは製品をAppStoreに上げる事は出来ませんが、古いiOSに対応したアプリも作成できるようですので、是非習得しておきたいと思います。
前回はNSObjectから派生したSwiftのクラスをObjective-Cで使うための方法を紹介しました。
Swiftは親クラスを持たないクラスを作る事も出来ます。その場合には気をつけなければいけない事があります。
まず、クラスの宣言の前に@objc(<#name#>)を付けて、Objective-Cから<#name#>というクラス名で使用出来るようにしなければなりません。マニュアルによると@objc(<#name#>)はSwiftで宣言したのとは違うクラス名でアクセス出来るようにも出来ると書かれているのですが実際に試してみると上手くいきません。とりあえず問題はありませんのでクラス名にします。
Objective-Cでクラスを扱う際には全てのクラスはNSObjectのサブクラスになります。従ってインスタンス化する時には
以下のようにしていました。
OneClass* obj; obj = [OneClass alloc]; obj = [obj init];
しかしこのような方法でNSObjectを親クラスに持たないクラスをインスタンス化する事は出来ません。なぜならallocとinitはどちらもNSObjectクラスのメソッドだからです。
ではどうするか?
Swiftのクラスにクラス・メソッドを作ってやり、その中でクラスのインスタンスを返してやります。
import Foundation @objc(SwiftClass02) class SwiftClass02 { class func create() -> SwiftClass02 { return SwiftClass02() } }
#import "ObjectiveC-Class.h" #import "Swift03-Swift.h" @implementation ObjectiveC_Class -(void)method { SwiftClass02* swiftClass02; swiftClass02 = [SwiftClass02 create]; } @end
次はSwiftの中でObjective-Cのクラスを使う方法を考えてみます。
WWDC 2014で現れた新しいプログラミング言語のSwift。正直なところ、こんなのが出てくるとは思いませんでした。
特徴については様々ところで書かれていますので紹介しません。
とりあえずどんなモンか試していきます。
全く新しいアプリを書くなら全てSwiftで書くという事も可能でしょうが、バージョンアップの場合に全てを一からSwiftで書き直すと言うのはシンドイ話です。
徐々にSwiftにシフトしていく事を前提に、新しいSwiftのクラスをObjective-Cで使う事を試してみます。
まず新しくSwiftで書かれたクラスを追加します。
Swiftのクラスを作成するにはLanguageをSwiftにしてやるだけで作成されます。
最初にSwiftのクラスを作成するとこのようにBridge-Headerを作成して設定を変更するかどうかを聞いてきますので迷わず「Yes」をクリックします。
すると”ProductModuleName-Bridging-Header.h”と言うファイルが作成され、プロジェクトに追加されます。またBuild Settingの中の”Objective-C Bridging Header”に追加されます。これはSwiftの中でObjective-Cのクラスを使用する場合に必要になるもので、今回は説明を省きます。
必要なのは”ProductModuleName-Swift.h” というファイルです。マニュアルにはこのファイルをObjective-Cのコードの先頭で#importしろと書かれているのですが、面倒なのはこのファイルはプロジェクトの中には現れません。ビルドを行わなければ(クリアなどをしても)ファイル自体が存在しないようです(多分)。
ですので構わずObjective-Cのコードの先頭に追加してしまいます。”ProductModuleName”は各プロジェクトの名前ですので必要に応じて置き換えてください。
この方法で、NSObjectをルートクラスとするようなクラスは使えるようになります。
新しくSwiftのクラスを追加したりメソッドやプロパティを追加削除したり、クラスを削除した場合にはXcodeがこのヘッダファイルを自動的に編集してくれるようです。勝手に編集されないようにファイルを見えないようにしているのでしょう。またSwiftのクラスを編集するとアップデートするまで若干時間がかかるようで、すぐにObjective-Cの方で新しいメソッドを参照しようとするとエラーが出ますが、ビルドすればすぐに解決するようです。
import Foundation class SwiftClass01: NSObject { func method01() { println("method01 has benn called"); } }
#import "ObjectiveC-Class.h" #import "Swift03-Swift.h" @implementation ObjectiveC_Class -(void)method { SwiftClass01* swiftClass01; swiftClass01 = [SwiftClass01 alloc]; swiftClass01 = [swiftClass01 init]; [swiftClass01 method01]; } @end
しかしSwiftは親クラスを持たないクラスを作成する事も出来ます。その場合にはちょっと変わってきます。
それはまた次に。