XcodeのUserScript

Cocoaの日々: Xcode のマクロ定義
こちらの記事をみて、昔 cocoa 勉強会でちょこっと紹介した内容に似たようなのがあったのを思い出しました。
久しぶりなのでブログに書いてみようかと思いました。

Objective-C 2.0 の プロパティ定義文を挿入する UserScript

さきの記事ではテキストマクロとしてプロパティ定義文を挿入して実際のプロパティ名は自分で入力するような形をとっていました。

私はもっと怠け者なので文字入力なしで済ましたいと思ったので UserScript にしました。

UserScript とは

これ。

ちょっとしたことができる仕組みです。

デフォルトで入っているのものでおそらくみんながお世話になっているだろう
コメントアウトのショートカット Cmd+/ は実は Perl で書かれた UserScript が実行されています。

何ができる?

こんなのが実行できます。

入力としては以下のものを受けられます。

  • 選択文字列
  • ドキュメント全体

出力はこんな感じ。

  • 新規書類で開く
  • クリップボードに入れる
  • 選択部分の置き換え
  • 選択部分の後に挿入
  • ドキュメントの内容を置き換え
  • ドキュメントの内容の後に挿入
  • 警告の表示

メニューから " Edit User Scrippts ..." を選べば編集画面が表示されます。
ショートカットキーもここでわりあてられます。

UserScript でプロパティ定義文を挿入する

やりたいことは

以下の2パターン作ったことがありますが、今回はクリップボード拡張を導入していない人向け。

@property を挿入する UserScript
#!/usr/bin/env ruby

lines = `pbpaste`
lines.each { |line|
  if line.match(/([_A-Za-z][_A-Za-z0-9]*\s*)+([\s\*]+)([_A-Za-z][_A-Za-z0-9]*);/)
  
    klas = $1
    star = $2
    ivar = $3  
  
    if klas != nil
      if /\ANSString/ =~ klas
        print "@property (nonatomic, copy) #{klas.strip} #{star.strip}#{ivar};\n"
      elsif /\Aid/ =~ klas
        if /delegate/ =~ ivar
          print "@property (assign) #{klas.strip} #{star.strip}#{ivar};\n"          
        else
          print "@property (nonatomic, retain) #{klas.strip} #{star.strip}#{ivar};\n"
        end
      elsif /\*/ =~ star
        print "@property (nonatomic, retain) #{klas.strip} #{star.strip}#{ivar};\n"
      else
        print "@property (assign) #{klas.strip} #{star.strip}#{ivar};\n"
      end
    elsif star == nil
      
    end
  end
}

使い方は
インスタンス変数定義部分をまとめてコピーして挿入したい場所でこのスクリプトを実行。
すると

  • NSString は @property (nonatomic, copy)
  • delegate という変数名は @property (assign)
  • その他オブジェクトは @property (nonatomic, retain)
  • オブジェクト以外は @property (assign)

として挿入します。

@synthesize を挿入する UserScript
#!/usr/bin/env ruby
lines = `pbpaste`
lines.each { |line|
  if line.match(/([_A-Za-z][_A-Za-z0-9]*\s*)+([\s\*]+)([_A-Za-z][_A-Za-z0-9]*);/)
  
    klas = $1
    star = $2
    ivar = $3
    print "@synthesize #{ivar};\n"
  end
}

使い方は
インスタンス変数定義部分をまとめてコピーして挿入したい場所でこのスクリプトを実行。

release を挿入する UserScript
#!/usr/bin/env ruby

lines = `pbpaste`

def releaseAndNil(variable)
  print "[#{variable} release], #{variable} = nil;\n"
end

lines.each { |line|
  if line.match(/([_A-Za-z][_A-Za-z0-9]*\s*)+([\s\*]+)([_A-Za-z][_A-Za-z0-9]*);/)
  
    klas = $1
    star = $2
    ivar = $3  
  
    if klas != nil
      if /\ANSString/ =~ klas
        releaseAndNil(ivar)
      elsif /\Aid/ =~ klas
        if /delegate/ =~ ivar
          print "delegate = nil;"          
        else
          releaseAndNil(ivar)
        end
      elsif /\*/ =~ star
        releaseAndNil(ivar)
      end
    end    
  end
}

使い方は
インスタンス変数定義部分をまとめてコピーして挿入したい場所でこのスクリプトを実行。
すると

  • delegate という変数名の場合は delegate = nil;
  • その他オブジェクトは [変数名 release], 変数名 = nil;
  • オブジェクト以外の場合は なにも出力しない

として挿入します。

保存方法

今回紹介したスクリプトは Output を ”Insert after Selection” として保存してください。

使い方の流れとしてはこんな感じで使うとインスタンス変数のリリース漏れなどを起こしにくいと思います。

  1. インスタンス変数をずらっと定義
  2. 定義部分をコピー
  3. プロパティ定義を挿入したい箇所で「@property を挿入する UserScript」を実行
  4. 実行ファイルを開いて@synthesize を挿入したい箇所で「@synthesize を挿入する UserScript」を実行
  5. dealloc メソッド内で「release を挿入する UserScript」を実行

その他

今回のスクリプトは普段使っていて特に問題はありませんでしたが、結構適当なのでコーディングスタイルが違う人は使えない代物かもしれません。ご利用は自己責任でお願いします。