CoreDataを使う1。基本的なオブジェクト。
iphoneアプリでデータの永続化にCoreDataの使用について、ちょっと整理していきましょ。
(これまで使ってきたDB接続のインターフェイスとは考え方が違うのでちょっとわかりにくかったこともあるので。)
使用している開発環境は、「XCode 3.2.1」です。
アプリケーションの作成
プロジェクトのタイプとして「navigation-based Application」を選択、「Use Core Data for storage」をチェックしてプロジェクトを作成するとCore Dataを使うためのテンプレートソースなどを作成してくれます。
今回をそれを利用します。
基本的なオブジェクト
「Core Data」を使用するさいの基本となるオブジェクトを生成するコードが、アプリケーションのデレゲートクラスに作られます。
このクラスのヘッダーファイルは以下のようなものになりますが。
@interface NoteAppDelegate : NSObject <UIApplicationDelegate> { NSManagedObjectModel *managedObjectModel; NSManagedObjectContext *managedObjectContext; NSPersistentStoreCoordinator *persistentStoreCoordinator; UIWindow *window; UINavigationController *navigationController; } @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel; @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator; @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet UINavigationController *navigationController; - (NSString *)applicationDocumentsDirectory; @end
「Core Data」に関係あるのは、以下の属性です。
NSManagedObjectModel *managedObjectModel; NSManagedObjectContext *managedObjectContext; NSPersistentStoreCoordinator *persistentStoreCoordinator;
NSManagedObjectModel
これは、各EntityやEntity同士の関連を保持、提供するためのモデルオブジェクトです。今回のプロジェクトのテンプレートでは、この定義を「xdatamodel」という拡張子のモデルファイルから読み込むようになっています。
以下は、その自動生成された実装部分です。
/** Returns the managed object model for the application. If the model doesn't already exist, it is created by merging all of the models found in the application bundle. */ - (NSManagedObjectModel *)managedObjectModel { if (managedObjectModel != nil) { return managedObjectModel; } managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain]; return managedObjectModel; }
NSPersistentStoreCoordinator
これは、永続化オブジェクトです。上記のモデルと実際のStorage(SQLiteなどのStorage)を関連づけて永続化を行う機能を実現します。
多分XMLなど他の形式をStorageにすることも可能なのでしょうけど、今回のプロジェクトのテンプレートでは、SQLiteが使われるようになっています。多分これが、標準的でベターな方法なのでしょう。
以下が実装部分です。
「addPersistentStoreWithType:configuration:URL:options:error」メッセージにおいて データベースファイルとして、アプリケーションディレクトリのトップのデータベースファイルを具体的なStorage、タイプをNSSQLiteSoterTypeが指定されています。
/** Returns the persistent store coordinator for the application. If the coordinator doesn't already exist, it is created and the application's store added to it. */ - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (persistentStoreCoordinator != nil) { return persistentStoreCoordinator; } NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"Note.sqlite"]]; NSError *error = nil; persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return persistentStoreCoordinator; }
NSManagedObjectContext
データベースから取得したデータオブジェクトを管理したりするメモリ内データベースのようなもの。
以下、自動生成された実装部分です。上記の永続化オブジェクト(NSPersistentStoreCoordinator)との関連づけが行われます。
/** Returns the managed object context for the application. If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application. */ - (NSManagedObjectContext *) managedObjectContext { if (managedObjectContext != nil) { return managedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { managedObjectContext = [[NSManagedObjectContext alloc] init]; [managedObjectContext setPersistentStoreCoordinator: coordinator]; } return managedObjectContext; }
とりあえず、準備部分をみたところで、続きはまた今度。
Class::Stdを使用したインサイドアウトクラスの作り方の基本
Perlベストプラクティスを参照してClass::Stdを利用したインサイドアウトクラスを使うようにしてみたので、基本のところを整理しておきます。
インサイドアウトクラス、Class::Stdを使用する目的などですが..
インサイドアウトクラスを使うことによりインスタンス属性へのカプセル化をする。通常のブレスされたハッシュを使う方法だと、ハッシュでのアクセスと同様の方法で属性にアクセスできてしまうので、属性をレキシカルなハッシュで管理する。
インサイドアウトクラスを使うと属性ごとのハッシュでのインスタンスデータの管理が行われる。インスタンス破棄時にそのハッシュデータのクリーンが必要ななるが、個々にたっているとメンドーなことになりので、Class::Stdを使って、このインスタンスのクリーンを自動化する。
継承を行うようになると(特に多重継承)、イニシャライズ、クリーンアップの順番などがややこしくなるのでClass::Stdを使用して、各階層個別のイニシャライズ、クリーンアップが適切に呼び出されるよう自動化。
実装例
とりあえず、コード例としては、以下のような2つの属性をもつ簡単なものを。
use strict; use warnings; package Cart::Item; use Class::Std; { my %item_id :ATTR; my %quantity :ATTR; sub BUILD { my ($self, $ident, $args_ref) = @_; $item_id{$ident} = $args_ref->{item_id} ? $args_ref->{item_id} : ''; $quantity{$ident} = $args_ref->{quantity} ? $args_ref->{quantity} : ''; } sub DEMOLISH { my ($self) = @_; print($self->item_id . " demolished \n"); } sub item_id { my $self = shift; $item_id{ident $self} = shift if @_; return $item_id{ident $self}; } sub quantity { my $self = shift; $quantity{ident $self} = shift if @_; return $quantity{ident $self}; } sub to_string() { my $self = shift; "item_id=" . $self->item_id . ",quantity=" . $self->quantity } }
実装に関するメモなど
「use Class::Std」として、Class::Stdをimportする。これにより、new、DESTROYなどのインスタンス構築、破棄に関するメソッドなどのここで定義したパッケージへのエクスポートがされる。
クラスの定義本体はカプセル化されるよう、レキシカルスコープ内に入れる。
インスタンスの属性は、Blessされたハッシュを使うのではなく、個々にハッシュとしてして宣言。インスタンスのIDをキー、インスタンス値をするHashでのインスタンス属性値管理を行うことになる。そして、このハッシュには:ATTR属性マーカーをつける。この:ATTRをつけることにより、ここで宣言した属性ハッシュを一括して管理できるようになり、インスタンス破棄時に属性ハッシュからの破棄が自動的に行われるようになる。( 属性マーカ宣言されたときに起動される「MODIFY_HASH_ATTRIBUTE」ハンドラー - Class::Stdで定義 - によって)。
インスタンスのイニシャライザはBUILDという名称で定義する。Class::StdからエクスポートされるnewメソッドがこのBUILDが呼び出される仕組みとなっていて、継承を行ったときも適切な順番でこのBUILDイニシャライザが呼び出されるようになっている。また、引数はインスタンス、インスタンスのID,newに渡された引数(Hashへの参照として渡される)となる。
インスタンスの破棄は、DEMOLISHという名称で定義する。Class::Stdからデストラクタ(DESTROY)がエクスポートされる仕組みになっていて、このデストラクタからDEMOLISHが呼び出されるようになっている。インスタンス属性ハッシュからの破棄は自動的に行われるので、単なる破棄でない(ファイルのクローズなど)もののクリーンアップの処理をこのDEMOLISHで行うことになる。これらのクリーンアップの処理は継承を行ったときも適切な順番で行われるようになっている。
インスタンス属性値へのアクセスは、属性ハッシュにからインスタンスIDをキーにして行うことになる。インスタンスIDは、Class::Stdで定義されているidentメソッド(=Scalar::Util::refaddrメソッドへの参照)で行うことができる。
以下、確認例。コンストラクタへの引数は、ハッシュへの参照となる、
my $item1 = Cart::Item->new({item_id=>'A-1', quantity=>2}); print($item1->to_string . "\n"); $item1->quantity(4); my $item2 = Cart::Item->new({item_id=>'A-2', quantity=>3}); print($item1->to_string . "\n"); print($item2->to_string . "\n");
インスタンス値の参照、更新がうまくいっていること、クリーンアップメソッド(DEMOLISH)が実行されていることが確認できる。
attachment_fuプラグインの保存先を変更
前回、attchment_fuでとりあえず画像アップロードをしてみたが、
今回、保存先と画像を参照するurlを変更する実装を行っていみる。
アプリケーションとは別のディレクトリで画像を管理したいということと、
railsではなくapacheで画像のリクエストを処理させるということが目的。
対象としたrailsは2.0.2。
基本としては、画像をあつかうmodelでattachment_fuのファイルストレージ(file_system_backend.rb)で定義されているメソッドのオーバーライドすることと、
初期化スクリプトを使ってパスとurlの設定を行えるようにすることにより対応。
モデルの定義
今回、Photoというモデルを画像を扱うモデルとして作成。以下のような実装を行う。
Configというネストしたクラスを定義。
Configというネストしたクラスを定義。さらにモデル側にそのインスタンス参照するためのconfigというスタティック属性を定義。
ここに、ベースとなるurl,格納ディレクトリのベース、対応する画像が無いときに表示する画像の相対パスを定義。
config/initializer/photo.rbで各属性へ値を設定することを想定した定義である。
なお、Configをインスタンス化した後ででconfig/initializer/photo.rbをloadしているのは、development環境でrailsを起動している場合、
このモデル自体が、リクエスト毎に再読み込みされるので、この初期化スクリプトも毎回読み直してやる必要があるから。
Configされた属性を返すメソッドをモデルに定義
上記したConfigの属性を参照するメソッド(base_url,base_path, no_image_filename)を定義。属性のアクセスの記述を簡易にする程度の目的。
full_filenameをオーバーライド
attachment_fuのファイルストレージ(file_system_backend.rb)で定義されているfull_filenameをオーバーライド。
このメソッドは、ファイルを格納するファイル名をフルパスで返すもの。Configの属性で定義したベースパス下のパスになるよう定義。
public_filenameをオーバーライド
attachment_fuのファイルストレージ(file_system_backend.rb)で定義されているpublic_filenameをオーバーライド。
これは、画像を参照するurlを返すメソッド。Configの属性で定義したベース以下のurlになるよう定義。
# # 商品画像のモデル # attachment_fuプラグインを使用して、アップロード、画像表示に対応 # class Photo < ActiveRecord::Base # モデルのConfiguration情報を格納 # 以下の属性を含む # <tt>base_path</tt> - 画像を配置するベースパス # <tt>base_url</tt> - 画像を参照するurlのベース # <tt>no_image_filename</tt> - No image画像の相対パス class Config attr_accessor :base_path, :base_url, :no_image_filename def initialize puts "init" @base_url = "http://localhost" @base_path = '/var/www' @no_image_filename = File.join( "/images/thumbs", "noimg.gif") end end # モデルのconfiguratioのインスタンス @@config = nil def Photo.config if @@config.nil? then @@config = Photo::Config.new load(File.join(RAILS_ROOT, 'config','initializers', 'photo.rb' )) end @@config end # attachment_fuのオプション設定 has_attachment :content_type => :image, :storage => :file_system, :max_size => 1.megabyte, :thumbnails => { :thumb => '100x100>', :small => '50x50>' }, :path_prefix => "photos" # アップロード時のvalidationをすることを指定 validates_as_attachment # No image画像の相対パス def self.no_image_filename @@no_image_filename = Photo.config.no_image_filename end # 画像を配置するベースパス # Used as the base path that #public_filename strips off full_filename to create the public path def base_path() @base_path ||= Photo.config.base_path end # 画像を参照するurlのベース def base_url() @base_url ||= Photo.config.base_url end # Gets the public path to the file # The optional thumbnail argument will output the thumbnail's filename. def public_filename(thumbnail = nil) base_url + full_filename(thumbnail).gsub(%r(^#{Regexp.escape(base_path)}), '') end # Gets the full path to the filename in this format: # # <base_path>/<path_prefix>/<画像IDの5から8桁>/<画像IDの1から4桁>/<画像名>のようになる。 # # override from Technoweenie::AttachmentFu::Backend::FileSystemBackend def full_filename(thumbnail = nil) file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s File.join(base_path, file_system_path, *partitioned_path(thumbnail_name_for(thumbnail))) end end
初期化
Photo.config.base_path = "/Library/WebServer/Documents" Photo.config.base_url = "http://localhost" Photo.config.no_image_filename = File.join( "/images/thumbs", "noimg.gif")
ただし、上にも書いたように、development環境で動作しているときは、アプリケーションの各クラスはリクエスト毎にロードされるので、
この初期化ファイルをモデル側で明示的に読み込まないといけないということになってしまっている。
結果
urlは
たとえば、以下のようになる。
http://localhost/photos/0000/0013/hoge.jpg
格納場所は
たとえば、以下のようになる。
/Library/WebServer/Documents/photos/0000/0013/hoge.jpg
attachment_fuプラグインで画像アップロード
前提
RMMagicをインストールしておく。
インストール
railsアプリケーションのトップでscript/pluginを実行してインストール
>ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu
今回の例
既存の商品モデル(product)の編集ページに対してその商品写真(photo)を加えるという例を作ってみる。
http://d.hatena.ne.jp/cuspos/20071110を参照させていただきました。
Model生成とmigrate
アップロードデータを扱うモデルを作成。
今回は商品(product)の商品画像(photo)のモデルを作成。
nyaago@kyonkyon: 501 $./script/generate model photo exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/photo.rb create test/unit/photo_test.rb create test/fixtures/photos.yml exists db/migrate create db/migrate/20090607125051_create_photos.rb
それから、migration。
Photoテーブルを生成する。各カラムは、fu_attachmentでのおきまりのよう。
class CreatePhotos < ActiveRecord::Migration def self.up create_table :photos do |t| t.column :parent_id, :integer t.column :content_type, :string t.column :filename, :string t.column :thumbnail, :string t.column :size, :integer t.column :width, :integer t.column :height, :integer t.timestamps end end def self.down drop_table :photos end end
それから、商品テーブル側に、photoへの参照をつけるmigrate
nyaago@kyonkyon: 502 $./script/generate migration mod_product exists db/migrate create db/migrate/20090607125553_mod_product.rb
class ModProduct < ActiveRecord::Migration def self.up add_column(:products, :photo_id, :integer, :null => true) end def self.down remove_column(:products, :photo_id) end end
rake db:migrate
model
photoモデルの実装を行う。
class Photo < ActiveRecord::Base has_attachment :content_type => :image, :storage => :file_system, :max_size => 1.megabyte, :thumbnails => { :thumb => '100x100>', :small => '50x50>' } validates_as_attachment def full_filename(thumbnail = nil) file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s File.join(RAILS_ROOT, file_system_path, *partitioned_path(thumbnail_name_for(thumbnail))) end end
-
- has_attachmentメソッドで画像の保存方法を指定。
今回、使っているのは以下のオプション
:content-type ファイルタイプ指定。今回は画像。他に何が指定できるのかは調べていないけど。
:storage ストレージタイプ。今回がファイルシステム。他に:db_file(db), :s3(amazon s3)がある。(プラグインのtechnoweenie/attachment_fu_/backends以下に対応するクラスが定義されている。)
:max_size ファイルの最大サイズ
:thumbnails これを指定すると、サムネイルを作ってくれる。
あと今回使ってないオプション
:resize_to リサイズして登録される。
-
-
- validates_as_attachmentメソッドにより、保存時のvalidationが実行されるようになる。
-
-
-
- full_filenameメソッドをオーバーライドすることにより、ファイルの保存場所を変更できる。今回、デフォルト実装のままだけど後で変更したくなりそうなので、ここの定義しているみている。
-
商品(product)モデル側も修正。
class Product < ActiveRecord::Base belongs_to :photo .. 省略 ..
商品からそれに関する写真を参照するためbelongs_to属性を指定。
views
以下、関係あるところだけを引用。
<div> <% unless @product.photo.nil? then %> <%= image_tag(@product.photo.public_filename()) %> <% end %> </div> <div> <label for="pthto">写真を追加:</label> <%= f.file_field :uploaded_data %> </div>
-
- モデルのpublic_filenameメソッドにより、画像へのurlが返されるので、その結果を使ってimageタグを作っている。
- file_fieldメソッドで ファイルアップロードタグを作っている。:uploaded_dataという名前を指定して作ってやるのが標準。そうすることにより。controller側ではPhoto.new(params[:photo] )としてmodelを作ってsaveするだけで、画像の保存までできる。
controller
参照元商品とこのphotoを保存するための定義を行う。
# productの更新を行う。 # Validationでエラーがあった場合は、再度入力ページ(edit)を表示する。 def update begin if params[:id] and params[:id].to_i > 0 # for update product = Product.find_by_id(params[:id]) if product.nil? then redirect_to :controller => '/error', :action => 'index' end photo = prepare_photo_for_update(product) product.attributes = params[:product].dup.delete_if { |key, value| key.to_sym == :uploaded_data } else # for insert photo = prepare_photo_for_insert product = Product.new() product.attributes = params[:product].dup.delete_if { |key, value| key.to_sym == :uploaded_data } product.photo_id = photo.id end # validate unless photo.nil? unless photo.valid? prepare_for_edit(product) return render(:action => "edit") end end unless product.valid? prepare_for_edit(product) return render(:action => "edit") end # save Product.transaction { unless photo.nil? if(!photo.save) prepare_for_edit(product) return render(:action => "edit") end product.photo_id = photo.id end if(!product.save) prepare_for_edit(product) return render(:action => "edit") end } rescue => e p e.inspect logger.debug(e.inspect) return redirect_to_error(e.inspect) end #このあと省略(表示ページへのリダイレクトをする) end private # 商品挿入時のphotoモデル生成 def prepare_photo_for_insert return nil if params[:product][:uploaded_data].blank? photo = Photo.new() # puts params[:product][:uploaded_data] photo.uploaded_data = params[:product][:uploaded_data] photo end # 商品変更時のphotoモデル生成 def prepare_photo_for_update(product) photo = if product.photo_id then Photo.find_by_id(product.photo_id) else nil end if photo.nil? Photo.new(:uploaded_data => params[:product][:uploaded_data]) else photo.uploaded_data = params[:product][:uploaded_data] photo end end # 編集ページを開く場合の準備 def prepare_for_edit(product) @product = product if @photo = Photo.find_by_id(product.photo_id) else @photo = nil end end
上記処理についての備考。
課題
以下は、これから検討。
-
- 画像削除する。
- 保存確認ページを実装する場合はどうする?
- 商品に対する複数Photoの保存(has_many アソシエーション)。
- 一覧データ+画像ファイル群からバッチで登録する。
- 保存場所をアプリケーション以下とは別の場所(または別ホスト)に保存できるか?
RMagickインストールメモ
環境はMacBook Pro / MaxOSX 10.5.7
参考させていただいたのは、篳篥日記,Installing RMagick on OS X using MacPorts,Goodpic ブログなど。
とりあえず,jpeg,png,gif,tiffあたりのファイルタイプを使えるようにインストール。
png,jpeg, freetype
/usr/local以下にインストール済み
tiffライブラリをインストール
http://dl.maptools.org/dl/libtiff/よりダウンロードして展開してインストール
>./configure >make >sudo make install
これで、/usr/local以下にインストールされる
ghostscriptライブラリのインストール
http://pages.cs.wisc.edu/~ghost/よりダウンロードして展開してインストール
>./configure >make >sudo make install
gs-fonts-std
[_ftp://ftp.t.ring.gr.jp/pub/GNU/ghostscript/]より取得して、
>tar xvzf gnu-gs-fonts-std-6.0.tar.gz >sudo cp -r fonts /usr/local/share/ghostscript/
ImageMagic
ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gzよりダウンロードして展開してインストール
今回のバージョンは6.5.3.1
>./configure --disable-static --with-modules --without-magick-plus-plus --with-quantum-depth=8 >make >sudo make install
--disable-staticは共有ライブラリのみの指定, --with-modulesは「enable building dynamically loadable modules」 (動的にロードされるモジュールを構築)ということで、RMagicで使うには、これが必要らしい。
with-quantum-depthは1チャンネルのBit数。デフォルトは16Bitであるが、そんな高画質はいあらないので8Bitを指定。
RMagick
gemの実行でOK。
$sudo gem install rmagick Building native extensions. This could take a while... Successfully installed rmagick-2.9.2 1 gem installed
RMagickインストール確認
引数に元画像と作成される画像の名称を指定してのリサイズを行うサンプルをつくる実行確認。
require 'rubygems' require 'RMagick' image = ARGV[0] original = Magick::Image.read(image).first resized = original.resize_to_fit(75,100) resized.write(ARGV[1])
i18nの整理
Rails2.2のi18nの使い方を整理しておきましょうっと。
初期化
「
主な設定属性は以下のとおり。
以下、設定例。load_pathはデフォルトのまま。言語設定は、日本語
I18n.load_path += Dir[ File.join(RAILS_ROOT, 'config', 'locales', '*.{rb,yml}') ] I18n.default_locale = "ja" I18n.locale = "ja"
翻訳テキスト
YAML形式で作成。
上のとおり、I18n.load_pathで設定したパスのファイルが翻訳テキストとして、読み込まれる。
階層化したキーに翻訳テキストを関連づけていく。
u_scope1: l_scope1: key1: "<翻訳テキスト>"
翻訳テキストの取得
「I18n.t」メソッドで指定したキーの翻訳テキストを取得する。
階層化したキーを「.」区切りった文字列で指定したり。
上記階層をシンボルの配列または「.」区切った表現で:scapeパラメータに指定、最下位階層をシンボルで指定するなどいくつか記法を使って取得できる。
以下の3種類の記述は同等である。
I18n.t 'u_scope1.l_scope1.key1' I18n.t :key1, :scope => [:u_scape, :l_scope] I18n.t :key1, :scope => 'u_scape.l_scope'
コルーチンのお勉強
プログラミングGaucheのコルーチン(19.8)内容のお勉強。
P299に載っているexitのための手続きが定義されるほうのコルーチンを定義するためのマクロ定義の解読。
(use util.queue) (define *tasks* (make-queue) ) (define-syntax define-coroutine (syntax-rules () [ (_ (routine yield) body ...) (define (routine) (call/cc ( lambda(return) (define (yield) (call/cc (lambda (cont) (enqueue! *tasks* cont) (return)) ) ) body ...)) ((dequeue! *tasks*))) ] [ (_ (routine yield exit) body ...) (define (routine) (call/cc ( lambda (escape) (call/cc ( lambda(return) (define (yield) (call/cc (lambda (cont) (enqueue! *tasks* cont) (return)) )) (define (exit) (call/cc (lambda (cont) ( (enqueue! *tasks* cont) (escape) ) ) ) ) body ...)) ((dequeue! *tasks*) ) ) ) ) ]))
まず、exitのないほう。
コルーチン
これによって返される手続き(コルーチン)では、call/ccによりコルーチン呼び出し時の継続(return変数)の取得を行う。
このcall/cc内でbodyの実行を開始する(=他の継続が呼ばれないかぎり、このbodyの実行が続けられる)。call/ccを抜けて(<=他の継続が呼ばれて)このコルーチン呼び出し時の継続に戻ったときは、実行キューから継続を取り出し実行。
yield手続き
内側で定義される他のコルーチンを呼び出す手続き(yield)では、call/ccにより継続を取得して後で呼び出されるように実行キューに入れてから、
コルーチン呼び出し時の継続を呼び出す。この結果は実行キューからの取り出し+実行となる。
exitをつける場合
抜けるための継続取得
まず、再上位にcall/ccが呼び出される。これにより取得した継続がコルーチンの実行と実行キューの継続から抜けるためのものとなる。
exit手続き
exitの定義は、再開されたときに実行されるように現在の継続をキューに入れてから、コルーチンの最上位の継続を呼び出すことにより、コルーチンの実行と実行キューの継続から抜ける。