甘いものが好きです

iOS App開発時に感じた疑問や課題、その他の雑感などを書いていきます。

警告「PerformSelector may cause a leak because its selector is unknown」への対処

セレクタを指定してオブジェクトにメッセージを送る処理について。

SEL sel = @selector(doSomething);
if ([obj respondsToSelector:sel]) {
    [obj performSelector:sel];
}

このようなコードをビルドすると、ARC有効時にperformSelector:の行について次の警告が出る。

PerformSelector may cause a leak because its selector is unknown

指示子@selectorによりセレクタを指定するのではなくSEL型変数を使用する場合にのみ警告が出る。実際、次のような場合には警告は出ない。

if ([obj respondsToSelector:@selector(doSomething)]) {
    [obj doSomething];
}
if ([obj respondsToSelector:@selector(doSomething)]) {
    [obj performSelector:@selector(doSomething)];
}

原因

この警告が出る原因に関するAppleへの問い合わせ結果が上記記事に記載されていたのを発見。参考になりました。ありがとうございます。

 いきなり、-performSelector:でワーニング出やがるし〜

 結局、2つあるインシデントの1つを使ってAppleに問い合わせたら「-performSelector:からの返り値をコントロールできなくてワーニング出るんだよ。

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:ac];
#pragma clang diagnostic pop

 で無視するようにしちゃってー。」て返答が返ってきますた。まあ、結局元々のToll-free bridge用の対応でいけるって事みたいです。
 正式回答なんで、皆さん、安心して上記対応でいきましょう。

対処方法

以下のページを参考にしつつ対処方法を整理する。*1

方法1: 警告を局所的に無視する

次のように警告の対象となる箇所を挟み込む形で#pragma指令を書くことにより、警告の種類を指定して局所的に警告を無視するように設定することができる。*2

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [obj performSelector:sel];
#pragma clang diagnostic pop
方法2: ランタイム関数を使用する

performSelector:ではなくランタイム関数objc_msgSend()によりメッセージを送信すると警告が出ない。

#import <objc/message.h> // objc_msgSend()を使用するために必要

// 中略

if ([obj respondsToSelector:sel]) {
    objc_msgSend(obj, sel);
}

方法3: performSelector:withObject:afterDelay:を使用する

performSelector:のかわりにperformSelector:withObject:afterDelay:を使うと警告が出ない。performSelector:withObject:afterDelay:が値を返さないことと、「performSelector:からの返り値をコントロールできなくてワーニング出る」というAppleからの回答内容を考えると、この結果は当然のものといえるだろう。

if ([obj respondsToSelector:sel]) {
    [obj performSelector:sel withObject:nil afterDelay:0.0f];
}

*1:確認時の環境は次のとおり。OS X 10.8.2、Xcode 4.5.2 (4G2008a)、Base SDK: iOS 6.0、Deployment Target: iOS 5.0、ARC: 有効。

*2:Apple LLVM compiler 4.1で確認。