Jun 162014
 

SwiftとObjective-Cの組み合わせ方のまとめ。相互運用では何が出来て何が出来ないのか。Swiftでクラスを記述する際の基礎知識の整理。必要最低限の属性。ゲッターとセッター。プロパティ。初期化と解放。

SwiftとObjective-Cの相互運用

Appleが互換性をもたせ組み合わせて使える言語にした理由は、

  • Objective-Cで、今まで親しんだ手法(言語の挙動や動作の取り扱いができる)をベースに
  • Swiftの持つ、モダンな最新の言語機能を活かしつつ

クラスを記述すること。 前回はSwiftとObjective-Cの互換性やそれぞれから機能をどのように利用するか相互運用について概要を書いたので、パート2は掘り下げて記述する。

 

Objective-Cクラスの継承

  • Objective-Cクラスを継承する場合、コロン(:)を使う
  • Swiftクラス名→コロン→Objective-Cクラス名の順
  • スーパークラスの機能をすべて利用できる
  • 自クラスでメソッドを再定義したい場合はoverrideキーワード
class MyViewController : UIViewController
{
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
    }
}

 


 

プロトコル(protocol)適用

  • カンマ(,)区切りでプロトコルを並べる
  • 複数並べられる
  • スーパークラスの後にカンマしてプロトコルを続ける
  • Swiftでは、クラスやプロトコルのネームスペース(namespace)は統一される
  • Objective-CのNSObjectプロトコルはSwiftではNSObjectProtocolに割り当て

 

class MyViewController : UIViewController, UITableViewDelegate
{
}

 

初期化/解放

  • Swiftでは、クラス中のプロパティを初期化しないまま放置しない
  • Swiftでは、Objective-Cと違い、分割メモリ割当メソッドは存在しない
  • Swiftでは、Objective-Cの初期化メソッドをSwiftに変換するため、Objective-Cクラスを利用してる場合でもSwiftの記法で初期化(initializer)できる
  • Swiftでは、クラスがメモリ解放される前にクリーンナッププロセスを追加する場合はObjective-Cのdeallocメソッドの代わりに解放メソッド(deinitializer/finalizerのようなもの)を実装出来る
  • Swiftでは、インスタントがメモリ解放される前に自動的にこの解放メソッド呼び出される
  • Objective-Cから派生したSwiftクラスを使っている場合、Objective-Cクラスを使っている場合、いずれの場合もSwiftは親クラスの(Objective-Cの)deallocメソッドを呼び出す
class MyView
{
    init(frame : Frame) {

    }
    deinit{

    }
}

 

Interface Builder

  • IBの機能をSwiftのclassから利用出来る属性(attributes)がある
  • アウトレット(@IBOutlet)
  • アクション(@IBAction)
  • ライブレンダリング(@IBDesignable / @IBInspectable)

@IBOutlet

  • プロパティ定義の前に@IBOutlet
  • コレクション(複数)のアウトレットを定義するためにも@IBOutletを利用可能
  • Swiftでアウトレットを定義すると型が割り当てられる(弱参照/オプション/nil)
@IBOutlet var button: UIButton
@IBOutlet var textFields: UITextField[]

@IBAction

  • メソッド定義の前に@IBAction
@IBAction func buttonTapped(AnyObject) {
    println("button tapped!")
}

@IBDesignable

  • InterfaceBuilderの中でインタラクティブにビューのデザインができるようになる。
  • UIViewやNSViewからカスタムなビューを作る場合、クラス定義の前に@IBDesignableを付ける事が出来る。
  • InterfaceBuilderにそのカスタムビューを設置(インスペクタでそのビューのクラスを設定)すると、キャンバスにレンダリングされるようになる。

@IBInspectable

  • プロパティ定義の前に@IBInspectableを付ける
  • ユーザ定義のランタイム属性と互換性がある
  • InterfaceBuilderにカスタムビューを追加すると、インスペクタでプロパティを編集出来る
@IBDesignable
class MyCustomView: UIView {
    @IBInspectable var textColor: UIColor
    @IBInspectable var iconHeight: CGFloat
}

 

 プロパティ属性

参照属性について

  • Swiftではデフォルトは強参照となる
  • オブジェクトへの弱い参照は、弱参照(weak)属性をつかう
class Message
{
    var title : String = "Hello"
}

class Mail
{
    var from : String = "You"
    var to : String = "Me"
    var msg : Message?
    weak var weakmsg : Message?
    init()
    {
        self.msg = Message()
        self.weakmsg = Message() //弱い参照なので保持されない
    }
}

Mail().msg?.title //"Hello"
Mail().weakmsg?.title //nilとなった

読み書き属性について

1)値格納用プロパティ

  • readwrite(読み書き用)には変数(var)属性
  • readonly(書き込み不可)には定数(let)属性をつけて定義
  • readwriteやreadonlyといった属性名は存在しない

varとletを付加するだけ:

    var firstValue: Int
    let length: Int

2)計算結果用プロパティ

  • readwrite(読み書き用)にはgetter/setter両方定義
  • readonly(書き込み不可)にはgetterのみを定義

readwriteの場合(setのパラメータは省略でき、省略時の入力パラメータ名はnewValueが暗黙的に指定される):

var origin = Point() //値格納用途
var size = Size() //値格納用途
var center: Point {
    get {
        let centerX = origin.x + (size.width / 2)
        let centerY = origin.y + (size.height / 2)
        return Point(x: centerX, y: centerY)
    }
    set(newCenter) {//newCenter(Point型)は入力パラメータ
        origin.x = newCenter.x - (size.width / 2)
        origin.y = newCenter.y - (size.height / 2)
    }
}

readonlyの場合(getを省略した形でOK):

    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
    return width * height * depth
    }

コピー

  • Ojbective-Cのcopy属性は@NSCopyingに該当
  • NSCopyingプロトコルに合わせて実装が必要

 

CoreData(@NSManaged)

  • モデル(CoreDataModel)の属性や関連に合わせて、それぞれのプロパティ定義に@NSManagedをつける
  • Objective-Cの@dynamicと同じ
  • @NSManagedは実行時にプロパティの実装が与えられる事を示す
  • @dynamicと違って@NSManagedはCoreDataでのみ利用可能な属性である

 
 
 
 

 Posted by at 17:09
Jun 102014
 

SwiftとObjective-Cの組み合わせ方のまとめ。何が出来て何が出来ないのか。特徴、型、互換性。

SwiftとObjective-Cの言語を利用する側として知っておく事のざっくりまとめ。SwiftからObjective-Cを使う場合、Objective-CからSwiftを使う場合。

Swiftとは?

  • CocoaやObjective-Cと互換性がある
  • SwiftからObjective-CのAPIを呼べる(systemフレームワーク)
  • SwiftからCライブラリを呼べる
  • SwiftからC++を直接呼べない
  • Objective-CからSwiftのAPIを呼べる
  • 拡張子は.swift
  • ヘッダーファイルがない(class記述に全ての情報を含むイメージ)

SwiftとObjective-C互換性

アプリ開発において利点…この互換性が最もフォーカスされるべき箇所。

  • Objective-CのコードからSwiftのクラスを呼べ、SwiftのコードからCocoaのクラスやパターン、書き方ができる
  • アプリ内にSwiftとObjective-Cの言語ファイルが混在していても大丈夫
  • 最新のSwift言語の機能を使うために、Objective-Cで開発したアプリの一部だけでもObjective-Cのコード箇所をSwiftのコードに置き換えることもできる

SwiftからObjective-Cを使う場合

Objective-CフレームワークやCライブラリ(UIKit/Foundation/SpriteKitなど)をimport文でインポート出来、Swiftのコードからモジュール(Module)としてAPI(メソッド/プロパティ/カテゴリ)を利用できる

例えば…

  • idはAnyObjectになる
  • NSStringはStringになる
  • pointerはoptional扱い

初期化プロセス

  • Swiftにはallocは存在しない
  • Swiftにはinitは存在しない
  • Swiftは型はお任せで大丈夫(明示的に指定出来るけれども)

initWith型はinitWith以降を小文字にして第一引数のラベルにするルール

[[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]

initWithを消し、Frameをframeにして引数のラベルとして

UITableView(frame: CGRectZero, style: .Grouped)

型は省略できるが明示的に付けることもできる

let table = UITableView(frame: CGRectZero, style: .Grouped)
let table:UITableView = UITableView(frame: CGRectZero, style: .Grouped)

initWithを消し、Frameをframeにして引数のラベルとして

UITableView(frame: CGRectZero, style: .Grouped)

Objective-CのFactory型のメソッドは見やすく便利

[UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0]
UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)

プロパティアクセス

  • ドット構文
  • 括弧は不要
  • Objective-Cではgetterとプロパティは同じ構文だが、Swiftでは全く別物(Objective-C側で@propertyで定義したプロパティのみ、Swiftでプロパティとして扱う)

Objective-Cではプロパティのgetterのように扱っていたものも

[UIColor darkGrayColor]

以下とはならないケースもある、つまりはObjective-Cフレームワーク側で実際は

UIColor.darkGrayColor

@propertyを使わず、単にメソッドとして定義されている場合もあるものはSwiftでは明示的にメソッドとして使い分けてコードを書く。

UIColor.darkGrayColor()

メソッド呼び出し

  • ドット構文
  •  セレクタの最初の箇所がメソッド名
  • 最初の引数を括弧のすぐ内側に記述(引数の名前は不要)
  • それ以降は引数の名前が必要
  • 引数がない場合も括弧は必要

 

[myTableView insertSubview:mySubview atIndex:2];

第一引数は引数の名前は不要、それ以降は必要になる

myTableView.insertSubview(mySubview, atIndex: 2)

 AnyObject(Swift)とid(Objective-C)

  • Swift言語のAnyObjectは様々なオブジェクトを表す型
  • Objective-Cのidと同じ
  • Swiftへインポート(import)する際、id型はAnyObject型にマッピングされる
  • 定数/変数にいずれもどんなクラスのオブジェクトも割当可能
  • 割当時にキャストは不要
  • ただし、オブジェクトに存在しないメソッドを呼ぶと実行時にエラー(実行するまでAnyObjectが何の型か特定されないので安全でないコードになるので注意)
  • エラーを出さないためにSwiftにはいくつかオプションがある

AnyObject型は様々なオブジェクトを受け入れられる

var cell: UITableViewCell = UITableViewCell()
var myObject: AnyObject = UITableViewCell()

キャスト不要

NSDate(myObject).dateByAddingTimeInterval(10)//Swiftでは不要
myObject.dateByAddingTimeInterval(10)

そもそも存在しない場合は実行した時点でエラー発生する可能性あり

myObject.start()

オプション(optinals)

Objective-Cエラーを抑えるSwiftの機能がある

1)?でエラーを回避できる(存在しないメンバーを呼んでる例)

var myObject = NSDate()
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)

2)if-let構文でメソッド応答の有無の確認ができる

if let fifthCharacter = myObject.characterAtIndex(5) {
    println("Found \(fifthCharacter) at index 5")
}

ダウンキャスト(AnyObjectから特定のオブジェクト型へ)

  • キャスト成功する保証はない
  • オプションを戻す可能性がある(nilのようなもの)
  • その値を確認できる

ユーザ設定のロードで日付を取り出す過程…NSDate型か格納されているかは不明

let userDefaults = NSUserDefaults.standardUserDefaults()
let lastRefreshDate: AnyObject? = userDefaults.objectForKey("LastRefreshDate")

キャスト成功か値を確認

if let date = lastRefreshDate as? NSDate {
    println("\(date.timeIntervalSinceReferenceDate)")
}

nilじゃないのが確実ならそのまま呼んでも大丈夫

let myDate = lastRefreshDate as NSDate

nilの扱い

  • Swiftでは、全ての値はnilではない
  • ただし、Swiftでは値がない事を示すためにnilを使える
  • 一方、Objective-Cの世界ではnilだったりnilでなかったりする
  • Objective-Cのオブジェクトを利用する前に、nilチェックをすることが確実(値が確実に空じゃないといえる場合を除く)

Extension

  • Objective-Cのcategoryに近い
  • 既存クラス(class)や構造体、列挙体の振る舞いを拡張する
  • カスタム型の拡張が出来る
  • システムフレームワークの拡張が出来る
  • プロパティを追加できる(算術のみ/書換は無理)
  • protocol追加に利用できる
  • Objective-C型のメソッドやプロパティはoverrideできない

拡張方法

  1. モジュールをimportで読み込む
  2. 同じ名前のクラスや構造体、列挙体を参照する
  3. 追加する中身を記述

 

extension CGRect {
    var area: CGFloat {
    return width * height
    }
}

クロージャ(Closures)

  • Objective-CのblockとSwiftではクロージャは互換性がある
  • クロージャと関数は同じ型
  • Objective-Cの__blockがSwiftでは標準的な値の扱われ方となる
void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) {/* ... */}
let completionBlock: (NSData, NSError) -> Void = {data, error in /* ... */}

Swiftでのオブジェクト比較

  • ==を使う(オブジェクトの中身の比較)
  • ===を使う(定数/変数が同じインスタンスを参照しているか)

 

 

Objective-CからSwiftを使う場合

SwiftからObjective-Cへのインポートも似たようなイメージ(Swiftで書いたAPIがObjective-Cヘッダとして生成されSwiftモジュールとして利用可能になる)

  • StringはNSStringになるなど上記とは逆方向マッピング
  • 一部はマッピングできないので注意(つまり新言語Swiftのみの進化した言語機能、特徴的な部分は旧言語Objective-Cとは互換がとれないところもある)

 

 Swift言語の型互換性

Objective-Cから派生したSwiftのクラスは自動的に互換性が保たれる(@objcが自動挿入される)が、 Objective-Cから派生してないSwiftのクラスをObjective-Cのコードで利用する場合、@objcを使う必要がある。@objcによりObjective-Cの実行環境でSwiftのAPIが利用できるようになる。

  • Objective-Cから利用したいメソッド、プロパティ、クラスの前に@objc
  • メソッドのシグネチャーが翻訳される(Swift->Obj-Cのケースの逆)
  • @objcをObjective-Cで参照される時の別名定義のために使えるSwiftで書かれたクラス側とクラス名を一致させる時に@objcを使う(Objetive-Cでアーカイブオブジェクト(Archived Objects)を扱っている場合オブジェクト内にクラス名が保存されるため)
@objc(Squirrel)
class りす {
    @objc(initWithName:)
    init (名前: String) { /*...*/ }
    @objc(hideNuts:)
    func 隠す(Int) { /*...*/ }
}

Objective-Cセレクタ

  • Objective-Cのセレクタはメソッド名を参照する型である。
  • SwiftではObjective-Cから派生したクラスの場合、クラス内のメソッドやプロパティはObjective-Cのセレクタとして利用できる
  • SwiftではObjective-Cから派生していないクラスの場合、@objをつける事でセレクタとして利用できる
  • performSelector:メソッドはSwiftには導入されていない

Objective-Cのクラスから派生しているメンバ

import UIKit
class MyViewController: UIViewController {

    func tappedButton(sender: UIButton!) {
        println("tapped button")
    }
}

これはSelector型と同じ。

let mySelector: Selector = "tappedButton:"

 


Optionals

オプション(optional)型は「値が存在しない」ことを表す。Objective-Cにおけるポインタのnilに近い概念だが、クラスではなくすべての型に動作するもの。オプションはObjective-Cのポインタより安全かつ様々な表現を持つ。Swiftの最大の特徴である機能の心臓部に位置する概念。

 

 Posted by at 16:52