propertyとretainとautoreleaseと
Objective-Cの@property (retain)なプロパティへのautoreleaseされているオブジェクトの代入ではまったのでメモしておきます。
自分のクラスの中ではselfを使わないとプロパティ経由にはならない
@interface MyClass { SomeObject *_prop; } @property (nonatomic, retain) SomeObject *_prop; @end @implementation MyClass @synthesize _prop; - (void) func:(SomeObject *) prop { self._prop = prop; //propのretainCountが+1される _prop = prop; //propertyのsetterを経由しないで_propにアクセスするためretainされない } @end
autoreleaseされているオブジェクトを継続的に使う場合は自分でretainする必要がある
@interface MyClass2 { SomeObject *_prop2; } @end - (void) func2 { _prop2 = [[SomeClass getAutoreleaseObj] retain]; //自分のクラス内で使い続けることをretainで明示する _prop2 = [SomeClass getAutoreleaseObj]; //この場合イベントループに戻ったときに解放されてしまう }
retainしないで代入した場合はautorelease poolが解放されるタイミングで解放されてしまいます。
感覚的にはfunc2メソッド内でしか使えないと思ったほうがいいです。
retainするpropertyにautoreleaseされたオブジェクトを代入する
最後にこの2つを合わせるとどうなるか。
@interface MyClass3 { SomeObject *_prop3; } @property (nonatomic, retain) SomeObject *_prop3; @end @implementation MyClass3 @synthesize _prop3; - (void) func3 { self._prop3 = [SomeClass getAutoreleaseObj]; //propertyのsetterでretainされる _prop3 = [[SomeClass getAutoreleaseObj] retain]; //直接メンバ変数にアクセスするので自分でretainする必要がある } @end
プロパティ経由なら自分でretainを書いてはいけないし、直接メンバ変数に代入する場合はretainする必要があります。
個人的にはクラス内での代入は下のパターン(自分でretainを書く方法)の方が分かりやすいと思います。
とはいえselfを付けないとsetterを経由しない、ということを知らないと「なんでpropertyでretainしてるのにさらにretainする必要があるの??」と誤解する僕のような人がいるかもしれないのでまとめてみました。
Rails3でjQueryを使う方法
インストール
gem 'jquery-rails'
jQueryをインストール。(prototype.js関連のファイルが削除される)
$ bundle install $ rails g jquery:install remove public/javascripts/controls.js remove public/javascripts/dragdrop.js remove public/javascripts/effects.js remove public/javascripts/prototype.js create public/javascripts/jquery.min.js create public/javascripts/jquery.js conflict public/javascripts/rails.js Overwrite public/javascripts/rails.js? (enter "h" for help) [Ynaqdh] y force public/javascripts/rails.js
プロジェクト作成時にprototype.jsが不要だと分かっている場合は-Jで生成を抑止できる。
$ rails new APP_NAME -J
動作確認
views/samples/index.html.hamlに以下の記述を追加。
3 %table#fade_table (snip) 19 :css 20 #fade_table { 21 display: none; 22 } 23 :javascript 24 window.onLoad = (function(){ 25 $('#fade_table').fadeIn('slow'); 26 })();
これでサーバを再起動して/samplesにアクセスするとデータのtableがフェードインしながら表示されます。
Rails3でRspecを使う方法
Haml、Sassを使えるようになったので今度はRspecを使えるようにします。
インストール
Rails3ではデフォルトでTest::Unit用のディレクトリやファイルが生成されるのですが、これは不要なのでプロジェクト作成時にTest::Unitを使わないようにします。
$ rails new rails3_rspec -T #-TでSkip Test::Unit
次にGemfileに必要なgemを記述します。
※同時にHaml、Sassを使う方法については以前の記事を参照のこと
Rails3とRuby1.9.2でHamlを使う方法
Rails3でSassを使う方法
gem 'haml-rails' group :development, :test do gem 'rspec-rails', '>= 2.0.0.beta.20' gem 'autotest' gem 'webrat' end
Gemfileができたらインストール
$ bundle install vendor/bundle $ rails generate rspec:install
動作確認
動作確認のためにscaffoldで一通りのファイルを生成してテストを実行
$ rails generate scaffold sample $ rake db:migrate $ rake db:test:prepare #テスト用DBを作成 $ rake spec ...............**............ (省略) 29 examples, 0 failures, 2 pending
こんな感じでRspecが使えるようになります。
また、autotestを使うとファイルに変更があったときに自動でテストが走るようになります。
$ bundle exec autotest
Rails3でSassを使う方法
せっかくHamlを使えるようにしたのでSassも使えるようにしたいところ。
とはいってもSassはHamlに同梱されているので前回の記事の手順でインストールまでは既に済んでいます。
設定
config/initializers/sass_config.rbを作成して以下の記述を追加。
#デフォルトのスタイルシートパスを削除 Sass::Plugin.remove_template_location("./public/stylesheets/sass") #app/stylesheetsをSass用のディレクトリに設定 Sass::Plugin.add_template_location("./app/stylesheets") #最小サイズのCSSに変換する Sass::Plugin.options[:style] = :compressed
デフォルトのパスでも問題ないのですが、Sassは.sassファイルから.cssファイルへコンパイルする仕組みのためソースファイルと生成されたCSSの場所を分離しておいたほうがスッキリすると思います。
またSassの変換結果のスタイルは以下のドキュメントに書かれていますので好きなものを設定してください。
http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#output_style
動作確認
Sass用のディレクトリとファイルを作成
$ mkdir app/stylesheets $ vim app/stylesheets/sample.sass
以下の記述をsample.sassに追加
h1 :font :size 200% :weight bold :border 16px solid #DD66CC :width 400px :padding 5px p :font-weight bold :border 16px solid #6666DD :width 400px :padding 5px
$ rails server
$ open http://localhost:3000/sample/index
こんな画面が表示されたら動作確認OK。public/stylesheets/sample.cssが生成されているはず。
Rails3とRuby1.9.2でHamlを使う方法
会社でRails3とHamlを使っているんですが、既に他の方が環境を作った上でコードを書いていたので、勉強のために一から環境構築をやってみました。
Rails環境構築
$ rvm 1.9.2 $ gem install rails $ rails new rails3_haml $ cd rails3_haml $ bundle install vendor/bundle #必要なgemをvender/bundleに一括インストール $ rails server
これでブラウザからhttp://localhost:3000にアクセスして動作していればひとまず最低限のRailsの環境構築はOK.
Hamlインストール
gem 'haml-rails'
haml-railsはRails3にHaml Generatorを追加するためのプラグインです。
元々はrails3-generatorsだったものがHaml部分だけ分離したものらしいです。
もう一度bundle install
$ bundle install
これで準備は完了。
config/application.rbにg.template_engine :hamlのような記述を追加する必要はありません。
あとは試しにcontrollerをgenerateして動作確認してみましょう。
$ rails g controller sample index create app/controllers/sample_controller.rb route get "sample/index" invoke haml #template_engineがhamlに変わっている exist app/views/sample create app/views/sample/index.html.haml invoke test_unit create test/functional/sample_controller_test.rb invoke helper identical app/helpers/sample_helper.rb invoke test_unit identical test/unit/helpers/sample_helper_test.rb $ rails server
これでhttp://localhost:3000/sample/indexにアクセスしてこんな画面が表示されればhamlが動作しています。
やってみた
makeplex salon:あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定 (1/2) - ITmedia エンタープライズ
3時間でできなければ優秀でないことが証明されてしまうことで一部で話題になっている問題をやってみた。
結果は...
“ほぼ確実に”優秀ではない!orz
とりあえずコード晒しとく。Rubyで書いたのにやけに長い。
真面目にアルゴリズムの勉強しないとだめだなぁ。
Kotsu = 1 Shuntsu = 2 Any = 3 class Array def Array.deepcopy(arr) Marshal.load(Marshal.dump(arr)) end def top self[self.length - 1] end def car if self.length > 0 self[0] else nil end end def cdr new_a = Array.deepcopy(self) new_a.shift new_a end def rest(i) new_a = Array.deepcopy(self) new_a.delete_at(i) new_a end end class Machi def initialize @machi = nil @atama = nil @triple = [] end attr_accessor :machi, :atama, :triple def eql?(m) self.hash == m.hash end def hash str = @triple.sort.join str += @atama.join if @atama str += @machi.join if @machi str.hash end def print @triple.sort! @triple.each{|t| STDOUT.print "(#{t.join})" } STDOUT.print "(#{@atama.join})" if @atama && @atama.length > 0 STDOUT.print "[#{@machi.join}]" if @machi STDOUT.print "\n" end def deepcopy new_machi = Machi.new new_machi.machi = Array.deepcopy(self.machi) new_machi.atama = Array.deepcopy(self.atama) new_machi.triple = Array.deepcopy(self.triple) new_machi end end def check_four_tiles(n) $four_tiles.each{|t| return true if t == n} return false end def search_same(temp, rest) return {} if temp.length == 0 v = temp[0] for i in 0...rest.length if v == rest[i] return {v => rest.rest(i)} end end return {} end def search_forward_backword(temp, rest) return {} if temp.length == 0 if temp.length == 2 return {} if temp[1] - temp[0] != 1 end forward = temp.first - 1 backward = temp.last + 1 result = {} for i in 0...rest.length if rest[i] == forward || rest[i] == backward if !result.key?(rest[i]) result[rest[i]] = rest.rest(i) end end end return result end def search_aida_machi(temp, rest) return {} if temp.length != 1 forward = temp.first - 2 backward = temp.last + 2 result = {} for i in 0...rest.length if rest[i] == forward || rest[i] == backward if !result.key?(rest[i]) result[rest[i]] = rest.rest(i) end end end return result end def get_machi(shuntsu2) return [] if shuntsu2.length != 2 case shuntsu2[1] - shuntsu2[0] when 1 ret = [] ret << shuntsu2[0] - 1 if shuntsu2[0] > 1 ret << shuntsu2[1] + 1 if shuntsu2[1] < 9 return ret when 2 return [shuntsu2[1] - 1] else return [] end end def search(type, rest, temp, result) if rest.length == 0 && temp.length == 0 $final_result << result return end case type when Kotsu search_same(temp, rest).each{|num, new_rest| new_result = result.deepcopy new_result.triple << (temp + [num]) search(Any, new_rest, [], new_result) } when Shuntsu search_forward_backword(temp, rest).each{|num, new_rest| new_result = result.deepcopy new_result.triple << (temp + [num]).sort search(Any, new_rest, [], new_result) } when Any if temp.length == 0 search(Any, rest.cdr, temp + [rest.car], result.deepcopy) if !result.atama && !result.machi && !check_four_tiles(rest.car) #一個待ちパターン new_result = result.deepcopy new_result.machi = [rest.car] new_result.atama = [] search(Any, rest.cdr, [], new_result) end elsif temp.length == 1 search_same(temp, rest).each{|num, new_rest| search(Kotsu, new_rest, temp + [num], result.deepcopy) if !result.atama #頭パターン new_result = result.deepcopy new_result.atama = temp + [num] search(Any, new_rest, [], new_result) end if !result.machi && !check_four_tiles(num) #刻子待ちパターン new_result = result.deepcopy new_result.machi = temp + [num] search(Any, new_rest, [], new_result) end } search_forward_backword(temp, rest).each{|num, new_rest| search(Shuntsu, new_rest, (temp + [num]).sort, result.deepcopy) exist_machi = false get_machi((temp + [num]).sort) .map{|m| !check_four_tiles(m)} .each{|m| exist_machi |= m} if !result.machi && exist_machi #順子待ちパターン new_result = result.deepcopy new_result.machi = (temp + [num]).sort search(Any, new_rest, [], new_result) end } if !result.machi search_aida_machi(temp, rest).each{|num, new_rest| exist_machi = false get_machi((temp + [num]).sort) .map{|m| !check_four_tiles(m)} .each{|m| exist_machi |= m} if exist_machi new_result = result.deepcopy new_result.machi = (temp + [num]).sort search(Any, new_rest, [], new_result) end } end end end end def check_number_of_every_tile(tiles) arr = [] ret = [] arr.fill(0, 1..9) tiles.each{|t| arr[t.to_i] += 1} for i in 1..9 ret << i if arr[i] == 4 end ret end $final_result = [] $four_tiles = [] input = [ "1112224588899", "1122335556799", "1112223335559", "1223344888999", "1112345678999", "1113335558888", "1222233456788", "1223567789789" ] input.each{|i| $final_result.clear $four_tiles.clear puts i nums = i.split(//).map{|n| n.to_i}.sort $four_tiles = check_number_of_every_tile(nums) search(Any, nums, [], Machi.new) $final_result.uniq.each{|r| r.print} puts }
id<Protocol>と書くときの@protocolの宣言の方法
delegateをメンバに持つようなクラスを作るとき、delegateの型をidじゃなく特定のProtocolが実装されていることを指定したい時がある。そういう時はid
調べてみたところ以下のページに解説があった。
id<Protocol>, Retain/Release and Protocol Inheritance | Mobile Orchard
Protocolの宣言時にNSObject Protocolを継承すればいいらしい。
つまり
@protocol MyProtocol <NSObject> - (void)hoge; @end
と書けば警告が出なくなる。