2017年1月15日

センター試験英語 2017

今年は172点。
時間はぎりぎり。もう少し読解スピードが上がると確認に時間を取れるようになる。
前半を間違えるのはいいけど、後半の間違いはくやしい感じ。

2016年12月18日

Xcode 8.2はシミュレータでスクリーンショットを撮ると落ちる on El Capitan

Xcode 8.2は、シミュレータに付いているスクリーンショット撮影機能(File->Save Screen Shot)を使うと落ちます。厳密に言うと1回目はOKで2回目に落ちる。

対策は
シミュレータで、Edit->Copy Screen
Previewで、File->New from Clipboard
手間もそんなに変わりません。

stackoverflowの記事(英語)

2016年12月16日

libswiftCore.dylibをロード出来ない問題

証明書はXcodeで自動で作成するようになったので、キーチェーンアクセスからappleの名前のものを手当たり次第削除して、Xcodeで一から作成したところ、アプリがシミュレータでは動作出来るのに、実機で実行できない問題にあたりました。Xcode 8.1です。
dyld: Library not loaded: @rpath/libswiftCore.dylib
というエラーが出ます。

ググったらキャッシュをクリーンさせる系の解決策もありましたが状況は変わらず。
最終的に
http://developer.apple.com/certificationauthority/AppleWWDRCA.cer
をダウンロードすることで解決しました。(プロジェクトのクリーンもやったかも)
https://forums.developer.apple.com/thread/21292
に書かれています。
このAppleWWDRCA.cerが何をするものかはわかりません。

2016年9月27日

No Common Blocks - Xcode 8から起こりがちなリンクエラー

Build SettingにあるNo Common BlocksというものがXcode 8からデフォルトでOnの模様。古いプロジェクトを初めてXcode 8で開くときに出てくる、コンパイラ設定変更の警告に従うとOnにされる。

これがOnだとObjective-Cのプロジェクトで、例えば
  • あるヘッダーでグローバル変数を定義している(例 : int a; )
  • そのヘッダーが2箇所以上から#importされる
の条件でリンクエラーを起こします。
externで回避しました。

  • ヘッダー側はextern int a;
  • 実装側でint a;

externの説明は他を参照して下さい。

検索用
Xcode 8
No Common Blocks
Apple Mach-O Linker Error
Linker command failed with exit code 1 (use -v to see invocation)
Message from debugger: Terminated due to signal 15

2016年9月13日

Swift 3.0でStringはどうなったか

前回Swift2.0のStringの仕様を調べたが、編集系(append, insert, remove, replace)のメソッドの仕様は整理しきれていない、というくやしい終わり方をした。
Swift 2.0でStringはどうなったか - 特殊文字などの扱い

今回はまず、この編集系メソッドから見ていく。
編集系メソッドの引数に与えるものは主にCharacterとStringであるが 、そのほかにCharacterを要素にもつシーケンスというものがある。これは、名前がSとして
<S : Sequence where S.Iterator.Element == Character>
という性質を持つ。ここではこれを「Characterシーケンス」ということにする。

append(連結)系

Swift2.0ではメソッド名がバラバラだったものが、めでたくappendに統一された。
CharacterやStringの場合は引数ラベルなしで与える。
Characterシーケンスの場合はラベルにcontentOfが付けられる。
mutating func append(_ c: Character)
mutating func append(_ other: String)
mutating func append<S : Sequence where S.Iterator.Element == Character>(contentsOf newElements: S)

このほかに土台のStringに変更を加えないで、与えられたものを追加したStringを返すメソッドがある。
func appending(_ aString: String) -> String
これは土台のStringが変更されないので、mutatingが付けられていない。他の編集系メソッドにも語尾がingでこの性質のメソッドがある。

insert(挿入)系

これも名前がinsertに統一された。
appendと同じようにCharacterシーケンスの場合はラベルにcontentOfが付けられる。
Stringを受けるものはない?
mutating func insert(_ newElement: Character, at i: String.Index)
mutating func insert<S : Collection where S.Iterator.Element == Character>(contentsOf newElements: S, at i: String.Index)

remove(削除)系

remove系は1文字削除、範囲指定削除、全削除の3つ。
1文字削除の場合はatラベルが付くが、範囲指定削除の場合はラベルなし。Allは仕方ないとして、1文字削除と範囲指定削除の統一の夢は果たせなかった。
範囲指定の場合、HalfOpenでも可。
mutating func remove(at i: String.Index) -> Character
mutating func removeSubrange(_ bounds: Range<String.Index>)
mutating func removeAll(keepingCapacity keepCapacity: Bool = default)

replace(交換)系

場所の指定方法はRange指定のみ。Index指定はなし。範囲指定はHalfOpenでも可。
交換するものはStringかCharacterコレクション。Characterはなし。

mutating func replaceSubrange(_ bounds: Range<String.Index>, with newElements: String)
mutating func replaceSubrange<C where C : Collection, C.Iterator.Element == Character>(_ bounds: Range<String.Index>, with newElements: C)

土台Stringに変更を加えないで編集したものを返す、mutatingが付かないメソッドがある。
func replacingCharacters(in range: Range<String.Index>, with replacement: String) -> String

土台Stringに変更を加えないで範囲内を置換する
func replacingOccurrences(of target: String, with replacement: String, options: String.CompareOptions = default, range searchRange: Range<String.Index>? = default) -> String

2016年9月12日

Swift2.2からSwift3.0への変換を行ってみて

Xcode8のGMが出たので、Swiftを2.2から3.0へ変換しました。自動でコンバートするXcodeの機能を使いました。
変換箇所があらかじめ表示されるので、一通り見て気が付いたことを書いていこうと思います。

enumの要素の頭文字が小文字に統一
どっちが正解なのかなぁと思いながらプログラムを作成していた部分なのではっきり決めてくれて良かったです。

配列の初期化メソッド
配列の初期化によく使われる
init(count: repeatedValue: )が
init(repeating: count: )
になりました。順番が変わって、さらに現在分詞になりました。

NS抜け
NSBundle が Bundle へ変更
NSIndexPath が IndexPath へ変更
NSURL が URL へ変更
Appleに従うのみ。

タイプメソッド扱いしていたものがタイププロパティ扱いに
NSBundle.mainBundle() が Bundle.main へ変更
UIScreen.mainScreen() が UIScreen.main へ変更
UIDevice.currentDevice() が UIDevice.current へ変更
UIColor.whiteColor() が UIColor.white へ変更
//例
open class var white: UIColor { get }
従うのみ。

CoreGraphics衣替え
CGRectMakeなどのC言語ベースのCG***関数がSwift3.0では廃止になっています。
(旧)CGRectMake(***) が
(新)CGRect(***) へ変更、CGRect(***)の方はSwiftの構造体のinit命令呼び出しです。

(旧)CGPointMake(***) が
(新)CGPoint(***) へ変更、CGPoint(***)の方はSwiftの構造体のinit命令呼び出しです。

(旧)CGRectContainsPoint(CGRect rect, CGPoint point) が
(新)CGRectインスタンスに対するcontains(_:)メソッドへ変更

(旧)CGContextFillPath(CGContextRef cg_nullable c) が
(新)CGContextインスタンスのfillPath()に変更
など

UIControlState()
UIControlState.Normal は UIControlState() へ変更。
Normalの方がわかりやすい?

Boolインスタンス名にisが付いた
UIViewのhidden が isHidden に変更
UIScrollViewのscrollEnabled が isScrollEnabled に変更
Boolを返すものはisが付けられる変更です。従うのみ。

Stringの編集系メソッド
appendなどがマイナーチェンジされました。
こちらに詳しく書いてます。
Swift 3.0でStringはどうなったか

enumerated
配列からインデックスと要素を取り出すArrayインスタンスのenumerate() が enumerated() と過去分詞になった。
同じような変更は他にもいくつかあり、対象の変更、非変更によって原形か(ing型 or ed型)になるらしい。変更の場合は原型、非変更の場合は(ing型 or ed型)。
ingとedの違いは対象が()内にある場合はing、前にある場合(つまりメソッドを呼んでいるインスタンス)はedと思うが要調査。
abc.defed() //abcが対象?
abc.defing(ghi) //ghiが対象?

UIViewControllerの回転系
shouldAutorotateと
supportedInterfaceOrientations
がメソッドからcomputedPropertyになりました。
従うのみ。

メソッド第一引数特別扱いの廃止への対応はいろいろ
Swift2.2で
//定義
func testFunc(labelA: Int, labelB: Int)

//呼び出し
someInstance.testFunc(3, labelB: 5)
というメソッドがあった場合、
これをSwift3.0では
//定義部を変えない方法
//定義
func testFunc(labelA: Int, labelB: Int)    //←変更しない

//呼び出し
someInstance.testFunc(labelA:3, labelB: 5)    //←ラベル付きに変更
とするか
//呼び出し部を変えない方法
//定義
func testFunc(_ labelA: Int, labelB: Int)    //←ラベルなし明記に変更

//呼び出し
someInstance.testFunc(3, labelB: 5)    //←変更しない
といろいろな変更方法があります。
実際の変更方法もいろいろありました。例をいくつか挙げます。
//Swift2.2
applicationWillResignActive(application: UIApplication)

//Swift3.0
applicationWillResignActive(_ application: UIApplication)
applicationという言葉はすでにメソッド名にある。ラベルにいれたら重複。

//Swift2.2
tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath:NSIndexPath)

//Swift3.0
tableView(_ tableView: UITableView, didSelectRowAt indexPath:IndexPath)
tableViewがメソッド名になっている。ラベルにいれたら重複。

UIViewの描画メソッド
//Swift2.2
drawRect(rect: CGRect)

//Swift3.0
draw(_ rect: CGRect)
rectという表現がメソッド名から消えた。rectであることは引数がCGRectインスタンスであることで表現されているからと推測。

//Swift2.2
numberOfSectionsInTableView(tableView: UITableView) -> Int

//Swift3.0
numberOfSections(in tableView: UITableView) -> Int
メソッド名からTableViewが消えて、inがラベルに追加になった。

Stringの
//Swift2.2
drawInRect(rect: CGRect, withAttributes attrs: [String : AnyObject]?)

//Swift3.0
draw(in rect: CGRect, withAttributes attrs: [String : Any]? = nil)
Rectが消えて、ラベルにinを追加。

同じことを2度言わないの法則があるみたいですね。
これらのように、基本的な方針を基にその場に応じた判断をされています。

NSNumberリニューアル
Swift2.2ではビット数とメソッド名の関係がObjective-Cの束縛を受けていましたが、Swift3.0になって完全にSwiftになりました。
//Swift2.2
    public init(char value: Int8)
    public init(unsignedChar value: UInt8)
    public init(short value: Int16)
    public init(unsignedShort value: UInt16)
    public init(int value: Int32)
    public init(unsignedInt value: UInt32)
    public init(long value: Int)
    public init(unsignedLong value: UInt)
    public init(longLong value: Int64)
    public init(unsignedLongLong value: UInt64)
    public init(float value: Float)
    public init(double value: Double)
    public init(bool value: Bool)

    @available(iOS 2.0, *)
    public init(integer value: Int)

    @available(iOS 2.0, *)
    public init(unsignedInteger value: UInt)
    
    public var charValue: Int8 { get }
    public var unsignedCharValue: UInt8 { get }
    public var shortValue: Int16 { get }
    public var unsignedShortValue: UInt16 { get }
    public var intValue: Int32 { get }
    public var unsignedIntValue: UInt32 { get }
    public var longValue: Int { get }
    public var unsignedLongValue: UInt { get }
    public var longLongValue: Int64 { get }
    public var unsignedLongLongValue: UInt64 { get }
    public var floatValue: Float { get }
    public var doubleValue: Double { get }
    public var boolValue: Bool { get }

    @available(iOS 2.0, *)
    public var integerValue: Int { get }

    @available(iOS 2.0, *)
    public var unsignedIntegerValue: UInt { get }
//Swift3.0
    public init(value: Int8)
    public init(value: UInt8)
    public init(value: Int16)
    public init(value: UInt16)
    public init(value: Int32)
    public init(value: UInt32)
    public init(value: Int64)
    public init(value: UInt64)
    public init(value: Float)
    public init(value: Double)
    public init(value: Bool)

    @available(iOS 2.0, *)
    public init(value: Int)

    @available(iOS 2.0, *)
    public init(value: UInt)

    open var int8Value: Int8 { get }
    open var uint8Value: UInt8 { get }
    open var int16Value: Int16 { get }
    open var uint16Value: UInt16 { get }
    open var int32Value: Int32 { get }
    open var uint32Value: UInt32 { get }
    open var int64Value: Int64 { get }
    open var uint64Value: UInt64 { get }
    open var floatValue: Float { get }
    open var doubleValue: Double { get }
    open var boolValue: Bool { get }

    @available(iOS 2.0, *)
    open var intValue: Int { get }

    @available(iOS 2.0, *)
    open var uintValue: UInt { get }

似たような名前のメソッドの場合、統一できるものは統一
NSCoderの場合だと
//Swift2.2
public func encodeDataObject(data: NSData)
public func encodeObject(object: AnyObject?)

public func encodeObject(objv: AnyObject?, forKey key: String)
public func encodeBool(boolv: Bool, forKey key: String)
public func encodeInt(intv: Int32, forKey key: String)
public func encodeInt32(intv: Int32, forKey key: String)
public func encodeInt64(intv: Int64, forKey key: String)
public func encodeFloat(realv: Float, forKey key: String)
public func encodeDouble(realv: Double, forKey key: String)
public func encodeInteger(intv: Int, forKey key: String)
//Swift3.0
open func encode(_ data: Data)
open func encode(_ object: Any?)

open func encode(_ objv: Any?, forKey key: String)
open func encode(_ boolv: Bool, forKey key: String)
open func encodeCInt(_ intv: Int32, forKey key: String)
open func encode(_ intv: Int32, forKey key: String)
open func encode(_ intv: Int64, forKey key: String)
open func encode(_ realv: Float, forKey key: String)
open func encode(_ realv: Double, forKey key: String)
open func encode(_ intv: Int, forKey key: String)
Swift3.0ではencodeに(ほぼ)統一されました。数値のビット数がSwift2.2ではObjective-Cの影響を受けていますが、3.0で完全Swiftになりました。

ちなみに
UIViewの
open func bringSubview(toFront view: UIView)
open func sendSubview(toBack view: UIView)
のように、ラベル化しても
外部引数ラベル = 引数インスタンス
の関係になっていないパターンもあります。toFrontは操作の詳細の説明なので。

あと、Cスタイルのfor文をSwift3で書き換えるときにハマるパターンについてQiitaに書きました。
Cスタイルのfor文をSwift3で書き換えるときにハマるパターン

感想
以前から、Swiftでメソッドを作成するときは一つ目の引数に外部引数名と内部引数名に同じものを書くやり方をしていたので今回その部分は移行がスムーズになりました。
UIBezierPathのメソッドに、呼び出し方がmoveToPoint(***)からmove(to: ***)に変更されたものがあります。***がインスタンス名だけでは何の型のインスタンスかわからない場合もあるでしょう。情報を省くことは一長一短かと思います。
破壊工程、非破壊工程の違いによる、メソッド名に動詞の原形、現在分詞、過去分詞の使い分けが難しいです。ニュアンスが非英語圏にはわからない。

参考
Swift API Design Guidelinesの紹介(Swift 3版)

2016年9月11日

iOS10対策

Privacy - Media Library Usage Description
この問題はシミュレータ(Xcode8.0)で発生したが、実機では発生しませんでした。
iPodライブラリの曲をアプリ内で使うとき、ユーザーに許可を取るようになりました。
この時の文面をinfo.plistに設定する必要があります。
Privacy - Media Library Usage Description(rawキーではNSAppleMusicUsageDescription)に何か文を追加します。
これを設定しておくと、アプリがライブラリにアクセスしようとしたところで、Don't allow / OKダイアログをOSが勝手に出してくれて、その中に先ほどの自分で追加した文が表示されます。
これを設定しないと、アプリが落ちます。
出す文章を各言語毎に変えたい時は、InfoPlist.stringsのローカライズをするとよいでしょう。

xibファイルのBuilds for
この問題はシミュレータ(Xcode8.0)で発生したが、実機では発生しませんでした。
[UIDeviceRGBColor length]で実行時エラーが発生しました。これは自分で書いたプログラムではないので原因がわかりにくかったのですが、xibファイルのBuilds forが未設定でした。Xcode3の時に作成したプロジェクトで発生しました。xibファイルのBuilds forをDeployment Target (8.0)にしました。

2016年8月26日

Swift2 標準ガイドブック

説明は全体的にざっくりアバウトな感じなので荻原本などを併用すること。

気がついたこと

イニシャライザの項目に間違いが多いので修正があるといいです。

2016年7月7日

詳解Swift改訂版買いました

詳解Swift改訂版を買いました。

Objective-C版も持っています。Objective-C版に比べて推敲が甘いかなと感じる部分が特に最後の方で多かったです。版を重ねて修正が加わるといいです。

以下、自分用のメモ。

構造体

イニシャライザ内でメソッドを呼ぶのは初期値設定が全部済んでから。
構造体の内部で別の構造体を定義出来る。
構造体の内部で、typeエイリアスを使って、別名を与えることが出来る。(メリット?)
タイププロパティの値を変更するインスタンスメソッドにはmutatingは不要。
タイププロパティの初期化のための式は値が必要とされた時に初めて評価される。
didSet WillSetはinit()内での設定では呼ばれ無い。didSetで値を設定しても再帰的に呼ばれることはない。
オプショナル
オプショナル==nilはnilかどうか
オプショナル==非オプショナルは中身を比べることが出来る
if letのほかにif varもある

オプショナル

readLineは標準入力
??の右はオプショナルでもいい。??は連鎖できる。

配列

配列を反対にするreverse()というメソッドがある。

String

Characterの配列からStringインスタンスを作るinitメソッドがある
UnicodeScalarにはvalueプロパティがあって値を取り出せる

辞書

新規はdict[a] = aaa
削除はdict[b] = nil
updateは更新、ただしもとからない場合は作成しない。

Tuple

let aTuple = (1, 2, 3)
let (a, b, c) = aTuple //これが出来る
let (aa, _, _) = aTuple //使わないときは_が使える
値が一つだけのTupleはその値だけと変わらない
要素が一つもないことを()で表現。Voidは()の別名
違うキーワードのTuple間の代入は無理。型が同じでも。一度キーワードなしにキャストすればOK
キーワード付きのタプルの代入では順番が違っていてもキーワード毎に要素が代入される。キーワードがない部分は順番通りに代入される。
タプルにTypealiasでなまえをつけることができる。


Switch

オプショナル付きのタプルを扱える

switch a {
case (0, b?):
    break
}

のように使う。

Enum

それぞれの要素は大文字で始める
計算型のプロパティは作成出来る。格納型は無理。
タイププロパティなら格納型、計算型のも作成出来る
タイプメソッドも定義出来る
enumのメソッドで自身を表すselfが使える
enumにはunion typeもある。付加情報またはペイロードという。
値型と共用型は別のものとしないといけない。
enum TestEnumUnion {

 enum TestEnumUnion {
 case testUnionStr(String) 
 case testUnionInt(Int, Int) 
}

let a = TestEnumUnion.testUnionInt(0, 0)
let aa = ["aa" : a]

if case .testUnionInt(0, 0)? = aa["aa"] {    //これができる
}

演算子

a % b = cで余りcの符号はaと同じ。bの符号によらない。
===と!==はインスタンスの実態が同じかどうか

@autoclosureをつけると遅延評価する

演算子の宣言はトップレベルで
値を変更するときも&は不要

クラス

クラスのインスタンスをletにしても格納型プロパティを変更出来る。
Super Classを持たないクラスはベースクラス
is はサブクラスでもtrueを返す
dynamicTypeはそのインスタンスのクラスを表す

メモリ管理

クロージャは参照型
===はクラスのインスタンス同士に使って、インスタンスが同一かどうか
新しく作成したインスタンスをweak変数に入れるとすぐに解放される。変数はnilになる。

下のb.dynamicTypeのオプショナルチェーンの返りはOptional<Int>

class A {
 func funcA(a: Int) -> Int? {
  if 0 < a {
   return a
  } else if a < 0 {
   return -a
  } else {
   return nil
  }
 }
}

var a:A? = A()

let b = a?.funcA(100)

b.dynamicType

オプショナルチェーン経由で値を返さないfunctionを実行したら
cはOptional<()>になる

class A {
 func funcB() {
 }
}

var a:A? = A()

let c = a?.funcB()

c.dynamicType


オプショナルチェーンを左辺にして代入操作を行える。
右辺は評価される
****?.**** = ****()
途中でnilチェックに引っかかったら代入は行われない。
****?.****への代入はVoid?を返す。つまり

if (****?.**** = ****()) != nil {
}

が行える。

プロトコル

構造体や列挙体にもプロトコルを適用出来る。

  • インスタンスメソッド、タイプメソッド
  • 「演算子による関数」の宣言
  • インスタンスプロパティ、タイププロパティ
  • イニシャライザ
  • 添字付け
  • typealias宣言

プロトコルからプロトコルに継承出来る
クラスに付けられることが前提のプロトコルは名前の横に: classと書く。何かのプロトコルを継承するときは,に続けて書く。
複数のプロトコルを組み合わせて新しいプロトコルを作るprotocol protocolNew: プロトコル1, プロトコル2 {}という書き方がある。
protocol<プロトコル1, プロトコル2>という書き方も出来る。

2016年2月24日

xibでUIView内レイアウト設計してアプリに取り込むシンプルな方法 Swift編

1.UIViewのサブクラスのソースファイルを作成。

2.UIViewをベースにして新規xib作成(UIViewControllerではない)。

3.xibで、UIViewのクラスを1で作ったクラスに設定する。


4.作成したUIViewを取り込みたいUIViewControllerなどでインスタンスを作成する
let settingView: SettingView = NSBundle.mainBundle().loadNibNamed("SettingView", owner: nil, options: [:]).first as! SettingView

5.xib内でレイアウトを行う。

2015年12月4日

Swift Compiler Architecture (Swiftコンパイラの構造)

Swiftがオープンソースになって公開された資料の中でSwiftのコンパイラについて書かれている部分を抜粋し、日本語に訳す。意味がわからないところは(?)を付けた。

Compiler and Standard Library

The main Swift repositoryにはSwiftのコンパイラとスタンダードライブラリのソースコードがあります。

Compiler Architecture

Swiftで書かれたソースコードからLLVM IRに変換される工程です。上から下に流れていきます。
  • Parsing (lib/Parse) : parserはソースコードをAbstract Syntax Tree (AST)に変換します。ここでは意味の情報も、型の情報も扱いません。ソースに文法の問題があるときはメッセージを出します。
  • Semantic analysis (lib/Sema) : parseされたASTを整形された型チェック済みのASTに変換します。意味の問題があるときはメッセージを出します。ここには型推論が含まれます。
  • Clang importer (lib/ClangImporter) : Clang modulesをインポートします。そしてCやObjective-CのAPIと対応するSwiftのAPIをmapします(?)
  • SIL generation (lib/SILGen) : Swift Intermediate Language (SIL)とはSwift用の中間言語で、さらなる分析と最適化に適しています。SIL generationは型チェック済みのASTを"生の"SILに"レベル下げ"します。
  • SIL guaranteed transformations (lib/SILOptimizer/Mandatory : 変数の初期化などのデータの流れを診断します。この工程を終えると“canonical(正式な)” SILとなります。
  • SIL Optimizations (lib/SILOptimizer/Analysislib/SILOptimizer/ARClib/SILOptimizer/LoopTransformslib/SILOptimizer/Transforms) : ARCなどの最適化を行います。devirtualization(?)やgeneric specialization(?)も行います。
  • LLVM IR Generation (lib/IRGen) : IR generationはSILをLLVM IRに"レベル下げ"します。ここからLLVMによる最適化と機械語生成へと続いていきます。

参考
Swiftの構文解析の流れを超ざっくり追ってみる
Swiftのコンパイラを改造して独自構文を追加する
Swiftオープンソース化の衝撃
Swiftコンパイラの構造と基盤テクニック
オープンソースSwiftにコントリビュートしよう
2016/06/25の技術書典でSwiftの本を出します
Swift Internal Introduces サポートページ
Swift中間言語の、ひとまず入り口手前まで


2015年11月15日

Objective-Cで保存したアーカイブデータをSwiftで読み込む

アプリをObjective-CからSwiftに書き換える際、少し悩むことに、Objective-Cで保存したアーカイブデータ(ユーザーのデバイスに保存されている)をSwiftで読み込むにはどうすればいいのかということがあります。
ここでは、NSKeyedUnarchiverやNSKeyedArchiverクラスを用いて利用するアーカイブデータがデバイスに保存されている場合に限定して、アプリをSwiftに書き換える時の手順などをまとめます。


ファイルの場所の指定
データの保存と読み込みではファイルの場所を指定します。ファイルの場所の指定はObjective-CとSwiftでほとんど同じ感覚でいけます。
var fileName = ""
let paths: [String] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
if 0 < paths.count {
    fileName = paths[0]
    fileName.appendContentsOf("/targetObject.dat")
    //print(fileName)
}
if 0 < paths.countは一応入れといた。
ちなみにstringByAppendingPathComponentというNSStringクラスのメソッドは使えなくなったようです。追加するものの前に自動的に/を加えるメソッドです。URLクラスに同じ機能のメソッドがありそっちを使えと表示が出ます。使えなくなったのが意図的だとすればStringとして扱うかURLとして扱うか中途半端ということでしょうか。


アプリにアーカイブ機能を付けるためにソースを書く部分
主に3つあります。
1 : データを保存するところ
2 : データを読み込むところ
3 : データ自体の定義


データの保存/読み込み
データの保存/読み込みの仕方は
A : オブジェクトをファイルに直接アーカイブする
B : NSDataを介してオブジェクトをファイルにアーカイブする
と2つあります。保存時と読み込み時でやり方が「対」になっていると余計な問題が出ることを避けられると思います。Objective-CでAの方法でアーカイブを行ったものはSwiftでもAのやり方で取り出すとうまくいきます。

A : オブジェクトをファイルに直接アーカイブする
//読み込み
if fileName != "" {
    if let obj = NSKeyedUnarchiver.unarchiveObjectWithFile(fileName) as? TargetObject {

    } else {
        //print("失敗")
    }
}
//書き込み
if fileName != "" {
    //objが保存したいオプジェクト
    let success = NSKeyedArchiver.archiveRootObject(obj, toFile: fileName)  
    if success {
        //print("保存に成功")
    }
}

B : NSDataを介してオブジェクトをファイルにアーカイブする
//読み込み
if let data = NSData.init(contentsOfFile: fileName) {
    let unarchiver = NSKeyedUnarchiver.init(forReadingWithData: data)
    if let targetObject = unarchiver.decodeObjectForKey("targetObject") as? TargetObject {
        //print("データ型OK")
    }
    unarchiver.finishDecoding()
} else {
    //print("データ取得失敗")
}
//書き込み
let data = NSMutableData.init()
//objが入れるデータ
let archiver: NSKeyedArchiver = NSKeyedArchiver.init(forWritingWithMutableData: data)
archiver.encodeObject(obj, forKey: "targetObject")
archiver.finishEncoding()
data.writeToFile(fileName, atomically: true)


データ自体の定義
ここではObjective-Cで設計したクラスをSwift側でも同じように再度設計するとします(このほかにObjective-Cで設計したクラスをSwift側からアクセスする方法もあります)。
BoolひとつとIntふたつの場合
@objc(TargetObject)
class TargetObject :NSObject, NSCoding {

    var boolValue: Bool = false
    var intValue1: Int = 0
    var intValue2: Int = 0
 
    @objc func encodeWithCoder(aCoder: NSCoder) {
 
        aCoder.encodeBool(boolValue, forKey: "boolValue")
        aCoder.encodeInteger(intValue1, forKey: "intValue1")
        aCoder.encodeInteger(intValue2, forKey: "intValue2")
    }
 
    @objc required init?(coder aDecoder: NSCoder) {
        super.init()
  
        boolValue = aDecoder.decodeBoolForKey("boolValue")
        intValue1 = aDecoder.decodeIntegerForKey("intValue1")
        intValue2 = aDecoder.decodeIntegerForKey("intValue2")
    }// NS_DESIGNATED_INITIALIZER
 
    override init() {
        super.init()
    }
}
ポイントは頭の@objc(TargetObject)です。これが必要です。NSObjectの継承も必要です。NSCodingも付けといてください。

データのビット数について
Objective-CとSwiftでは数値系データの名前やらビット数やらが別のものになったので確認しておきます。
Int32とInt64のような名前にビット数が付く指定制はObjective-CとSwiftで名前とバイト数の混乱はないですが、Objective-Cのint、NSIntegerと、SwiftのIntはちょっと変則的で

Objective-Cでは
int 32ビット
NSInteger 32ビット環境では32ビット、64ビット環境では64ビット

Swiftでは
Int 32ビット環境では32ビット、64ビット環境では64ビット

となっています。つまりビット数を揃えようとすると
Objective-Cでintに対応するものはSwiftではInt32しかなく、
Objective-CでNSIntegerに対応するものがSwiftではIntになる。
となります。

これを受けてSwiftではエンコード時、デコード時のメソッド名のビット数と名前の組み合わせはObjective-Cのものを採用しています。つまりデコードは
decodeIntForKey(32ビットを扱う)
decodeIntegerForKey(32ビット環境では32ビット、64ビット環境では64ビットを扱う)
となります。encode側も同じネーミングの考え方をしています。
となると
Objective-Cでintでencodeしたものは、Swiftで値を入れるときにdecodeIntForKeyを使い、SwiftのInt32に入れる。
Objective-CでIntegerでencodeしたものは、Swiftで値を入れるときにdecodeIntegerForKeyを使い、SwiftのIntに入れる。
とする全ての過程でビットが統一されます。
しかし、

Objective-CでintのものはSwiftでもIntにしたいよね。シンプルな型名のものがいいんだ。

という気持ちもあると思います(あることにしてください)。
そこで、整数をencode時とdecode時で違うビット数として扱ったらエラーが出るかどうか確認しました。
するとなぜかエラーは出ずに正常に実行されました。調べるとNSCoder Class Referenceの中に

A coder object stores object type information along with the data

とありました。データと一緒に型の情報を入れてエンコーディングするということでしょうか。
さらに調べるとArchives and Serializations Programming GuideのType Coercionsという項目に

NSKeyedUnarchiver supports limited type coercion. A value encoded as any type of integer, be it a standard int or an explicit 32-bit or 64-bit integer, can be decoded using any of the integer decode methods. Likewise, a value encoded as a float or double can be decoded as either a float or a double value.

を発見。limited type coercionがちょっとわかりませんが、2文目以降を読むとどうやらInt系、float系さえ守っていれば任意のバイト数での取り出しが大丈夫のようです。ただし、あふれは当然自己責任になります。たしかにこの仕様になっていると32bitデバイスからiCloudを経由して64bitデバイスにデータを移す際のトラブルも防ぐことが出来そうですね。

Arrayなど
さて次はオブジェクト型です。Objective-CとSwiftで型名が違うもののうちArrayとStringについて調べました。
NSString型からStringもNSArrayからArrayもいけました。互換性あります。なのでObjective-CでNSArrayに入れたNSStringは、SwiftでArrayに入ったStringとして取り出せます。
SwiftのデコードメソッドはInt系やFloat系は非オプショナルとして取り出せますが、AnyObject系は取り出したときにオプショナルとなるのでアンラップの処理が必要になります。
if let stringValueCandidate = aDecoder.decodeObjectForKey("stringValue") as? String {
    stringValue = stringValueCandidate
}
if let sampleArrayCandidate = aDecoder.decodeObjectForKey("array") as? [String] {
    sampleArray = sampleArrayCandidate
}

アンアーカイブで落ちる
Swift内でアーカイブ対象のクラスを書くときに@objc(クラス名)が必要ですが、そのクラス名はアーカイブされているものの(アーカイブされた時点での)クラス名を書きます。これがないとNSKeyedUnarchiver.unarchiveObjectWithFile()やNSKeyedUnarchiverのdecodeObjectForKey()メソッドで落ちます。
Swiftでは@objc(クラス名)は見ていないのかと思ったのですが、保存するときに@objc(クラス名)が書いてある場合は、取り出すときにも同名の@objc(クラス名)が必要のようです。

2015年11月11日

Swiftのオプショナルのオプショナルて何??


オプショナルのオプショナルはあります。
正式な呼び方がなんなのかはわからないけど確かにあります。ちょっと長くなるけど調べたときのいきさつ形式で書きます。

エラーハンドリングについて調べていたときに以下のコンパイルエラーに遭遇しました。

Value of optional type 'Int?' not unwrapped; did you mean to use 'try!' or chain with '?'?
オプショナルの値のInt?はアンラップされません、やろうとしていることはtry!か、?を使った連鎖で出来ませんか?

このエラーは以下のようなプログラムの最後にある try? で出ました。実行はPlaygroundです。
enum MyError: ErrorType {
  case myError
}

func someThrowingFunction(a: Int) throws -> Int {
  if 1 < a {
    throw MyError.myError
  }
  return a
}

func someFunction() {
  let a: Int = try? someThrowingFunction(5) //ここでエラー発生
}
このエラーメッセージは、「try? というのは戻り値を格納する値の型をオプショナル型にする必要があるのでして下さい」という趣旨のメッセージです。ここでは最後のaの宣言を Int? にすればよいです(または型を書かずにコンパイラに推論させればよい)。
まあこれでこの件は解決なんですが、ただ、メッセージの最後の chain with '?' (?を使った連鎖)が気になりました。chainは連ねるという意味ですから??のように重ねること?なぜこの状況でchainが出てくるのか?といろいろ考えているうちに、もしかして ?? というものがあるのでは?と思い、以下のプログラムを試してみました。メソッドの戻り値を Int? にしてみました。
//戻りをInt?に変更
func someThrowingFunctionOPT(a: Int) throws -> Int? {
  if 1 < a {
    throw MyError.myError
  } else if 0 <= a {
    return a
  } else {
    return nil
  }
}

func someFunction() {
  //aの型をInt?に変更
  let a: Int? = try? someThrowingFunctionOPT(5) //やはりエラー
}
こんどは最後の文でエラーメッセージが
Value of optional type 'Int??' not unwrapped...
とさっきの Int? の部分が Int?? になりました。
そこでその部分を
func someFunction() {
  //aの型をInt??に変更
  let a: Int?? = try? someThrowingFunctionOPT(5) //エラー消える
}
とするとメッセージは消えます。
a.dynamicType を調べると
Optional<Optional<Int>>
となりました。どうやらaはIntのオプショナルのオプショナルという型になったようです。恥ずかしながらオプショナルのオプショナルというものは初めて知りました。

で、さらに疑問が出てきます。もっと連結できるんではないかと。
func someThrowingFunctionOPT(a: Int) throws -> Int? {
 if 1 < a {
    throw MyError.myError
  } else if 0 <= a {
    return a
  } else {
    return nil
  }
}

//戻りがInt??のメソッドを追加
func someThrowingFunctionOPT2(a: Int) throws -> Int?? {
  let a: Int?? = try? someThrowingFunctionOPT(5)
  return a
}

func someFunction() {
  //aの型をInt???にするとエラーが消える
  let a: Int??? = try? someThrowingFunctionOPT2(5)
  a.dynamicType
}
これも同じように最後のaの型を Int?? では例のエラーメッセージが出て、Int??? にすると消えるパターンでした。a.dynamicType も
Optional<Optional<Optional<Int>>>
となりました。
きりがないのでもうやりません。

で、初めの疑問に戻りますが、chain with '?' は宣言した値の型に?をいくつかつけることで解決するのではないかという意味だと推測します。chainといっても1も含みます。なのでInt から Int? へのおすすめをする状況でもchianどうですか?と出てきます。メッセージが長くなるのをいやがって1も含むことにしたのかもしれません。ちなみに?の連鎖ならchain of ?なのではないか、withを使っているといるということは何か別の解釈があるのではないかという気もしますが。

で、やっとタイトルに対する結論
オプショナル型は入れ子にすることができる。オプショナルの中にオプショナルを入れることが出来る。

Int?型で中身nilのものをさらにオプショナルで2回くるんで(Int???)実行すると
//a: Int???
if let aa = a {
  //走る
}
if let aa = a! {
  //走る
}
if let aa = a!! {
  //走らない
}
となる。おもしろい。

次に、nilか非nilかのチェックをisで行おうとしてみます。このisの使い方は正式ではないのでエラーが出ます。ここではわざとエラーを出します。このエラーではアンラップの提案をされますが
//a: Int???
if a is Int { //Downcast from 'Int???' to 'Int' only unwraps optionals; did you mean to use '!!!'?
  
}
出てくるエラーは
Downcast from 'Int???' to 'Int' only unwraps optionals; did you mean to use '!!!'?
!!!してはどうですか?と提案されます。
この提案がプログラマの助けになる提案なのかどうなのかは置いといて、!!!が正式なものとされている様です。

???とか!!!とか初めて見たときは冗談かと思ったのですが、エラーが出たときに実行を続けようとする仕様を実装しようとするとこうなってしまうということなんでしょう。多重ラップの必要性はエラー処理以外でもあるのでしょうか。詳しい方ならわかるかもしれません。

2015年11月3日

従来のソフトのままでiPad Proの画面に対応する方法

以下の記事はちょっとまだ公式のソースを確認できていないので自己責任でお願いします。

iPhoneが3.5インチサイズから4.0インチサイズに移行したときは4.0用の起動画面を用意することで4.0に対応していることを示すやり方でした。用意しなければ真ん中に3.5インチサイズの画面が出て、黒い帯で上下の空いた領域を埋める仕様でした。iPhone6や6sのときも対応/非対応の区別は起動画面を用意するかしないかでした。

今回iPad Proが新規解像度2732 x 2048で出てきましたが、対応/非対応のポイントはやはり起動画面のようです。
ただ今回はファイルを用意するのではなく、Launch Screen File(storyboard風のやつ)が設定してあることが「iPad Proに対応している(2732 x 2048も想定して描画処理を書いてある)」ことを示すようです。図のSourceのほうではなくてFileのほうです。iPad Pro対応しないとき(iPadのレギュラーサイズの画面を引き伸ばしたいとき)は「なにも設定しない」にしてください。こうすることで内部でiPadレギュラーサイズとして描画したものをiPad Proの画面の大きさに引き延ばすような処理になります。
ちょっとまだ公式のソースを確認できていないので自己責任でお願いします。実機はまだ発売されていないので確認したのはシミュレータのみです。Xcodeは7.1です。

未来ぽい感じがして好きだったんですが一旦OFFにしておきますかね。

2015年10月20日

Swift 2.0でStringはどうなったか - 特殊文字などの扱い

Objective-CのNSStringの仕様は自然ではなかったので、Swiftには期待しています。
Swift2.0でStringの仕様がだいぶ変わったということで検証してみます。

昔は何が困ったか

今回のStringの仕様の検証は、昔の仕様で困っていた部分が動機になっているので、まずそれを書きます。NSStringでは文字を基本的に16ビットで扱っていますが、文字の中には16ビットで扱えないものがあり、そういうものは32ビットなど他の長さで使用します。そのごちゃごちゃはUnicodeの仕様上仕方ないのですが、NSStringではその32ビット文字を2文字としてカウントするなどUnicodeのごちゃごちゃをうまくさばききれていなくて扱いが雑な部分がありました。
欲しいものは、内部で何ビットだろうとどういうどういうデータ状態であろうと、人間が一文字と認識するものは一文字として扱うというポリシーに従ったAPIです。
Swiftが発表になったときにUnicodeとの親和性もアピールしていたので期待しています。



The Swift Programming Languageを読む

まずはいつものThe Swift Programming Languageで基本をおさらい。
・Stringはvalue typeである(reference typeではない)。
・Stringの文字列に含まれている個々の文字へのアクセスにはStringのcharactersプロパティからアクセス出来る。
・SwiftのStringは内部ではUnicode scalarをベースにしている。Unicode scalarとは文字や記号に対応する21ビットの数。U+0000からU+D7FFとU+E000からU+10FFFFがある。U+D800からU+DFFFは含まない。U+が何かというのは大変ややこしいので割愛。
・人間が文字と認識する形式がExtended Grapheme Cluster。SwiftのCharacterは一つのExtended Grapheme Clusterに対応。一つのExtended Grapheme Clusterは一つ以上のUnicode scalarで構成される
・Stringの文字数カウントはcharactersプロパティのcountプロパティで取得出来る。取得されるのはCharacterの数で、CharacterはExtended Grapheme Clusterをベースにしているので人間が文字と認識する形式で文字数が取得できる。
・String内の文字の位置(Characterの集まりとして見たときの各Characterの位置)はString.Indexという型によって表す。Stringのsubscript機能のアクセスに用いることが出来る。実態が少し疑問なので要調査。
・StringやCharacterの比較(==)はextended grapheme clusterを基に行われ、文字の意味が同じであればUnicode scalarが違っていても同一とみなす。
・StringをUTFエンコードされたデータとして取得したいときはutf8プロパティ、utf16プロパティ、unicodeScalarsプロパティを利用して取得できる。



Standard Libraryを読む

次にStandard Libraryを読みます。Standard Library Referenceも併用しながら見ていきます(便利なので)。

struct String {...}
Stringはstructとして宣言されている。

String内を見てみる(ほとんどはextension部分で定義されている)。
Stringの中でさらにstructなどが宣言されている
//かなりの抜粋
struct CharacterView : CollectionType {...}
var characters: String.CharacterView
ちょっとわかりにくいけどString内でCharacterViewというstructが宣言されていて、その宣言された型のプロパティを持つ。

そのCharacterViewをの中身を見てみる(ほとんどはextension部分で定義されている)。
CharacterViewは"Characterを集めたもの"という見方をしてよいというのがネーミング由来かと思われる。
//かなりの抜粋
struct Index {
  func successor() -> String.CharacterView.Index
  func predecessor() -> String.CharacterView.Index
}
var startIndex: String.CharacterView.Index
var endIndex: String.CharacterView.Index
subscript (i: String.CharacterView.Index) -> Character
subscript (subRange: Range<String.CharacterView.index="">) -> String.CharacterView
注目はIndex、これはstructで宣言されている。進む戻るのメソッドがあるしsubscriptの引数にも使われる。
Standard Libraryの学習用のPlayground内の情報によるとIndexは基本的にはString内の文字の位置を示すもので、文字ごとに異なるデータ量の違いを吸収してプログラマから(何文字目かなどの)文字指定を操作しやすくするコンセプトのものらしい。内部に整数を保持しているのかと思ってその数の定義箇所を確かめようとしたがちょっと見当たらず。
文字はメモリ内で何バイト使うかというのがバラバラなのでIndexに要求される機能は多くなりそう。
Indexの中身を予想すると
1先頭から何文字目か
2先頭から何バイト目か
3その文字は何バイト使っているか
などの情報を扱っていると思う。
これらはIndexの中に保持しているというより一つのString分をまるごとどこかに保持しているかもしれない。


CharacterViewはCollectionTypeプロトコルを付けているのでcountプロパティを持つ。
String内でCharacterViewに似ているものが他にも宣言されていて、
UnicodeScalarView
UTF16View
UTF8Viewがある。



基本プログラム

宣言と初期化
Unicode scalar値でStringを作ることが出来る。一文字だけならCharacterも同じように作れる。
//Character
var dollarSignChar: Character = "\u{24}"
var blackHeartChar: Character = "\u{2665}"
var sparklingHeartChar: Character = "\u{1F496}"

//1文字のString
var dollarSignStr: String = "\u{24}"
var blackHeartStr: String = "\u{2665}"
var sparklingHeartStr: String = "\u{1F496}"

//2文字のString
var dollarSigns: String = "\u{24}\u{24}"
var blackHearts: String = "\u{2665}\u{2665}"
var sparklingHearts: String = "\u{1F496}\u{1F496}"

単なる"a"は何型になるかPlaygroundでやってみる
var a = "a"
a is String//true
a is Character//false
var a: Character = "a"
a is String//false
a is Character//true
なにも明示しなければString、Characterと明示すればCharacterだった。


文字カウント
charactersプロパティのcountというプロパティで取得できます。
UTF8とUTF16のそれぞれのエンコードによる文字数(?)も取得出来る
countプロパティはCollectionTypeというプロトコルで宣言されてます。UTF8View、UTF16View、UnicodeScalarView、CharacterViewは皆、宣言にCollectionTypeプロトコルが付いています。
str.countで可能なようにして欲しい。
sparklingHeart = "\u{1F496}"//きらきらハートマーク
var numInCharacter = sparklingHeart.characters.count//1
var numInUTF8 = sparklingHeart.utf8.count//4
var numInUTF16 = sparklingHeart.utf16.count//2


subscriptの使い方
var str: String = "ABCDE"
str[str.startIndex]//"A"
//str[str.endIndex]//これは実行時エラー
str[str.endIndex.predecessor()]//"E"
str[str.startIndex.successor()]//"B"
str[str.startIndex.advancedBy(2)]//"C"
str[str.startIndex..<str.endIndex]//"ABCDE"
str[str.startIndex.successor()..<str.endIndex.predecessor()]//"BCD"
Indexを与えるとCharacterが返ってきて、Rangeを与えるとStringが返ってきます。


append(連結 )系
var str: String = "A"
str.append(Character("B"))
str.append(UnicodeScalar("C"))
str.appendContentsOf("DEF")
let charactersGHI: [Character] = ["G","H","I"]
str.appendContentsOf(charactersGHI)
StringをappendするときでもappendStringではない。appendContentsOfらしい。append出来るものはStringでなくても内部にCharacterを持つものであれば連結が可能。それもappendContentsOfというメソッド名。つまりappendContentsOfはStringを引数に持つものと内部にCharacterを持つものの両方が定義されている。名前を揃えて覚えやすくしたのか?appendStringがいいけど。


insert(挿入)系
str = "ABCDE"
str.insert(Character("c"), atIndex: str.startIndex.advancedBy(3))//ABCcDE
let charactersddd: [Character] = ["d","d","d"]
str.insertContentsOf(charactersddd, at: str.startIndex.advancedBy(5))//ABCcDdddE
let strbbb = "bbb"
str.insertContentsOf(strbbb.characters, at: str.startIndex.advancedBy(2))//ABbbbCcDdddE
insert系は2つ。Characterを受け付けるものと、"Characterの集まり"を受け付けるもの。Characterを受け付けるものはatIndexなのに"Characterの集まり"を受け付けるもののほうはat。


remove(削除)系
str = "ABCDE"
str.removeAtIndex(str.startIndex.advancedBy(2))//ABDE
str.removeRange(str.startIndex.advancedBy(1)..<str.startIndex.advancedBy(3))//AE
str.removeAll()//空
remove系は単独削除、複数削除、全て削除の3つ。全て削除の派生メソッドとしてキャパシティーを保持するかどうか指定するメソッドもある。


replace(交換)系
str = "ABCDE"
str.replaceRange(str.startIndex.advancedBy(1)..<str.startIndex.advancedBy(4), with: "bcd")
//AbcdE
str.replaceRange(str.startIndex.advancedBy(1)..<str.startIndex.advancedBy(4), with: ["い","う","え"])//AいうえE
str.replaceRange(str.startIndex.advancedBy(2)..<str.startIndex.advancedBy(3), with: "ウ")//AいウえE
replace系はStringを受け付けるものと、"Characterの集まり"を受け付けるもの。

連結、挿入、交換でStringを受け付けたり受け付けなかったり、Characterを受け付けたり受け付けなかったりしてバラバラな印象がある。今後のバージョンアップで統一されていくかもしれない。

疑問
IndexをIndex()のように作成することが出来ない。どのようにすればいいのか
引数でIndexを与えるときに簡単に書けるやり方があるか
startIndexは常に0(に該当するもの)のような気がするがこのプロパティどんなときに0以外になるのか



C言語の文字列の扱い

C言語文字列からStringインスタンスを作成するメソッドがある。クラスメソッドの
static func fromCString(cs: UnsafePointer<CChar>) -> String?
static func fromCStringRepairingIllFormedUTF8(cs: UnsafePointer<CChar>) -> (String?, hadError: Bool)
これらはnul-terminatedされたUTF-8フォーマットのものを受け付ける。
与えられたものがNULLならnilを返す。
2つのメソッドの違いはUTF-8解釈できないものがあるときに
上はnilを返す。
下はU+FFFDに置き換える。(たぶん置き換えがあったときにhadErrorがtrue)
これらのメソッドには@warn_unused_resultが付いている。@warn_unused_resultの詳細は保留。
他のエンコードされたデータから変換するメソッドは、Swift2.0のスタンダードライブラリにあるのはこれだけ。



別のエンコードのものを標準インスタンスにしたい(指定エンコードデータ→標準インスタンス)

Shift-JISなどのエンコードとの変換はNSStringのメソッドを使う方法しか見当たりませんでした。

指定エンコードデータから標準インスタンスへの変換は初期化のメソッドにいろいろあります。どれもinitにクエスチョンマークがついているのでnilを返す可能性があります。見た目はキャスト処理などもなく普通に実行していますが、メソッドが定義されているのはNSStringです。

Byteから
convenience init?(bytes bytes: UnsafePointer<void>, length len: Int,encoding encoding: UInt)
No copyバージョンのもある

NSDataから
convenience init?(data data: NSData,encoding encoding: UInt)
//使用例
let strKanji = String(data: dataKanji, encoding: NSShiftJISStringEncoding)!
びっくりマークが要ります。

unicharから
convenience init(characters characters: UnsafePointer<unichar>,length length: Int)
No copyバージョンのもある

CStringから
convenience init?(CString nullTerminatedCString: UnsafePointer<int8>,encoding encoding: UInt)

UTF8から
convenience init?(UTF8String nullTerminatedCString: UnsafePointer<int8>)
上の方で述べたようにSwiftスタンダードライブラリにもUTF8からの変換があります。

NSStringのメソッドにはクラスメソッドでインスタンスを作るやりかたもありますが、Swiftからの呼び出しはざっくり削除されてました(動作は未確認)


指定したエンコードで取り出したい(標準インスタンス→指定エンコードデータ)

こっちはシンプルです。

基本的にこれです
func dataUsingEncoding(_ encoding: UInt) -> NSData?

あいまいな変換でも許容するなら
func dataUsingEncoding(_ encoding: UInt,allowLossyConversion lossy: Bool) -> NSData?
あいまいについては次の項で確認する。

コンバート可能かどうか調べるなら
func canBeConvertedToEncoding(_ encoding: UInt) -> Bool



特殊な漢字への対応

第一水準から第四水準という日本での漢字の分類がある。数字が大きくなるほうに仕様頻度の低いものが割り当てられている。ある時期のObjective-Cでは標準インスタンスとShiftJISなどの別エンコーディングデータとの変換は第二水準まで行えた。Swift2.0の時点ではどうなっているか。といってもSwiftのスタンダードライブラリにはそのメソッドがないのでNSStringのメソッドを使う。NSStringのメソッドを使う時点で期待が薄いがやってみる。

まずは少し上で見た
func canBeConvertedToEncoding(_ encoding: UInt) -> Bool
をplayground上で実行

結果
"亜".canBeConvertedToEncoding(NSShiftJISStringEncoding)//第一水準 結果はtrue
"弌".canBeConvertedToEncoding(NSShiftJISStringEncoding)//第二水準 結果はtrue
"俱".canBeConvertedToEncoding(NSShiftJISStringEncoding)//第三水準 結果はfalse
"丂".canBeConvertedToEncoding(NSShiftJISStringEncoding)//第四水準 結果はfalse

"亜".canBeConvertedToEncoding(NSJapaneseEUCStringEncoding)//第一水準 結果はtrue
"弌".canBeConvertedToEncoding(NSJapaneseEUCStringEncoding)//第二水準 結果はtrue
"俱".canBeConvertedToEncoding(NSJapaneseEUCStringEncoding)//第三水準 結果はfalse
"丂".canBeConvertedToEncoding(NSJapaneseEUCStringEncoding)//第四水準 結果はfalse

"亜".canBeConvertedToEncoding(NSISO2022JPStringEncoding)//第一水準 結果はtrue
"弌".canBeConvertedToEncoding(NSISO2022JPStringEncoding)//第二水準 結果はtrue
"俱".canBeConvertedToEncoding(NSISO2022JPStringEncoding)//第三水準 結果はfalse
"丂".canBeConvertedToEncoding(NSISO2022JPStringEncoding)//第四水準 結果はfalse
とやはり、第三第四水準は変換が出来ない。


実際にコンバートしてみると
結果
"亜".dataUsingEncoding(NSShiftJISStringEncoding)//第一水準 結果は 889f
"弌".dataUsingEncoding(NSShiftJISStringEncoding)//第二水準 結果は 989f
"俱".dataUsingEncoding(NSShiftJISStringEncoding)//第三水準 結果はnil
"丂".dataUsingEncoding(NSShiftJISStringEncoding)//第四水準 結果はnil

"亜".dataUsingEncoding(NSJapaneseEUCStringEncoding)//第一水準 結果は b0a1
"弌".dataUsingEncoding(NSJapaneseEUCStringEncoding)//第二水準 結果は d0a1
"俱".dataUsingEncoding(NSJapaneseEUCStringEncoding)//第三水準 結果はnil
"丂".dataUsingEncoding(NSJapaneseEUCStringEncoding)//第四水準 結果はnil

"亜".dataUsingEncoding(NSISO2022JPStringEncoding)//第一水準 結果は 1b244230 211b2842=""
"弌".dataUsingEncoding(NSISO2022JPStringEncoding)//第二水準 結果は 1b244250 211b2842=""
"俱".dataUsingEncoding(NSISO2022JPStringEncoding)//第三水準 結果はnil
"丂".dataUsingEncoding(NSISO2022JPStringEncoding)//第四水準 結果はnil
変換出来ないのはnilが返ってくる。

で、気になるあいまいを許す変換メソッドを実行してみると
結果
"亜".dataUsingEncoding(NSShiftJISStringEncoding, allowLossyConversion: true)//第一水準 結果は 889f
"弌".dataUsingEncoding(NSShiftJISStringEncoding, allowLossyConversion: true)//第二水準 結果は 989f
"俱".dataUsingEncoding(NSShiftJISStringEncoding, allowLossyConversion: true)//第三水準 結果は 3f
"丂".dataUsingEncoding(NSShiftJISStringEncoding, allowLossyConversion: true)//第四水準 結果は 3f

"亜".dataUsingEncoding(NSJapaneseEUCStringEncoding, allowLossyConversion: true)//第一水準 結果は b0a1
"弌".dataUsingEncoding(NSJapaneseEUCStringEncoding, allowLossyConversion: true)//第二水準 結果は d0a1
"俱".dataUsingEncoding(NSJapaneseEUCStringEncoding, allowLossyConversion: true)//第三水準 結果は 3f
"丂".dataUsingEncoding(NSJapaneseEUCStringEncoding, allowLossyConversion: true)//第四水準 結果は 3f

"亜".dataUsingEncoding(NSISO2022JPStringEncoding, allowLossyConversion: true)//第一水準 結果は 1b244230 211b2842=""
"弌".dataUsingEncoding(NSISO2022JPStringEncoding, allowLossyConversion: true)//第二水準 結果は 1b244250 211b2842=""
"俱".dataUsingEncoding(NSISO2022JPStringEncoding, allowLossyConversion: true)//第三水準 結果は 3f
"丂".dataUsingEncoding(NSISO2022JPStringEncoding, allowLossyConversion: true)//第四水準 結果は 3f
となった。変換出来ないのは3fというデータが返って来ている。これは"?"という文字の番号。つまり変換出来ない文字には"?"が当てはめられている。

Unicodeで定義されていてもShiftJISとの変換処理のところが対応されていないと変換出来ない模様。
ただ、第三水準からランダムに取り出して試してみたときにいくつかは変換可能の結果が返ってきた。第三水準でも多少は変換できるものがあるらしい。

ちなみにUTF系に対しては普通に変換が可能
"亜".canBeConvertedToEncoding(NSUTF32StringEncoding)//第一水準 結果はtrue
"弌".canBeConvertedToEncoding(NSUTF32StringEncoding)//第二水準 結果はtrue
"俱".canBeConvertedToEncoding(NSUTF32StringEncoding)//第三水準 結果はtrue
"丂".canBeConvertedToEncoding(NSUTF32StringEncoding)//第四水準 結果はtrue

つまり
・Unicodeエンコーディングを使っているうちは第4水準まで扱いがOK
・JIS、シフトJIS、EUCのエンコードに変換が可能なのは第2水準まで(おそらくそれらのエンコードからの変換も第2水準まで、未確認)
となりました。Swift2.0の時点ではこのようですが今後変更になる可能性はあります。



異体字

葛飾区の「葛」のように一つの漢字にいろいろな細かいバリエーションがあるものがある。この違いを扱うには異体字セレクタを用いる。データ上はベースとなる番号に異体字セレクタを続ける形になる。Swiftはこれをどう処理するか。
まず、
・ノーマル
・異体字セレクタU+E0100付き
・異体字セレクタU+E0101付き
の3つでインスタンスを作りそれぞれのいろいろなフォーマットのcountプロパティを見てみる
var itaiji: String = "\u{845B}"
var itaiji0: String = "\u{845B}\u{E0100}"
var itaiji1: String = "\u{845B}\u{E0101}"
itaiji.characters.count//1
itaiji.utf8.count//3
itaiji.utf16.count//1
itaiji0.characters.count//1
itaiji0.utf8.count//7
itaiji0.utf16.count//3
itaiji1.characters.count//1
itaiji1.utf8.count//7
itaiji1.utf16.count//3
charactersはどれも1、utf8とutf16はノーマルと異体字セレクタ付きで値が変わる。UTF16で異体字セレクタは2つ分になるので上の3というのは予想通り。
異体字セレクタが付いていてもcharactersの文字カウントは"ひともじ"で行うことが確認出来た。
(フォントも含めて対応している必要があるが)playgroundでの表示でも違いが確認出来た。この字のノーマルとU+E0101付きは一緒とのこと。
"ひともじ"として扱えるならCharacterインスタンスも作れる
var itaijiChar: Character = "\u{845B}"
var itaijiChar0: Character = "\u{845B}\u{E0100}"
var itaijiChar1: Character = "\u{845B}\u{E0101}"
エラー出ず。

つまり
異体字セレクタ(異体字情報データ)は異体字セレクタとして扱っている