Posted at

RubyMotionでGCDを使う

More than 5 years have passed since last update.

GCD

今日はRubyMotionでGCD(Grand Central Dispatch)を使う話です.


GCD とは

GCDというのは,


非常に効率的なシステム機能と使い勝手のよいプログラミングモデルを併用して,マルチプロセッサを最大限に活用するために必要なコードを徹底的に簡素化


するものらしいです

(AppleのGrand Central Dispatchの説明より)

UITableViewとかそういうのはMainThreadでちょっと重い処理をすると,すぐにパフォーマンスが悪くなってなんだこのアプリ糞だな!とか言われるので,そういう時には別Threadを立ててMainThreadの処理を邪魔しないように処理を行う必要があります.

それを楽にしてくれるのがGCD.


Objective-C

Obj-Cのコードだと,

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
// 処理
});

こんな感じになります.


RubyMotion

RubyMotionでは,GCD関連はDispatchクラスを使います.

RubyMotion Runtime Guid にも書いてます.

上で書いたObj-CのコードをRubyMotionで書きなおすと,

    Dispatch::Queue.concurrent.async{

# 処理
}

こうなります.超簡単.


UIの更新に関して

さて,実際にはConcurrent Dispatch Queueで時間のかかる処理を行い,

処理を行った結果をUIに反映させることがよくあります.

その場合に直接UIへ値を代入とかすると落ちます.

UIはMainThreadから更新しなければなりません.

その場合は,

    Dispatch::Queue.concurrent.async{

# 処理
Dispatch::Queue.main.async{
#UI更新
}
}

このようにDispatch::Queue.mainでMain Dispatch Queueに更新処理を入れることで実現できます.


実戦

実際に少し書いてみます.

$ motion create gcd

$ cd gcd


app/app_delegate.rb


app/app_delegate.rb

    class AppDelegate

def application(application, didFinishLaunchingWithOptions:launchOptions)
@window = UIWindow.alloc.initWithFrame UIScreen.mainScreen.bounds
@window.rootViewController = NSBundle.mainBundle.loadNibNamed(
'RootViewController',
owner: self,
options: nil).first
@window.rootViewController.wantsFullScreenLayout = true
@window.makeKeyAndVisible
true
end
end


InterfaceBuilder

resouces/RootViewController.xib という形でUIButton(Tag 2),UILabel(Tag 1)を追加しておいて下さい.

(参考:[RUBYMOTION] INTERFACEBUILDERと合わせて使って楽をしよう


app/root_view_controller.rb


app/root_view_controller.rb

    class RootViewController < UIViewController

def viewDidLoad
@label = view.viewWithTag 1
@button = view.viewWithTag 2
@button.addTarget(
self,
action: 'onClicked:',
forControlEvents:UIControlEventTouchUpInside)
end

def onClicked(sender)
p "onClicked"

Dispatch::Queue.concurrent.async {
NSThread.sleepForTimeInterval 5
Dispatch::Queue.main.async {
@label.text = ["hoge", "fuga", "moge"].sample
}
}
end
end



実行

$ rake

シミュレータが立ち上がり,Buttonを押すとLabelの文字列がちょっと待ったあとに変わります.

連打すると次々変わると思います.

その間Buttonが押せないとかそういうことが起こらないのが分かると思います.