31712


いつもお世話になっております。  
文字列からクラス内の関数を呼び出し、アニメーションをさせたいと考えております。  
悪戦苦闘しており、皆様のお力を借りたいと思いました。

エラー内容は以下になります。  
Cast from 'Unmanaged?' to unrelated type 'SKTexture' always fails

ソースは抜粋して表記しております。

[code]

    class sample: NSObject {
        func hoge() -> [SKTexture] {
            return [
                hoge_0001(),
                hoge_0002(),
            ]
        }
    }
    
[/code]

ソースは抜粋して表記しております。

[code]

    self.aa = sample()
    (略)
    let o = self.aa.perform(Selector("hoge"))
    a = SKAction.animate(with: [o as! SKTexture], timePerFrame: 0.033)
    (略)
    self.node.run(a, withKey: "hoge")
    
[/code]

どうかよろしくお願い致します。


CC BY-SA 3.0
2017-01-08T12:45:33.430
31709
2017-01-08T13:38:48.787
20142
question
swift swift2 swift3
文字列からクラス名や関数名を生成し実行させたい


Swift 2とSwift 3両方のタグを示されていますが、Swift 3でコード例を書かれていますので、回答はSwift
3で書かせていただきます。また、Swiftのベテランプログラマーたちの間では「型名は(型名だけ)大文字で始める」と言うコーディングルールがよく守られていますので、そこも書き換えさせていただきました。必要に応じ適宜読み替えてください。

* * *

まずは、質問中にある

> **Cast from 'Unmanaged?' to unrelated type 'SKTexture' always fails**

ですが、`NSObjectProtocol`に定義されている`perform(_:)`メソッドの戻り値は`Unmanaged<AnyObject>!`型です。

#
[`perform(_:)`](https://developer.apple.com/reference/objectivec/nsobjectprotocol/1418867-perform)

> ## Declaration
[code]

>     func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>!
>  
[/code]

`Unmanaged<T>`型の値は`as`系のキャスト(`as`, `as?`,
`as!`)では普通のデータ型には変換できません。可及的速やかに`takeRetainedValue()`または`takeUnretainedValue()`を呼んで、中身の`T`型の値を取り出す必要があります。どちらかよくわからない場合には「普通は`takeUnretainedValue()`で良いはず」と覚えて、後からInstruments等でメモリリークを調べるなどしてください。

[code]

    let selName = "hoge"
    let o = self.aa.perform(Selector(selName)).takeUnretainedValue()
    
[/code]

これで`o`は、`AnyObject`型になりますので、`as!`で普通のクラス型にダウンキャストできます。

* * *

ただし、もう一つ。

ご質問には書かれていませんが、次の行中のこの部分。

[code]

    [o as! SKTexture]
    
[/code]

あなたの書かれた例の中の、`hoge()`メソッドは、`[SKTexture]`を返すはずです。つまり`o`の中身は、「`SKTexture`のArray」なので、(単独の)`SKTexture`型にキャストしても必ず失敗します。おそらく、`o
as! [SKTexture]`の書き間違いでしょう。

[code]

    let a = SKAction.animate(with: o as! [SKTexture], timePerFrame: 0.033)
    
[/code]

SpriteKit絡みのコードを実際に実行させるのは手間がかかるので、コード上で気になる点を修正しただけですが、お試しください。

* * *

ただ、あなたの書かれたサンプルコードを見る限りでは、わざわざセレクターを介して動的にメソッドを呼び出す必要性が感じられません。たとえば、辞書型など使ってみてはいかがでしょうか?

[code]

    class Sample: NSObject {
        let textureDict: [String: [SKTexture]] = [
            "hoge": [Hoge_0001(), Hoge_0002()],
            //...
        ]
    }
    
[/code]

(ちょっとしたサンプルコードを書くときでもわかりやすい名前を心がけられた方が良いですよ。特にQ&Aサイトを利用するときでも、わかりやすい名前をつけておくことで、質問者の意図が伝わりやすくなります。`aa`と`Sample`は、どう使うのかわからないので、書き換えられませんでしたが。)

[code]

        self.aa = Sample()
        //(略)
        let textureName = "hoge"
        if let textures = self.aa.textureDict[textureName] {
            let animAction = SKAction.animate(with: textures, timePerFrame: 0.033)
            //(略)
            self.node.run(animAction, withKey: "hoge")
        }
    
[/code]

より型安全になり、実行時クラッシュの可能性のある`as!`なんて危険なキャストを使う必要も無くなりますし、`takeRetainedValue()`と`takeUnretainedValue()`のどちらを使うんだ(間違えるとメモリリークや実行時クラッシュに結びつく)と迷う必要も無くなります。

是非ご検討ください。


CC BY-SA 3.0
2017-01-08T13:38:48.787
31712
2017-01-08T13:38:48.787
2020-06-17T08:14:45.997
-1
13972
31709
answer