Grand Central Dispatch

 タスクを非同期に実行する技術

Dispatch Queue の種類

Serial Dispatch Queue : FIFOで現在実行中の処理を待つ
  - 1スレッドのみ
Concurrent Dispatch Queue : パラレルに実行
  - 複数のスレッドを使用する

Dispatch Queue の生成

Serial Dispatch Queue
  dispatch_queue_create(@"キュー名", NULL);
  - キューを作成した数だけスレッドが生成される

Concurrent Dispatch Queue
  dispatch_queue_create(@"キュー名", DISPATCH_QUEUE_CONCURRENT);

Dispatch Queueはcreateしたものは、releaseが必要!

Dispatch Queue の取得

    // メイン
    dispatch_queue_t main = dispatch_get_main_queue();

    // 優先度高い
    dispatch_queue_t high = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    // 標準
    dispatch_queue_t def = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 低い
    dispatch_queue_t low = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

    // バックグランド
    dispatch_queue_t back = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

Dispatch Queueの優先度の変更
  dispatch_set_target_queue

一定時間後に実行したいもの
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        NSLog(@"after 2.0");
    });

ミリ秒後に実行したい場合は NSEC_PER_MSEC を使用

Dispatch Group
  追加したDispatch Queueがすべて処理が完了したら処理を実行したい場合

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
   
    dispatch_group_async(group, queue, ^{NSLog(@"blk0");});
    dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
   
    dispatch_group_notify(group, queue, ^{NSLog(@"done");});
   
    dispatch_release(group);

  定期的にチェックしたい場合
    long result = dispatch_group_wait(group, 1ull * NSEC_PER_SEC);
    if (result == 0 ) {
        // すべての処理が終了
    } else {
        // 処理実行中
    }

dispatch_barrier_async : 処理を1つだけしか実行しない事を保証する
  読み込みは複数同時で良いが、書き込み中は他の読み込みも行ってほしくない場合等

  dispatch_async(queue, blk0_read);
  dispatch_async(queue, blk1_read);
  // 上記の2つの処理が終わらないとblk_writeは実行しない
  dispatch_barrier_async(queue, blk_write);
  // blk_writeが終わらないとblk2, blk3は実行しない
  dispatch_async(queue, blk2_read);
  dispatch_async(queue, blk3_read);

dispatch_apply : 複数回処理を実行する

dispatch_suspend / dispatch_resume : 処理の中断, 再開
  dispatch_suspend(queue);
  dispatch_resume(queue);

Dispatch Semaphore : データ不整合予防
  dispatch_semaphore_wait / dispatch_semaphore_signal(終了) を使い分けしてシンプルなはいた制御を行う

dispatch_onec : 一度だけ実行
  singleton等に適用

Dispatch I/O
  大きなファイルを読むときに分割して読み込み

Ruby on Raileのユニットテスト

Ruby on Railsのユニットテストについて勉強

テストの種類とサポートツール
ユニットテストサポート
 - Test::Unit
  - Rails標準のフレームワーク - 可読性の欠ける
 - RSpec
  - 可読性に優れている - Test::Unitとは異なる独特の記法
 - Shoulde
  - Test::Utilからの移行が容易 - 機能面で、RSpecに及ばない

エンドツーエンドテスト
 - Request Spece
  - rspec-rails 標準
 - Steak
  - 受け入れテストを記述できる
 - Cucumber
  - 自然言語的な記述でテストシナリオの記述が可能

RSpecによるユニットテスト
 - リポジトリの作成 Test::Utilを組み込まない
  - reils new [APP_PATH] -T
 - rspec-rails のインストール
  - Gemfileに追記
group :development, :test do
  gem "rspec", "2.4.0"
  gem "rspec-rails", "2.4.1"
end
  - ライブラリのインストール
bundle install
 - RSpecの設定ファイル生成
rails generate rspec:install

モデルのテスト
 - モデル生成
reils generate model article title:string body:text
 - マイグレーションスクリプト実行
rake db:migrate
    - ※ undefined method `prerequisites' for nil:NilClass
      エラーの場合、rspecのバージョンを消してbundle updateを実行
group :development, :test do
  gem "rspec"
  gem "rspec-rails"
end
  - データロード
rake db:test:load

 - テストの実行
  - spec/models/article_spec.rb
require 'spec_helper'

describe Article do
  context "title and  body init" do
    before do
      @article = Article.new(
        :title => "first blog",
        :body => "first body"
      )
    end
    it "title check" do
      @article.title.should == "first blog"
    end
    it "body check" do
      @article.body.should == "first body"
    end
  end
end
  - コマンド
bundle exec rspec spec/models/article_spec.rb
 - 出力の変更
  - オプション-f dをつける
  - .rspecで設定可能
bundle exec rspec -f d spec/models/article_spec.rb

 - バリデーションのテスト
  - app/models/article.rbにバリデーションを追加
class Article > ActiveRecord::Base
  attr_accessible :body, :title
  validates :title,  :presence => true
end
  - テストの追加
  context "not title" do
    before do
      @article = Article.new
    end
    it { @article.should_not be_valid }  end
  context "exist title" do
    before do
      @article = Article.new(:title => "first blog")
    end
    it { @article.should be_valid }
  end

コントローラのテスト
 - コントローラの生成
  - rails g controller articles index
 - コントローラで使うメソッド達
  - get, post, put, delete => HTTPリクエスト発行
  - response => HTTPレスポンスの取得
  - assigns => コントローラのいんスタン変数を取得
  - flash, session, cookie => 各項目の取得
 - 一覧表示機能のテスト
  - spec/controllers/articles_controller_spec.rbの実装
require 'spec_helper'

describe ArticlesController do
  describe "GET 'index'" do
    before do
      @article1 = Article.create(
        :title => "Article 1",
        :body  => "Hello"
      )
      @article2 = Article.create(
        :title => "Article 2",
        :body  => "World"
      )
     get 'index'
    end
    it "should be success" do
      response.should be_success
    end
    it "all articles" do
      assigns[:articles].should =~ [@article1, @article2]
    end
  end
end
  - コントローラに一覧取得処理を追加
class ArticlesController < ApplicationController
  def index
   @articles = Article.all
  end
end

ビューのテスト
 - 一覧画面のテスト
require 'spec_helper'

describe "articles/index.html.erb" do
  before do
    assign(
      :articles, [
        Article.create(
          :title => "first blog",
          :body  => "content"
        )
      ]
    )
    render
  end
  it "title view" do
    rendered.should =~ /first blog/
  end
  it "body view" do
    rendered.should =~ /content/
  end
end
 - viewのテストで使うメソッド
  - assign => ビューのインスタンス変数に値を設定する
  - render => ビューのレンダリングを行う
  - rendered => レンダリングした結果を返す
 - Viewの実装
<h1>Articles#index</h1>

<% @articles.each do |article| %>
  <h2><%= article.title %></h2>
  <div><%= article.body %></div>
<% end %>

ヘルパーのテスト
 - 登校日を表示するヘルパー
require 'spec_helper'
describe ArticlesHelper do
  describe "#posted_on" do
    before do
      @now = Time.now
      @article = Article.create(
        :title => 'first blog',
        :body  => 'content'
      )
    end
    it {
      helper.posted_on(@article).should == "#{@now.year}/#{@now.month}/#{@now.day}"
    }
  end
end
 - ヘルパーのテストで使うメソッド
  - assign => インスタンス変数をセットする
  - helper => 対象ページのヘルパーをインクルードしたオブジェクトにアクセス
 - ヘルパーの実装
module ArticlesHelper
  def posted_on(article)
    time = article.created_at
    "#{time.year}/#{time.month}/#{time.day}"
  end
end


Rails入門(チュートリアル)

ruby on rails の勉強がてら、本家のチュートリアルを一通り実行してみる

3.2 プロジェクトの作成
$ rails new blog
- ヘルプが見れる(rails new -h)

3.3.1 DBの内容
config/database.yml
- 各環境の設定内容が記述されている
- デフォルトではsqlite3

3.4 DBの生成
$ rake db:create

4.1 サーバ起動
$ rails server
- http://localhost:3000 にアクセスするとwelcomeページが表示される
- 終了はCtrl+c

4.2 お決まりのHello world
- コントローラの生成
$ rails generate controller home index
- 生成されるコントローラは app/controllers/home_controller.rb
- view(html)を修正app/views/home/index.html.erb
<h1>Hello, Rails!</h1>

4.3 アプリケーションのHomeページを設定
- 必ずpublic/index.html が表示されてしまうため削除
$ rm public/index.html
- ディスパッチの設定を追加
- ファイル: config/routes.rb

Blog::Application.routes.draw do
#...
# You can have the root of your site routed with "root"
# just remember to delete public/index.html.
root :to => "home#index"

- その後再度 http://localhost:3000 にアクセスすると、先ほどのテンプレートが表示される

6 リソースの作成(MVCを作る)
- scaffold を指定すると、model, view, controllerを一括で作成してくれる
$ rails generate scaffold Post name:string title:string content:text

6.1 マイグレーション
- テーブルの作成や、変更などを管理する仕組み
- ファイル :db/migrate/20120508162203_create_posts.rb
- マイグレーションスクリプト実行
$ rake db:migrate
- productionで作成する場合はオプションをつける rake db:migrate RAILS_ENV=production

6.2 Topページにリンクを追加
- ファイル : app/views/home/index.html.erb
<h1>Hello, Rails!</h1>
<%= link_to "My Blog", posts_path %>

- link_to [タイトル], 遷移先?

6.4 モデルの確認
- ファイル : app/models/post.rb

6.5 バリデータの追加
- ファイル : app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :content, :name, :title
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
end

- attr_accessibleは変更可能なパラメータ
- presence は必須、lengthは長さを指定

6.6 コンソールを使ってみよう
$ rails console
>> p = Post.new(:content => "A new post")
=> #<Post id: nil, name: nil, title: nil,
content: "A new post", created_at: nil,
updated_at: nil>
>> p.save
=> false
>> p.errors.full_messages
=> ["Name can't be blank", "Title can't be blank", "Title is too short (minimum is 5 characters)"]

- バリデータが効いているのがわかる

6.7 一覧ページ
- ファイル : app/controllers/posts_controller.rb
def index
@posts = Post.all

respond_to do |format|
format.html # index.html.erb
format.json { render json: @posts }
end
end

- @postsに投稿内容をすべて取得する
- html, jsonに対応
- http://localhost:3000/posts.html
- http://localhost:3000/posts.json

- テンプレート : app/views/posts/index.html.erb
- html エスケープしない場合、
- rails 2 だと <%=h post.name %>
- rails 3 では <%= raw post.name %>

6.8 カスタムレイアウト
- フッターやヘッダーを定義
- ファイル : app/views/layouts/application.html.erb
- 各コントローラ毎に指定する場合
- app/views/layouts/posts.html.erb

6.9 新規作成ページ
- ファイル : app/controllers/posts_controller.rb
def new
@post = Post.new

respond_to do |format|
format.html # new.html.erb
format.json { render json: @post }
end
end
- テンプレート : app/views/posts/new.html.erb
<h1>New post</h1>
<%= render 'form' %>
<%= link_to 'Back', posts_path %>
- render 'form'を指定することで、app/views/posts/_form.html.erb の内容を出力する

- 投稿処理
def create
@post = Post.new(params[:post])

respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render json: @post, status: :created, location: @post }
else
format.html { render action: "new" }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end

6.10 詳細表示
- http://localhost:3000/posts/1 にアクセスすると /posts/(id) として、:idに取得可能
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => @post }
end
end

- テンプレート : app/views/posts/show.html.erb

6.11 編集ページ
- 詳細表示と違い、jsonでのレスポンスがないためシンプル
def edit
@post = Post.find(params[:id])
end
- 更新処理
def update
@post = Post.find(params[:id])

respond_to do |format|
if @post.update_attributes(params[:post])
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
- update_attributesで必要なパラメータを更新

6.12 削除
- ファイル : app/controllers/posts_controller.rb
def destroy
@post = Post.find(params[:id])
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end

7.1 モデルの追加
- テーブル間のリレーションシップを指定 post:references
$ rails generate model Comment commenter:string body:text post:references
- モデルクラスにも定義されている
- ファイル : app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
attr_accessible :body, :commenter
end

- 再度マイグレーションスクリプトが生成されているて、それを反映
- ファイル : db/migrate/20120508170658_create_comments.rb
$ rake db:migrate

7.2 関連づけモデル
- 1投稿に付き複数のコメントが存在するためPostのmodelを修正
- ファイル :app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :content, :name, :title
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
has_many :comments
end

7.3 ルート(ディスパッチ)にコメントを追加
- ファイル : config/routes.rb
- posts部分を修正
resources :posts do
resources :comments
end

7.4 コントローラの追加
$ rails generate controller Comments
- 上記エラー出る場合、ルートファイルで追加した部分をコメントアウトする

- Postのテンプレートにcomment投稿を追加
- ファイル : app/views/posts/show.html.erb
<p id="notice"><%= notice %></p>

<p>
<b>Name:</b>
<%= @post.name %>
</p>

<p>
<b>Title:</b>
<%= @post.title %>
</p>

<p>
<b>Content:</b>
<%= @post.content %>
</p>

<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
<div class="field">
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

- コメントコントローラに処理を追記
- ファイル : app/controllers/comments_controller.rb
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.create(params[:comment])
redirect_to post_path(@post)
end


- 詳細画面でコメントを閲覧できるように修正
- ファイル : app/views/posts/show.html.erb
<h2>Comments</h2>
<% @post.comments.each do |comment| %>
<p>
<b>Commenter:</b>
<%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
<%= comment.body %>
</p>
<% end %>


8.リファクタリング
- テンプレートの共通部分を外だししてみる

8.1 コレクション
- コメント部分のテンプレを外だし
- ファイル : app/views/comments/_comment.html.erb
<p>
<b>Commenter:</b>
<%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
<%= comment.body %>
</p>

- 参照部分の修正 app/views/posts/show.html.erbを修正 (まだ納得は行かない><)
- 修正前
<% @post.comments.each do |comment| %>
<p>
<b>Commenter:</b>
<%= comment.commenter %>
</p>
<p>
<b>Comment:</b>
<%= comment.body %>
</p>
<% end %>

- 修正後
<%= render @post.comments %>

8.2 Form部分の外だし
- ファイル : app/views/comments/_form.html.erb
<%= form_for([@post, @post.comments.build]) do |f| %>
<div class="field">
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>

- 参照部分の修正 : app/views/posts/show.html.erb
- 今回は単純に置き換える
<%= render "comments/form" %>

9 コメントの削除
- リンクの追加 : app/views/comments/_comment.html.erb
<p>
<%= link_to 'Destroy Comment', [comment.post, comment],
:confirm => 'Are you sure?',
:method => :delete %>
</p>

- コントローラに追加 : app/controllers/comments_controller.rb
def destroy
@post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
@comment.destroy
redirect_to post_path(@post)
end

9.1 関連データの削除も対応
- 親の投稿データが削除された場合、一緒に削除
- ファイル : app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :content, :name, :title
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
has_many :comments, :dependent => :destroy
end

10 セキュリティー
- Basic認証
- ファイル : app/controllers/posts_controller.rb
class PostsController < ApplicationController
http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index, :show]
# GET /posts
# GET /posts.json
def index
@posts = Post.all

- :exceptで対象外を指定
- :only => :destroy を指定すると、削除のみ認証する

11 複数モデル
- タグモデルの生成
$ rails generate model tag name:string post:references
- マイグレーション実行
$ rake db:migrate
- モデルに関連づけを行う
- ファイル : app/models/post.rb
has_many :tag

accepts_nested_attributes_for :tags, :allow_destroy => :true,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }

- accepts_nested_attributes_forは親子関係の定義?
- allow_destroyはネストした属性にチェックボックスを表示
- reject_if は禁止条件を設定

- フォームテンプレートにタグを追加
- ファイル : views/posts/_form.html.erb
<h2>Tags</h2>
<%= render :partial => 'tags/form',
:locals => {:form => post_form} %>

- タグ用のフォームテンプレート
- ファイル : app/views/tags/_form.html.erb
<%= form.fields_for :tags do |tag_form| %>
<div class="field">
<%= tag_form.label :name, 'Tag:' %>
<%= tag_form.text_field :name %>
</div>
<% unless tag_form.object.nil? || tag_form.object.new_record? %>
<div class="field">
<%= tag_form.label :_destroy, 'Remove:' %>
<%= tag_form.check_box :_destroy %>
</div>
<% end %>
<% end %>

- 投稿テンプレートにタグを追加
- ファイル : app/views/posts/show.html.erb
<p>
<b>Tags:</b>
<%= @post.tags.map { |t| t.name }.join(", ") %>
</p>

12 View Helpers
- ファイル : app/helpers/posts_helper.rb
def join_tags(post)
post.tags.map { |t| t.name }.join(", ")
end

- 使ってみる : app/views/posts/show.html.erb
- 変更前
<p>
<b>Tags:</b>
<%= @post.tags.map { |t| t.name }.join(", ") %>
</p>
- 変更後
<p>
<b>Tags:</b>
<%= join_tags(@post) %>
</p>

はじめてのRuby


RVM(Ruby Version Manager)


  • RVMとは複数バージョンのRubyを使えるルーツ

rvmのインストール

$ curl -L get.rvm.io | bash -s stable
.bashrcに追加
source ~/.rvm/scripts/rvm

Rudyのインストール

rudyのインストール可能なバージョンを確認
$ rvm list known

rudyのインストール
$ rvm install 1.9.2
The provided compiler '/usr/bin/gcc' is LLVM based, it is not yet fully supported by ruby and gems, please read `rvm requirements`
 - Mac OSX Lionではインストール時にエラーが出たので、下記を実行
   - export CC=/usr/bin/gcc-4.2


インストールバージョンの確認
$ rvm list


最新版にするには
$ rvm get head
$ rvm reload

Rudy on Railsのインストール

Rudyのパッケージ管理ツール(gem)を使用してrailsをインストール
$ gem install rails

アプリケーションの作成

プロジェクトの作成

$ rails new books

パッケージの管理

Bundlerは複数のPCで必要なGemパッケージをインストールする仕組みを提供してくれる
 - Gemfile : 必要なパッケージ情報
 - Gemfile.lock : インストール済み情報

静的コンテンツ

PROJECT/public

MVC生成コマンド

コントローラの作成

$ rails generate controller [コントローラ名]

DBの作成

$ rake db:create
 - config/database.yml の内容でDBを作成する

モデルの作成

$ rails generate model title

Scaffold機能

 - controller, model, viewをコマンド一つで実行してくれる


マイグレーション

テーブルの作成や、変更の仕組みとしてマイグレーションと呼ばれる機能を提供

マイグレーションスクリプト実行

$ rake db:migrate


レスポンス関連

レスポンス形式の変更

    respond_to do |format|
      format.html
      format.json {render :json => オブジェクト}
      format.xml  {render :xml => オブジェクト}
    end

リダイレクト

    render_to :action => 'アクション名'

UIKitにBlocksを適用(UIActionSheet)

UIKitでは、ボタンを押したり、アクションが行われると、delegateの仕組みを使って通知を受けます。

例:

- (void)showActionSheet
{
    UIActionSheet* as = [[UIActionSheet alloc] initWithTitle:@"title"                                                    delegate:self                                           cancelButtonTitle:@"cancell"                                      destructiveButtonTitle:@"destruct"                                           otherButtonTitles:@"btn1", @"btn2", nil ];
    [as showInView:self.view];
    [as autorelease];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    switch(actionSheet.tag) {
        case MSG_CONFIRM_TAG:        {
            switch (buttonIndex) {
                case 1:
                    // destruct                    break;
                case 2:
                    // cancell                    break;
                case 3:
                    //btn1                default:
                    break;
            }
        }
            break;
        default::
            break;
    }
}



表示するロジックと、アクションが別々に書かれていて、直感的なコードではない。
また、異なるUIActionSheetでも同じdelegate先になってしまう(書き方しだいですが)

iOS4からはBlocksというコールバックのような仕組みが準備されていて、それを使えば、表示部分と、アクションされた部分を一カ所で記述できる。

例1:

- (void)showActionSheet{    UIActionSheet* as = [[UIActionSheet alloc] initWithTitle:@"sample"                                                       block:^(NSInteger buttonIndex){                                                           switch (buttonIndex) {                                                               case 1:                                                                   // destruct                                                                   break;                                                                                                                                  case 2:                                                                   // cancell                                                                   break;                                                               case 3:                                                                   //btn1                                                               default:                                                                   break;                                                           }                                                       }                                            cancelButtonTitle:@"cancell"                                      destructiveButtonTitle:@"destruct"                                           otherButtonTitles:@"oth1", @"oth2",                         nil];    [as showInView:self.view];    [as autorelease];}
例2:



- (void)showActionSheet
{
    UIActionSheet* as = [[UIActionSheet alloc] init];
    as.title = @"sample";
    [as addButtonWithTitle:@"btn1" withBlock:^{
// btn1
    }];
    [as addButtonWithTitle:@"btn2" withBlock:^{
// btn2
    }];
    [as addButtonWithTitle:@"cancel" withBlock:^{
// cancell
    }];
    [as setCancelButtonIndex:2];
    [as showInView:self.view];
    [as autorelease];
}

今回はcategoryとして生成しましたが、もちろんサブクラス化しても対応できます

  • initializeと、ボタンを追加する(addButtonWithTitle)にそれぞれblockを受け取る
  • delegate先のblock格納先として、UIActionSheetCallbackを作成



UIActionSheet+Blocks.h




@interface UIActionSheet (Blocks)
typedef void (^UIActionSheetCallback_t)(NSInteger buttonIndex);
typedef void (^UIActionSheetButtonCallback_t)(void);

- (id)initWithTitle:(NSString *)title block:(UIActionSheetCallback_t)block cancelButtonTitle:(NSString *)cancelButtonTitle destructiveButtonTitle:(NSString *)destructiveButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
- (NSInteger)addButtonWithTitle:(NSString *)title withBlock:(UIActionSheetButtonCallback_t)block;
@end


@interface UIActionSheetCallback : NSObject <UIActionSheetDelegate> {
    UIActionSheetCallback_t callback;    NSMutableDictionary* buttonCallbacks;
}
@property (nonatomic, copy) UIActionSheetCallback_t callback;@property (nonatomic, retain) NSMutableDictionary* buttonCallbacks;
- (id)initWithCallback:(UIActionSheetCallback_t) callback;
@end


 UIActionSheet+Blocks.m
#import "UIActionSheet+Blocks.h"
@implementation UIActionSheet (Blocks)
- (id)initWithTitle:(NSString *)title block:(void(^)(NSInteger))block cancelButtonTitle:(NSString *)cancelButtonTitle destructiveButtonTitle:(NSString *)destructiveButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {
    self = [self initWithTitle:title delegate:nil cancelButtonTitle:cancelButtonTitle destructiveButtonTitle:destructiveButtonTitle otherButtonTitles:nil];    if (self) {
        self.delegate = [[[UIActionSheetCallback alloc] initWithCallback:block] autorelease];
      
        va_list args;
        va_start(args, otherButtonTitles);
        for (NSString *arg = otherButtonTitles; arg != nil; arg = va_arg(args, NSString*)) {
            [self addButtonWithTitle:arg];
        }
        va_end(args);
    }
    return self;}
- (NSInteger)addButtonWithTitle:(NSString *)title withBlock:(void(^)(void))block {
    if (!self.delegate) {
        self.delegate = [[[UIActionSheetCallback alloc] init] autorelease];
    }
  
    NSInteger index = [self addButtonWithTitle:title];
    [((UIActionSheetCallback*)self.delegate).buttonCallbacks setObject:[[block copy] autorelease] forKey:[NSNumber numberWithInteger:index]];  
    return index;
}
@end
@implementation UIActionSheetCallback
@synthesize callback;@synthesize buttonCallbacks;
- (void)_init {
    self.buttonCallbacks = [NSMutableDictionary dictionary];    [self retain];  
}
- (id)init {
    if (self = [super init]) {
        [self _init];
    }
    return self;}
- (id)initWithCallback:(UIActionSheetCallback_t)aCallback {
    if(self = [super init]) {
        self.callback = aCallback;
        [self _init];
    }
    return self;}
// UIAlertView delegate メソッド- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if(callback)        callback(buttonIndex);
    UIActionSheetButtonCallback_t buttonCallback = [buttonCallbacks objectForKey:[NSNumber numberWithInteger:buttonIndex]];    if (buttonCallback)
        buttonCallback();
    [self release];
}
- (void)dealloc {
    self.callback = nil;
    self.buttonCallbacks = nil;    [super dealloc];
}
@end





コードはこちらから
https://github.com/hrk-ys/ios-blocks

ReactNativeでAndroid対応する話

前提 ReactNativeでiOS版のアプリをリリースしていて、Android版をリリースする話 トラブルシューティング Build.VERSION_CODES.Q が存在しないエラー compileSdkVersionを29以上にすると解決 メモリー足りないエラー Execu...