読者です 読者をやめる 読者になる 読者になる

騒音のない世界 BLOG

コンピュータの話と、音楽の話

Null安全がすごい

先日社内で事業部のエンジニア全体向けに発表する機会があり、テーマが割と何でもありだったのでNull安全について話しました。発表の中で話せなかったことなども踏まえてブログにもまとめてみようと思います。

まずこのテーマを選んだ理由ですが、背景として

  • 自分がちょうどSwiftでiOSアプリを開発しており、Null安全な言語を利用していた
  • 事業部でNull安全な言語を使っているプロジェクトが他になかった
  • 比較的近年になって広がりを見せている新しい概念である
  • 自分がSwiftを書き始めて一番感動した部分だった
  • 将来的に言語選定の際の判断軸になるなど、具体的なアクションにつながりそう

といったところがあり、共有する価値があるのではと考えました。そういうわけなのでできるだけ特定の言語に依存せず、まったく知らない人にその概念を理解してもらい、良さを伝えるということを念頭に置いています。

Null安全とは

Null安全とは、一言で言うと「Nullチェックをコンパイラが強制する仕組み」のことです。Nullチェックをしないとコンパイルが通らない状態にすることで、実行時にNullへの不正なアクセスを防ぐ、ということをゴールとしています。

概念の説明としてはこれだけなのですが、Null安全が導入された背景を理解するため、NullとNull安全を取り巻く歴史を紐解きつつ、さらに深掘りしていこうと思います。

Nullの発祥

Nullという概念は1965年にTony HoareがALGOL Wという言語に導入したのが始まりとされています。その後様々な言語でNullが導入されていますが、2009年のカンファレンス(QCon)でTony Hoare自身が「Nullは10億ドルに相当する誤りだった」という旨の発言をしています。さらに、当時からコンパイラが参照の安全性をチェックするという発想はありながらも、実装の簡易さから単にNullを導入するという選択をしてしまい、多くのクラッシュにつながってしまった、とも話しています。

気をつけてNullチェックをする、みたいなことは当たり前のようになってしまいがちですが、人間が気をつけないとバグにつながるようなことはできるだけ機械に任せたほうが良いでしょう。考案者本人が認めているように、Nullチェックも本来はもっと仕組みで自動化されるべき存在だということではないでしょうか。コンパイラによって自動化するというのはその1つのアプローチといえます。

Null安全の歴史

Null安全という言葉は2005年のカンファレンス(ECOOP)でBertrand Meyerによって「Void Safety」という名前で紹介されたのが最初のようです。その後本人が開発するプログラミング言語Eiffelに導入されています。実装レベルではそれ以前にも似たような仕組みを実験的に導入している言語はいくつかあったみたいです。

それなりにメジャーな言語に普及していったのはここ数年のことで、だいたい2010年ごろからです。2010年に登場したRustをはじめ、Kotlin(2011~), Swift(2014~), TypeScript 2.0(2016~)など様々な言語に導入されました。

コンピュータやプログラミング言語の歴史から考えれば比較的近年になって広まってきた考え方であるといえるでしょう。

Null安全の仕組み

もう少し具体的な仕組みに踏み込んで見たいと思います。Null安全は実際にどういった形で実装されているのでしょうか。いろいろなバリエーションはあるかと思いますが、個人的に一番慣れ親しんでいるSwiftを例に取りつつ説明していきます。

Null安全はその性質上、言語機能として実装されることが多いです。やや雑な説明かもしれませんが、以下の2つの条件が満たせればNull安全な言語であるといえるでしょう。

  1. Optional型が存在すること
  2. Optionalでない型にはNullを代入できないこと

Optionalという言い方は言語によって異なるかと思いますが、わりとよく使われている用語のようです。概念としては「値もしくはNullを代入できるが、その中身には直接アクセスできない」ような型だと考えていただければよいかと思います。Optional型は明示的にNullチェックをすることによって中身の値にアクセスできるようになります。この型を使っていれば、Nullへの不正なアクセスは起こり得ません。

また、Optionalでない型にNullを代入できてしまうと結局そちらでNullへの不正なアクセスが起きうるため、2つめの条件も保証されている必要があります。

コード例を見る

Swiftでのコード例を見てみましょう。Swift未経験の方でもわかるようにコメントを含めていますのでなんとなく理解してもらえればと思います。まずOptional型です。

var opt: String? // StringのOptional型を定義する
opt = nil // Optionalなのでnilを代入できる
opt.lowercased() // Error! NullチェックをしていないのでStringのメソッドは呼べない

if let non_opt = opt { // Nullチェック
  non_opt.lowercased() // NullチェックをしたのでStringのメソッドを呼べる
}

Swiftの場合、型名の後ろに?を付けることでOptionalにすることができます。また、nilがいわゆるNullです。Optionalにはnilを代入でき、チェックなしには値にアクセスしたりメソッドを読んだりすることはできません。

次に、Optionalでない型です。

var non_opt: String = "TEST"
non_opt = nil // Error! Optionalでないのでnilは代入できない
non_opt.lowercased() // Stringのメソッドを呼べる

Optionalでない型にはnilは代入できません。つまりnilにはなりえないのでチェックなしにメソッドを呼ぶことができます。Non-null typeと呼ぶこともあります。

Javaなど従来の多くの言語では参照型のみNull代入可能であることが多いですが、Swiftでは値型か参照型か、ということとNull代入できるかどうかということは関係ありません。IntやDoubleといった値型でもOptionalならNull代入できますし、クラスなどの参照型でもOptionalでなければNullは代入できません。

これによって、型が値型か参照型かは意識せず、「存在しない」という状態をNullで表せるようになります。

Null安全のメリット

Null安全についてイメージが深まってきたでしょうか。次にNull安全のメリットについて見ていこうと思います。メリットとしては以下の様なものがあると思います。

うっかりNullチェックを忘れてしまうことがなくなる

コンパイラがNullチェックを強制するためです。Nullチェックをするか否かの判断軸も明確になるため、全体的にNullについての悩みが減ります。

生産性が高まる

開発のイテレーションにおいてNullチェック忘れというありがちなミスを早い段階で見つけられるようになるため、実装, コンパイル, 実行, デバッグ, というイテレーションを早めることができ、生産性が高まることが期待できます。

関数のインタフェースが表現豊かになる

これは直感的には理解しづらいかもしれませんが、例えば関数が失敗した場合の簡単なエラーの返し方としてOptional型を使うことができます。

エラーの表現として-1や空文字を返すようなパターンは古くから存在しますが、-1や空文字を失敗として扱うと理解しづらくなりますし、失敗しうるのかどうかがドキュメントを読まない限りわかりません。Nullを返すパターンは多少ましですが、結局失敗しうるのかどうかは関数のシグニチャからはわかりません。エラーをthrowする書き方は良いのですが、「書くコスト」は無視できません。チームで開発する場合、メンバーのスキル感によってはコストの高い書き方に対してリターンを理解してもらうための学習コストもかかります。それらを加味して、Optionalを返すというエラーの返し方は手軽でありながらエラーを返す可能性がある関数なのかどうかがわかりやすいため可読性も高いのが良いと感じています。

また、引数がOptionalであれば文字通りオプショナルな引数と判断することもできます。このように、関数のインタフェースが表現豊かになるのは大きなメリットといえます。

Null安全の悩ましい点

メリットの多いNull安全ですが、悩ましい点もあります。

抜け道の存在

現実的に、Optional型でも処理的にNullにならないケースもあります。そのような場合に冗長性を廃するため、Null安全な言語でありながらNullチェックを回避する仕組みが多くの言語に存在しています。

例えばSwiftにはForced Unwrappingと呼ばれる仕組みがあります。これは正しく使えば冗長なチェックを減らすことができるのですが、間違って使ってしまうと結局実行時にNullにアクセスしてしまい、クラッシュにつながってしまいます。Forced Unwrappingするには、変数の後ろに!を付けます。

var opt: String? // StringのOptional型
opt = nil // Optional型なのでnil代入できる
var non_op = opt! // Forced Unwrapping。中身はnilなので実行時にクラッシュする。

正しく使えれば便利なこともあるのですが、これによって結局Nullによるクラッシュを100%なくすには至っていません。これはNull安全の仕組みを採用しないほどのデメリットとは考えていませんが、もう少し良い仕組みにもできそうな気がしています。

Null安全を導入する悩ましさ

これは使う上での話というよりはそもそも導入する上での障壁の話です。

Null安全な言語であることの条件の1つとして「Optionalでない型にはNullを代入できないこと」というものを上げました。これは、例えばJavaにOptionalを導入しても、通常のNull代入可能な参照型があることによってNull安全の恩恵を受けにくくなってしまうということです。

このような背景があるため、既存のNull安全でない言語にNull安全を導入するのは難しいところがあります。Null安全なしくみを利用したければ、そのようなしくみのある言語を採用する必要があるでしょう。そのためプロジェクトの初期に決定する必要があり、すでに動いているプロジェクトに導入するのは難しいと言えます。

ちなみに、AltJSの1つであるTypeScriptはそのへんをうまくやっていて、バージョン2.0からデフォルトでは後方互換性を保ちつつ、フラグをたてることで後方互換性を捨てる代わりにNull安全の機能を導入できるようになっているみたいです。とはいえ、後方互換性をもたせることの重要性が高い言語ではそのようなアプローチも難しいのかもしれないと思っています。

Non-Null Typeについて

Null安全について説明するとOptional型に注目してしまいがちなのですが、本当に強力なのはそれと対をなすOptionalでない型、つまりNon-Nullな型の存在です。

Optionalな型が生まれたときに素早くチェックし、登場する変数をOptionalでない型のみにできれば、そこはNullが存在しない世界、ロジックに集中できる世界です。Nullにまつわる煩わしい問題をプログラムの端に追いやって、ロジックの本質的な領域を増やすことができれば見通しの良いプログラムを組むことにつながるでしょう。

おわりに

近年のプログラミング言語における新しい概念としてNull安全について解説しました。

プログラミング言語は人間に優しくなるように進化しています。構造化プログラミング、オブジェクト指向、型安全など、どれも可能なこと自体は大きく変わっていませんが、人間にとって読みやすく、書きやすく、管理しやすくするためのアイディアです。Null安全もその一つと言えるのではないでしょうか。

Null安全は、Nullがもたらす煩わしさを低減し、コアなロジックに集中できるようにする強力なツールであると感じています。正しく活用できれば多くのメリットが得られるでしょう。

Swift界隈では色々と議論され、「良い」という認識が広まっているテーマな割に、コミュニティによってはまだ全然浸透していないと感じています。Null安全という考え方に興味を持ってもらうきっかけになれば幸いです。

参考リンク

新曲「裸の王様」出しました

ちょっと出してから時間立っちゃいましたが、新曲出しました。

ドラムもギターもノリノリの曲です。ぜひどうぞ。

過去の曲もぜひ。

1~2ヶ月に1曲ぐらいのペースで上げてます。 更新情報など気になったら各種SNSのフォローお願いします。

新曲「終わりのない物語」出しました

クリーントーンのギターが淡々とつぶやくようにシンプルなメロディを弾きながら広い世界を旅していくような感じで、素朴で暖かい曲になったと思います。

ぜひ聴いてください。

過去の曲もぜひ。

1~2ヶ月に1曲ぐらいのペースで上げてます。 更新情報など気になったら各種SNSのフォローお願いします。

ポリモーフィズムをもっと理解する

ポリモーフィズムオブジェクト指向の3本柱として「継承」「カプセル化」と共に語られることが多いですが、サブクラス化してオーバーライドして…というのはポリモーフィズムの一面にすぎません。この記事ではポリモーフィズムとは何なのかを説明すると共に3種類のポリモーフィズムを取り上げ、「ポリモーフィズム」という言葉を様々な角度から見ていきたいと思います。対象読者としては全くの初学者というよりも何となくポリモーフィズムは知ってて使ったりもするけどちゃんと説明できないとか、そういう人を考えています。

ポリモーフィズムを一言で説明する

ポリモーフィズムとは、一言で説明するとどういう概念なのでしょうか。日本語訳としては、オブジェクト指向の文脈だと「多態性」、関数型の文脈だと「多相性」が多い印象ですが、どちらもピンとくる言葉ではありません。そこでまずは言葉を分解して読み解いてみたいと思います。

ポリモーフィズム(Polymorphism)という言葉はポリ(Poly)とモーフィズム(Morphism)にわけられます。ポリは「複数の」、モーフィズムは「形状」と置き換えられそうです。つまりポリモーフィズムとは「複数の形状」と読み取れます。

まだ抽象的で掴みどころがないですね。実際ポリモーフィズムという言葉自体は結構抽象的で、後ほどご紹介しますが生物学などでも用いられる幅広い言葉のようです。そこで、もう少しプログラミング的な意味に注目していこうかと思います。Wikipediaの冒頭の説明がとてもわかりやすいです。

polymorphism is the provision of a single interface to entities of different types. Polymorphism (computer science) - Wikipedia

短くすると「Single interface for different types」とでも言うのでしょうか。つまりプログラミングにおけるポリモーフィズムとは、複数の異なる型に対して共通のインタフェースを提供することです。

3種類のポリモーフィズム

ポリモーフィズムには大きく分けて3種類あります。それぞれ見ていきます。ポリモーフィックな部分だけサンプルを軽く書いておきますので雰囲気だけ感じて頂ければと思います。

Ad hoc polymorphism

いくつかの限定された型に対して共通のインタフェースを提供するポリモーフィズムです。実装上は関数のオーバーロードによって実現されます。同じ名前の関数に対して、引数や戻り値の型を変更したものを用意します。

関数オーバーロードポリモーフィズムであるというのは少し意外に感じられる方もおられるのではないでしょうか。しかしこれも確かに「複数の異なる型に対して共通のインタフェースを提供」しています。

// DogとCatに対してtouchという共通インタフェースを提供する
class Breeder {
    func touch(_ dog: Dog) {
        dog.say()
    }
    func touch(_ cat: Cat) {
        cat.say()
    }
}

let breeder = Breeder()
breeder.touch(Dog()) // bowwow
breeder.touch(Cat()) // meow

Parametric polymorphism

様々な型に適用できるようなインタフェースを提供するポリモーフィズムです。実装上はいわゆるGenericsやTemplateと呼ばれる言語機能により実現されます。型を引数として渡すことで処理を様々な型に適用することを可能にします。

関数型言語において単にポリモーフィズムと言う場合、こちらを指します。

// Animalに適合するあらゆる型に対してtouchという共通インタフェースを提供する
func touch<A: Animal>(_ animal: A) {
    animal.say()
}

touch(Dog()) // bowwow
touch(Cat()) // meow

Subtyping

実行時にサブクラスのポインタをベースクラスのポインタとして扱うことにより、ベースクラスの共通なインタフェースから各サブクラスの異なる実装を呼べるようにするポリモーフィズムです。

オブジェクト指向言語において単にポリモーフィズムという場合、こちらを指します。

// AnimalとAnimalのサブクラスにtouchという共通インタフェースを提供する
func touch(_ animal: Animal) {
    animal.say()
}

touch(Dog()) // bowwow
touch(Cat()) // meow

なぜポリモーフィズムか?

なぜそのような言語機能があると嬉しいのか?という話ですが、これは僕の個人的な考えを書かせていただきます。

ポリモーフィズムは「複数の異なる型に対して共通のインタフェースを提供すること」であるというのは前述しましたが、その本質は「共通のインタフェースを用意する」という部分であると考えています。

「共通のインタフェースを用意する」という考え方は身の回りの道具のデザインにもあふれています。PCを見ればUSBやイヤホンジャックなど、共通化されたインタフェースばかりです。これがもたらすメリットは、インタフェースに適合している機器であれば何でも取り付けられるということです。言い換えれば、共通のインタフェースは柔軟な拡張性をもたらすものであり、それが良いインタフェースデザインであれば「疎結合」も促進します。

また、共通のインタフェースにはヒューマンインタフェース上のメリットもあります。ボタンやスイッチなどの使い方が共通であれば使い方を新たに覚える必要もありませんし、ミスの割合が減ることも期待できます。

プログラミング言語も人間が使う道具なので、同様のメリットがあると考えられます。つまり柔軟な拡張性と、ミスの低減です。新しい機能を追加するときに現状のコードにほとんど変更を加えることなく自然な形で追加できたり(柔軟な拡張性)、ifやswitchで分岐することなく共通インタフェースを呼び出すようにすることで、新たな機能を追加したときに書き加える箇所を減らすことができたり、連結や分解、探索、ソートなど汎用的な処理に対して呼び出すべき関数がすぐにわかったり(ミスの低減)します。

まとめると、なぜポリモーフィズムが嬉しいのか?に対する答えは「良いインタフェースを設計するのに便利だから」です。これが僕が考えるポリモーフィズムのメリットです。

その他の分野でのポリモーフィズム

ポリモーフィズムという言葉をより深く理解するため、他分野においてどのように使われているかもさらっと見てみましたのでご参考までにどうぞ。疎い分野なので間違いなどあったらすいません。

生物学

同じ種の生物の中で形や構造などが異なることをポリモーフィズムと言います。人間であれば男女の性差、目の色や肌の色、血液型などを指し、そのうち遺伝子に基づくものを遺伝的多型(Genetic polymorphism)といいます。

物質科学

同じ化学組成だが複数の異なる結晶形をとることをポリモーフィズムと言います。温度や圧力など、結晶が生成するときの条件によって結晶系が決まるようです。例えば、グラファイトとダイヤモンドはどちらもC(炭素)からなります。

おわりに

いかがでしたでしょうか。ポリモーフィズムについて様々な角度から見ていきつつ、その概念やメリットについて考察してみました。何か新たな発見があれば幸いです。

参考

結局ほとんど英語のWikipediaを参考にしました。

UIPasteboardをもっと理解する (後編)

「UIPasteboardをもっと理解する」の後編です。前編はこちらになります。

UIPasteboardをもっと理解する (前編) - 騒音のない世界 BLOG

後編は具体的な動作例とハマりがちな注意点について見ていきます。

具体的な動作を見る

ペーストボードの中身をログに吐いて、具体的な動作を見ていきます。環境はシミュレータ(10.2)です。ログに吐くコードはこんな感じです。

func printPb(_ pb: UIPasteboard) {
    for item in pb.items {
        for (key, value) in item {
            print("\(key): \(value)")
        }
    }
}

UITextViewやUITextFieldからテキストをコピー

画面を長押しして出るメニューからCopyを選んでHogeという文字をコピーします。

f:id:a_beco:20170124041619p:plain

public.utf8-plain-text: Hoge
com.apple.flat-rtfd: <72746664 00000000 ...>
Apple Web Archive pasteboard type: <3c21444f 43545950 ...>

複数の値が入りました。1つずつ見ていきます。

最初のpublic.utf8-plain-textutf-8エンコーディングのプレーンテキストのようです。エンコーディングごとにUTIが違うことがわかります。2つめのrtfdはRich Text Format Directoryというやつで、画像なども含められるAppleのリッチテキストフォーマットです。3つめのWeb ArchiveというのはSafariでつかうフォーマットで、htmlや画像、音などリソースも含めたウェブページを保存するフォーマットです。

このように、標準のUITextViewなどからコピーすると複数のフォーマットでコピーされることがあります。

写真アプリでコピー

標準の写真アプリで写真をコピーしてみます。

com.apple.mobileslideshow.asset.localidentifier: <31303645 39394131 ...>
public.jpeg: <UIImage: 0x608000092390>, {4288, 2848}

こちらも複数の値が入りました。

最初のものはよくわかりませんが、com.apple.mobileslideshowというのが写真アプリのバンドルIDなので、おそらく写真アプリの内部でのみ用いるデータフォーマットかと思われます。2つめは単にjpegフォーマットです。

また、テキスト、写真ともにUIからコピーするとそれまで保持していた値はクリアされるようです。

コードでコピー

コードから画像と文字列をコピーしてみます。

print("===")
UIPasteboard.general.image = UIImage(named: "sample")
printPb(UIPasteboard.general)
print("===")
UIPasteboard.general.string = "Hoge"
printPb(UIPasteboard.general)
print("===")

結果はこうなりました。

===
public.jpeg: <UIImage: 0x6080002809b0>, {512, 512}
com.apple.uikit.image: <UIImage: 0x60800009fd10>, {170.66666666666666, 170.66666666666666}
public.png: <UIImage: 0x60000009f630>, {512, 512}
===
public.utf8-plain-text: Hoge
===

imageプロパティに画像をセットするとjpeg, pngに加えてcom.apple.uikit.imageというものがコピーされました。元データのフォーマットによらず複数のフォーマットでコピーされます。ドキュメントを見てもpngとjpgで保存される旨が書いてあります。

テキストは単にセットするとUTF8のプレーンテキストになりました。

[2017.03.30 追記] iOS8系, 9系だとimageプロパティに画像をセットした時にcom.apple.uikit.imageしかコピーされません。この場合iOS8系のメモ帳アプリに貼り付けられませんでした。他にも貼り付けられないアプリがあるかもしれないので、直接itemsプロパティに代入したほうが良いかもしれません。

ペースト先の処理を考える

コピーしたデータをUI操作によりペーストできるかどうかは、ペースト先のアプリが対応しているUTIにより決まります。ペースト先のアプリはペースト操作が実行されるとペーストボードから対応UTIのデータ取得を試み、あればそのデータを取得してUIに反映します。

ドキュメントにも注意点として書かれていますが、自分でコピー処理を実装する場合、ペースト先のことを考えてさまざまなフォーマットでコピーしておくと親切です。例えば、画像をコピーするためにpublic.pngにのみ保存していると、public.jpegにしか対応していないアプリにはペーストできないことになり、ユーザが混乱してしまう可能性があるので注意が必要です。UITextViewや写真アプリでコピーした際に複数のフォーマットでコピーされるのもそのような理由があるものと思われます。

間違えがちなポイント

特定フォーマットの存在チェック

ドキュメントによると、stringプロパティやimageプロパティのnilチェックによってペーストボード内にデータがあるかどうかをチェックしてはいけません。代わりにiOS10から追加されたhasStringsなどを使うと良いようです。これはリソースを無駄に消費しないようにするためのようです。

注意点として、hasStringsstringの有無ではなくstringsの有無と対応する、ということがあります。stringは1つめのPasteboard Itemからしか文字列を返さないのに対し、stringsはペーストボード内のすべてのPasteboard Itemからすべての文字列を返します。なので2つめ以降のPasteboard Itemに文字列が入っている場合、hasStringsがtrueでもstringnilになることがあります。下記のコードで再現します。

let pb = UIPasteboard.general
        
pb.image = UIImage(named: "sampleImage") // 1つめのPasteboard Itemは画像を入れる
pb.addItems([["public.utf8-plain-text" : "hoge"]]) // 2つめのPasteboard Itemは文字列を入れる
        
print("hasStrings: \(pb.hasStrings)")
print("string: \(pb.string)")

結果

hasStrings: true
string: nil

stringの有無を調べたければcontains(pasteboardTypes: [String])を使うと良いです。こちらは先頭のPasteboard Itemしかチェックしません。文字列のUTIはUIPasteboardTypeListStringという配列から取得できるので、contains(pasteboardTypes: UIPasteboardTypeListString as! [String])のように書けます。先程の例に適用すると、この戻り値はfalseとなります。

ペーストボードからデータ削除

UIPasteboard deleteなどで検索すると空文字をセットするコードがたくさん出てきますが、これはあまりよくないかと思います。細かい話ではありますが、空文字は文字列としてペーストボードに保存されてしまうため、UITextViewで長押しした際のメニューで無駄にPasteが出てしまいます。

f:id:a_beco:20170124035844p:plain

データ削除はitemsプロパティを直接操作することで可能です。全て削除する場合は単にUIPasteboard.general.items.removeAll()とすれば良いです。先程のメニューはこうなります。

f:id:a_beco:20170124040000p:plain

おわりに

2記事に渡ってUIPasteboardについてまとめてみましたが、いかがでしたでしょうか。何か新しい発見があれば幸いです。

UIPasteboardをもっと理解する (前編)

UIPasteboardといえばstringプロパティやimageプロパティでコピーペーストできるやつ、ぐらいに思ってませんか。この記事ではその概念と注意点をしっかり押さえつつ、具体的な動作例を見ていくことでUIPasteboardの理解を深めていきます。

(ちょっと長すぎたので概念と具体例で前後編に分けます。今回は前編になります。)

UIPasteboardとは

公式docの冒頭を要約すると、UIPasteboardは「データを自分のアプリ内のある場所から他のあらゆるアプリに共有するもの」のようです。典型的な使い方はデータコピーなどの際にUIPasteboardを通してデータを保存しておいて、アプリ内や別なアプリから再びUIPasteboardを通してデータを読み出して使う、といった感じです。単純なテキストや画像のコピーだけではなく、バイナリや独自データを他のアプリに転送するような用途でも使用できます。

UIPasteboardの種類

大きく分けて2種類のペーストボードがあります。

The General Pasteboard

どのアプリからも自由にアクセスできる、システムワイドなペーストボードです。UIPasteboard.generalでシングルトンとしてアクセスできます。メモ帳アプリなどで普通にコピーするとこのペーストボードに保存されます。逆に言うと、General Pasteboardをプログラムから操作することによって現在コピーしているものが書き換わってしまい、ユーザーの混乱を招く恐れもあるので注意が必要です。

iOS10からはHandoffの新機能として、複数端末間でGeneral Pasteboardに保存したデータを共有できるようになりました。こちらは共有不可にする設定も可能です。

Named Pasteboard

アプリ内で完結するようなものや、Team ID が共通なアプリ間でのデータの受け渡しにはNamed Pasteboardが利用できます。これは、一意な名前をつけて生成され、システムを汚さず独占的に使用できるペーストボードです。

何を保存するのか

UIPasteboardは何を保存するのでしょうか。UIPasteboardのヘッダを見るとこういう部分があります。

// Direct access

open var items: [[String : Any]]

Direct accessという言葉が示すように、このitemsプロパティでアクセスできるitemが、ひとつのペーストボードに保存されるデータそのものです。これはPasteboard Itemと呼ばれるもので、ひとつのPasteboard Item[String : Any]というDictionaryとなっています。var宣言されているため追加、削除、挿入など何でもできます。そして、これ以外のメソッドやプロパティのほとんどはこのデータに便利にアクセスするものです。

つまり、ひとつのペーストボードが保存しているのは「何でも保存できる辞書」の配列ということです。これが意味するのは、ペーストボードが保持する値は1つではないということです。普段単純にコピーペーストなどしていると新しいものをコピーすると古いものは消えるのが当然のような感じがしますが、ペーストボード自体はプログラム的には(リソースが許す限り)いくつでも値を保持できます。

Pasteboard Type

Pasteboard ItemのDictionaryにおけるキーとなる値がPasteboard Typeです。この実体は単なるStringであり、意図せず重複してしまうことがなければどんな文字列でも指定することができますが、UTI (Uniform Type Identifier)の使用が推奨されています。

UTI (Uniform Type Identifier)

UTIはAppleが制定しているデータフォーマットを識別する識別子で、reverse-DNSで書きます。UTIについてはこちらのドキュメントが詳しいです。 Introduction to Uniform Type Identifiers Overview

例えば、com.apple.quicktime-moviecom.apple.pict public.jpegのように書きます。

また、UTIは継承でき、is-a関係を持ちます。public.dataを継承してpublic.textpublic.imageという型が定義され、public.imageを継承してpublic.jpegpublic.pngが定義される、といった具合です。

同じような役割を果たすものとしては、古くはOSTypeという"JPEG"のように4文字でファイルタイプを識別するものがあったり、他にはファイル拡張子やMIME typeなどもそうですが、UTIはより柔軟で拡張性が高いとのことです。

ちなみに、特定のアプリ内でしか使わないデータフォーマットの場合は<app-bundle-id>.foo.barのように命名するようです。

まとめ

  • ペーストボードにはGeneralとNamedの2種類ある
  • 1つのペーストボードは「何でも保存できる辞書」の配列を持つ
    • 1つの辞書をPasteboard Itemという
    • itemsプロパティですべてのデータにアクセスできる
    • stringプロパティやimageプロパティなどは単にitemsの中のデータに便利にアクセスするためのもの
  • 辞書のキーはPasteboard Type
    • 標準化されたデータフォーマット識別子であるUTI形式で指定する

いかがでしたでしょうか。前編はここまでです。後編では具体的な動作例や注意点について見ていきます。

UIPasteboardをもっと理解する (後編) - 騒音のない世界 BLOG

SoundCloudの再生数といいね数を振り返る('10~'16)

2010年8月より、SoundCloudで少しずつ自作の楽曲をアップロードしてきました。 騒音のない世界のサイトYoutubeチャンネルもありますが、楽曲は全てSoundCloudに上げています。

beco | Free Listening on SoundCloud

2017年1月12日現在、51曲アップロードされており、総再生数はおかげさまで35万回を超えています。 いつも楽曲を聴いてくださっている皆様、コメントや楽曲のご利用報告をくださる皆様ありがとうございます。

今回はこれまでを振り返る意味で、再生数の推移といいね数の推移について書きたいと思います。

再生数の推移

f:id:a_beco:20170112010008p:plain

2016年の総再生回数は129,270回でした。 2010年はほとんど見えませんが61回でした。

いいね数の推移

f:id:a_beco:20170112010435p:plain

2016年の総いいね数は506でした。 2010年は0回, 2011年は2回、2012年は8回です。 いいね数は再生数と比べて2016年の伸びが顕著ですね。

楽曲ごとのいいね数

f:id:a_beco:20170112011227p:plain

楽曲ごとのいいね数です。 公開時期などもあるので必ずしも人気順ではありませんが、1位は「新生活」でした。

2位は初音ミク楽曲の「強欲な娘」です。

インスト曲とミク曲が両方並んでいてなんかうれしいです。

おわりに

最初はアップロードしても全然聴かれていませんでしたが、徐々にコメントもらえたり、いろいろなところで使ってもらえるようになってきてありがたい限りです。音楽的にやりたいこともだんだん具体化してきたかなーという感じがしています。

これからももっと良い曲を届けていきますので、応援していただけるととても嬉しいです。 気になった方はサイトのお気に入り登録、フォロー・チャンネル登録、よろしくお願いします。ぐっときたらいいね・リツイートなどもしていただけるとありがたいです!

動画制作されてる方はBGMとしてのご利用も自由ですのでぜひどうぞ。商用利用も可です。詳しくはサイトを御覧ください。

では、2017年もよろしくお願いします〜。