NSArray / NSMutableArray のメモリ管理
配列のメモリはちゃんと管理されてるか知りたかったんで確認がてらサンプル作って確かめてみました.
分かったことは以下のルールです.
- 初期化 : initXXXXのメソッドで指定されたオブジェクトは全てretainされる. arrayXXXはautorelease.
- 追加 : 追加されるオブジェクトがretainされる.
- 削除 : 追加されるオブジェクトがreleaseされる.
- 配列自体がdealloc : 内包しているオブジェクト全てがreleaseされる.
検証コード
NSObject *objA = [[NSObject alloc] init]; NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:objA, nil]; NSLog(@"after init array; array retainCount:%d expect 1", [array retainCount]); NSLog(@"after init array; objA retainCount:%d expect 2", [objA retainCount]); NSObject *objB = [[NSObject alloc] init]; NSLog(@"after init objB; objB retainCount:%d expect 1", [objB retainCount]); [array addObject:objB]; NSLog(@"after add objB; objB retainCount:%d expect 2", [objB retainCount]); [array removeObject:objB]; NSLog(@"after remove objB; objB retainCount:%d expect 1", [objB retainCount]); NSObject *objC = [[NSObject alloc] init]; [array addObject:objC]; NSLog(@"after add objC; objC retainCount:%d expect 2", [objC retainCount]); [array release]; NSLog(@"after release array; objA retainCount:%d expect 1", [objA retainCount]); NSLog(@"after release array; objC retainCount:%d expect 1", [objC retainCount]); NSObject *objD = [[NSObject alloc] init]; NSArray *array2 = [[NSArray alloc] initWithObjects:objD, nil]; NSLog(@"after init array2; objD retainCount:%d expect 2", [objD retainCount]); [array2 release]; NSLog(@"after release array2; objD retainCount:%d expect 1", [objD retainCount]);
結果
after init array; array retainCount:1 expect 1 after init array; objA retainCount:2 expect 2 after init objB; objB retainCount:1 expect 1 after add objB; objB retainCount:2 expect 2 after remove objB; objB retainCount:1 expect 1 after add objC; objC retainCount:2 expect 2 after release array; objA retainCount:1 expect 1 after release array; objC retainCount:1 expect 1 after init array2; objD retainCount:2 expect 2 after release array2; objD retainCount:1 expect 1
結果、予想通りの動きでした.
Safariの位置に関するプロパティまとめ
最近、Safari上でJavascriptを組むことが多く成ってきたのでコレを機に把握しきれていない位置関連のプロパティをまとめてみることにしました.
はじめに
- 単位は全てpxで統一
- 青字のプロパティはDOM(標準ではない)関連のプロパティ
- 緑字のプロパティはCSS関連のプロパティ
CSS関連のプロパティには単位も含まれますがpxで統一しているので表記上省略しています.
DOM関連のプロパティはdocument.getElementById('id')等で取得したオブジェクトのプロパティです.
CSS関連の値の取得はdocument.defaultView.getComputedStyle(elem,null)で取得したオブジェクトのプロパティを使うと実際に表示されている値が取得できます.プロパティ名にはキャメルケース(LCC)で参照できます.
border,margin,padding関連
例えばdiv要素だとして以下のように覚えるとシンプル!
- borderも含めた長さがoffsetXxxで取得(Width/Height)
- borderを含めない長さがclientXxx又はscrollXxxで取得(Width/Height)
- 上下左右のborderはclientXxxで取得(Top/Right/Bottom/Left)
スクロール時の値
直下の子要素によって、挙動が違います.
どうして+4とか0とかになっているのかドキュメントが見つかりませんでしたが、事実計算方法が違うようです.試したのはimgとdivだけですが、他にも同様の事象があるとおもいます.
スクロールバーが無い場合はscrollWidth/scrollHeightは、それぞれclientWidth/clientHeightと同じくなります.
要素の位置
この例だとdiv要素の3つ重ねています.
例のoffsetTopの算出方法は以下のようになります、
offsetTop = 外側要素(pading-top) + 中間要素(margin-top + border-top + padding-top) + 内側要素(margin-top)
25 = 5 + 5 + 5 + 5 + 5
要素のborderの外側からpositionがstatic以外の親要素のborderの内側との相対距離をoffsetTop/offsetLeftで取得できます.
基準となる親要素はoffsetParentで取得できます.
因にhtml要素と、body要素はposition:staticです.
Sinatra で content_for を使う.
最近、Sinatraを使い出しました. 凄く手軽です.
シンプルだけあってRailsにある機能が欲しいときが良くあります.
Railsのcontent_for
知らない人はググってください. でもまー、概要ぐらいは説明します.
RailsもSinatraもテンプレートを外枠と内枠で分けることができます. でも内枠から外枠に値を渡したいときがあります。そんなときに役立つのがcontent_forです.
(例)タイトルを変えてみる
外枠のテンプレート「layout.rb」
<html> <head><title><%= yield :title %></title></head> <body> <%= yield %> </body> </html>
内枠のテンプレート「index.rb」
<% content_for :title, 'よほほほ' %> ブルックのステッカー買ったけど張る場所がない.
実行結果
<html> <head><title>よほほほ</title></head> <body> ブルックのステッカー買ったけど張る場所がない. </body> </html>
こんな感じの機能です.
Sinatra での実現方法
では本題、Sinatraではcontent_forがありません.
手軽な方法としてはsinatraのプラグインsinatra-content-forがあります.
でも今回はプラグインを使わずにやってみました.
以下のコードをapp.rbとかsinatraの実装ファイルから参照するなり、貼付けてください. それだけでcontent_forが使えます.
module Sinatra module Helpers def content_for(name, content=nil, &block) @content_for_param ||= {} content ||= block if content.nil? if @content_for_param.include?(name) @content_for_param[name].join else nil end else content = content.call if content.is_a? Proc content = content.dup if content.is_a? String @content_for_param[name] ||= [] @content_for_param[name] << content nil end end end module Templates def render_with_content_for(engine, data, options={}, locals={}, &block) if block_given? interrupt_block = Proc.new { |name| if name.nil? block.call else content_for name end } render_without_content_for engine, data, options, locals, &interrupt_block else render_without_content_for engine, data, options, locals end end alias_method :render_without_content_for, :render alias_method :render, :render_with_content_for end end
っで使い方ですがRailsのcontent_forと全く同じってわけではないです. blockを使った表記がerbのプリコンパイルが期待通りに成ってなかったのでちょっと工夫する必要があります.
本来なら以下のように書けると思ったのですが
<% content_for(:title) do %> FooBar <% end %>
プリコンパイルを見てみると以下のような感じで内枠の文字列として追加されちゃってますorz
content_for(:title) do ; @_out_buf.concat "FooBar"; end;
そこで工夫してあげると.以下の書き方で回避できました.
<% content_for(:title){ "FooBar" } %>
ちなみに以下のようにも書けます.
<% content_for(:title, "FooBar") %>
さらに同じ名前で数回コールすると結合されるようにしました。
<% content_for(:title, "FooBar") %> <% content_for(:title, "Boo") %> => FooBarBoo
[Life] IMO for iPhone まとめ Wiki の検索自動リンクbookmarklet
iPhone上で確認するとき、メンドクサイのでbookmarklet化してみました.
githubに上げたのでご自由にどうぞ.
bookmarklet登録方法
- http://gist.github.com/472351 を開く
- auto_search_link.bookmarklet.jsのrawをクリックしてファイルを表示します.
- 全選択してソースをコピー
- ブックマークする. 名前は「AutoSearchLink」とかにする.
- とりあえず「保存」ボタンで完了する.
- ブックマークから「編集」ボタンを押す
- 先ほど保存した「AutoSearchLink」を編集する.
- URLを入れる場所にコピーしたソースを貼付けて完了する.
使い方
IMO for iPhoneまとめ Wiki* の適当なページでブックマークの「AutoSearchLink」を開くと自動的に検索リンクが張られます.
リンクが邪魔な場合は、リロードすれば無く成ります.
コマンドラインでHTTPレスポンスを確認する
HTTPのレスポンスヘッダ情報が期待通りか確認する際に使ったのでメモ
wgetの場合
wget --server-response --spider [URL] または wget -S --spider [URL]
実行すると、ファイルをダウンロードせずにレスポンス情報が表示されます.
$ wget --server-response --spider http://127.0.0.1:4567/offline/cache.manifest スパイダーモードが有効です。リモートファイルが存在してるか確認します。 --2010-07-07 17:57:28-- http://127.0.0.1:4567/offline/cache.manifest 127.0.0.1:4567 に接続しています... 接続しました。 HTTP による接続要求を送信しました、応答を待っています... HTTP/1.1 200 OK Last-Modified: Wed, 07 Jul 2010 08:43:02 GMT Connection: Keep-Alive Content-Type: text/cache-manifest Date: Wed, 07 Jul 2010 08:57:28 GMT Server: WEBrick/1.3.1 (Ruby/1.8.7/2010-01-10) Content-Length: 61 長さ: 61 [text/cache-manifest] リモートファイルが存在します。
curlの場合
curl --verbose [URL] または curl -v [URL]
実行すると、以下の情報が画面にでます.
wgetより分かりやすいかも!
$ curl --verbose http://127.0.0.1:4567/offline/cache.manifest * About to connect() to 127.0.0.1 port 4567 (#0) * Trying 127.0.0.1... connected * Connected to 127.0.0.1 (127.0.0.1) port 4567 (#0) > GET /offline/cache.manifest HTTP/1.1 > User-Agent: curl/7.20.0 (i386-apple-darwin10.3.0) libcurl/7.20.0 OpenSSL/0.9.8n zlib/1.2.5 libidn/1.18 > Host: 127.0.0.1:4567 > Accept: */* > < HTTP/1.1 200 OK < Last-Modified: Wed, 07 Jul 2010 08:43:02 GMT < Connection: Keep-Alive < Content-Type: text/cache-manifest < Date: Wed, 07 Jul 2010 09:00:14 GMT < Server: WEBrick/1.3.1 (Ruby/1.8.7/2010-01-10) < Content-Length: 61 < CACHE MANIFEST stylesheets/msafari.css offline/editor.html * Connection #0 to host 127.0.0.1 left intact * Closing connection #0
[Javascript]jQueryで二度押し防止対策のプラグイン
そんなに難しくないので自分で書いてみた.
Gistに公開しているのでご自由にどうぞ.
jquery.singleclick.js · GitHub
(function($) { var lockKey = 'clickLockedId'; $.fn.releaseClickLocked = function(){ var tid = this.data(lockKey); if(tid!==undefined){ clearTimeout(tid); } $(this).removeData(lockKey); }; $.fn.singleClick = function(handler, timeout) { timeout = timeout===undefined ? 3000:timeout; $(this).click(function(evt){ var self = $(this); if(self.data(lockKey)!==undefined){ return false; } var tid = setTimeout(function(){ self.releaseClickLocked(); },timeout); self.data(lockKey, tid); return handler.apply(this,[evt]); }); }; })(jQuery);
使い方
以下はリンクをクリックしまくった時の挙動がコメントで説明しています.
// 3秒間に1度だけコールされます. $('a.sample1').singleClick(function(evt){ alert('foo'); }); // 1秒間に1度だけコールされます. $('a.sample2').singleClick(function(evt){ alert('foo'); }, 1000); // releaseClickLockedはタイムアウトを待たずにロックを解除します. $('a.sample3').singleClick(function(evt){ var self = $(this); setTimeout(function(){ self.releaseClickLocked(); },1000); });
[Ruby] Railsのエラー管理ツールのHoptoadを使ってみた
Railsのエラー管理がラクに出来るらしいので試しに設定してみました.
この手のツールは見てて楽しいので. 苦になりやすいエラー対応も気分良く出来そう.
導入方法やハマったこと等をまとめてみました.
アカウント登録
試してみるだけなので迷わずタダで使えるプランで!と見てみると
「Try Hoptoad FREE for 30 days」(お試し30日無料!)
なんて出てて無料プランが無いのか?と勘違いしました.
よーーく見ると下の方に
「Egg Plan for Free」(エッグプランは無料ですよ)
と小さく目立たずに書いてある。
あとは適当に入力項目を埋めていって「Create Account」
プロジェクト登録とRailsの設定
ログインしてプロジェクトを追加すると、Railsへのセットアップ方法がかかれています.
1. Remove your existing Exception Notifier from your ApplicationController(ExceptionNotifiableを使ってる場合は消してね):
class ApplicationController < ActionController::Base <del>include ExceptionNotifiable</del>
2. Add the hoptoad_notifier gem to config/environment.rb(config/environment.rb に gem hoptoad_notifierを追加):
config.gem 'hoptoad_notifier
3. Install the gem(rakeコマンドでインストール):
rake gems:install
4. Unpack the gem(gemをアンパック、vendor/plugins以下に配置されるだけなのでやらなくてもOK):
rake gems:unpack GEM=hoptoad_notifier
5. Configure the notifier(設定ファイルの生成):
script/generate hoptoad --api-key apikey123apikey123apikey123apikey123
6. Look for the exception to appear in your errors list(例外はHoptoadのエラーリストを見えると表示されるよー).
準備がこれで完了しました. あとは実際に通知をさせればHoptoadにエラーリストが表示されます.
エラーが見れるかテスト
プロジェクトルートで以下のコマンドを実行
rake hoptoad:test
$ rake hoptoad:test (in /Users/satoruk/Developments/foo) ** [Hoptoad] Notifier 2.3.0 ready to catch errors Configuration: http_open_timeout: 2 proxy_host: nil notifier_url: "http://hoptoadapp.com" port: 80 proxy_user: nil http_read_timeout: 5 api_key: "apikey123apikey123apikey123apikey123" notifier_version: "2.3.0" ignore: ["ActiveRecord::RecordNotFound", "ActionController::Rou development_environments: [] params_filters: ["password", "password_confirmation"] proxy_pass: nil ignore_by_filters: [] secure: false framework: "Rails: 2.3.8" project_root: "/Users/satoruk/Developments/foo" backtrace_filters: [#<Proc:0x00000001017033c8@/opt/local/lib/ruby/gems/1.8 proxy_port: nil ignore_user_agent: [] protocol: "http" host: "hoptoadapp.com" development_lookup: true notifier_name: "Hoptoad Notifier" environment_name: "development" Setting up the Controller. Processing request. Raising 'HoptoadTestingException' to simulate application failure. Sending request to http://hoptoadapp.com/notifier_api/v2/notices/: XMLのコードがずらりと ** [Hoptoad] Success: Net::HTTPOK ** [Hoptoad] Environment Info: [Ruby: 1.8.7] [Rails: 2.3.8] [Env: development] ** [Hoptoad] Response from Hoptoad: <?xml version="1.0" encoding="UTF-8"?> <notice> <error-id type="integer">1986027</error-id> <url>http://bar.hoptoadapp.com/errors/0000000/notices/00000000</url> <id type="integer">000000</id> </notice> Rendered layouts/_head (2.6ms) Rendered layouts/_groval_header (1.5ms) Rendered layouts/_copyright (0.8ms) Rendered layouts/_footer (7.6ms)
っでHoptoad上で見てみるとエラーがでてますと.
各エラーはUnresolvedとResolvedのステータスが変えられるのでエラーを調べ終わったらResolvedにしてく感じです.
ハマったところ
このまえのエントリーのエラー処理でrescue_action_in_publicを上書きしてたので、Hoptoadが動きませんでした. コードを読むとHoptoadはrescue_action_in_publicを利用しているので、以下のような回避策が必要です.
def rescue_action_in_public(exception) #:doc: unless hoptoad_ignore_user_agent? HoptoadNotifier.notify_or_ignore(exception, hoptoad_request_data) end # : # : # : end
Hoptoadがまともに動いてなかったとき、「rake hoptoad:test」の結果に以下のような内容があります.
Raising 'HoptoadTestingException' to simulate application failure.