私は、エミッタノードと物理演算を使用してゲームプレイを強化し、新しいiOS7とスプライトキットを対象としたiOSゲームを構築しています。アプリの開発中に深刻な問題が発生しました。シーン、ノード、エフェクトを作成しましたが、完了してメイン画面に戻る必要がある場合、これらのリソースによって割り当てられたすべてのメモリをどのように解放しますか?
理想的には、ARCはすべてを解放し、アプリケーションはシーンを作成する前のメモリ消費レベルに戻す必要がありますが、これは起こりません。
ビューのdeallocメソッドとして、次のコードを追加しました。これは、シーンを描画し、閉じた(削除した)ときにすべてを削除する役割を果たします。
- (void) dealloc
{
if (scene != nil)
{
[scene setPaused:YES];
[scene removeAllActions];
[scene removeAllChildren];
scene = nil;
[((SKView *)sceneView) presentScene:nil];
sceneView = nil;
}
}
この件に関して何か助けていただければ幸いです。
Sprite Kitで多くのメモリの問題が発生し、テクニカルサポートチケットを使用して情報を取得しました。これはここに関連している可能性があります。新しいSKSceneを起動すると、前のSKSceneが使用していたすべてのメモリが完全に解放されるかどうかを尋ねていました。私はこれを見つけました:
+ textureWithImageNamed:によって割り当てられた基になるメモリは、新しいSKSceneに切り替えるときに解放される場合と解放されない場合があります(通常は解放されません)。あなたはそれに頼ることはできません。 iOSは、適切であると判断した場合、たとえばメモリ不足の状態を検出した場合に、+ textureWithImageNamed:または+ imageNamed:によってキャッシュされたメモリを解放します。
テクスチャを使い終わったらすぐにメモリを解放したい場合は、+ textureWithImageNamed:/ + imageNamed:の使用を避ける必要があります。 SKTexturesを作成する別の方法は、最初に+ imageWithContentsOfFile:を使用してUIImagesを作成し、次にSKTexture/+ textureWithImage:(UIImage *)を呼び出して結果のUIImageオブジェクトからSKTexturesを作成することです。
これがここで役立つかどうかはわかりません。
そのコードはすべて不要です。コードにメモリリークや保持サイクルがない場合、スプライトキットビューを放すと、すべてがメモリからクリアされます。
内部的には、Sprite Kitはキャッシュメカニズムを採用していますが、それを制御する方法はありません。また、適切に実装されているかどうかを制御する必要もありません(これは安全に想定できます)。
これがInstrumentsに表示されているものではない場合は、保持サイクル、リークを確認してください。シーンとビューのdeallocが呼び出されることを確認します。ビュー、シーン、または他のノードへの強い参照が他のオブジェクト(特にシングルトンおよびグローバル変数)に残っていないことを確認してください。
これと数日間戦った後、鍵は実際には次のとおりでした。[sceneView presentScene:nil];またはSwiftの場合:sceneView.presentScene(nil)
これはviewDidDisappearで実行できます。これがなければ、あなたの見解は、解雇された後でも、愛する人生のシーンにとどまり、記憶をかみ砕き続けます。
Swift:
私の個人的な経験では、Xcodeインスツルメントの助けを借りて解決しました。まず、割り当てやリークよりも、メモリの大幅な増加を即座に示したアクティビティモニターを使用しました。
しかし、便利な方法もあります。
deinit {
print("\n THE SCENE \(type(of:self)) WAS REMOVED FROM MEMORY (DEINIT) \n")
}
これは、シーンを削除するたびにdeinit
が呼び出されたかどうかを確認するためのもう1つのヘルプです。
クラス内にscene
またはparent
への強い参照はありません。誰かがいる場合は、たとえば次のように弱くする必要があります。
weak var parentScene:SKScene?
プロトコルについても同じですが、プロパティclass
を使用して、この例のように弱いと宣言できます。
protocol ResumeBtnSelectorDelegate: class {
func didPressResumeBtn(resumeBtn:SKSpriteNode)
}
weak var resumeBtnDelegate:ResumeBtnSelectorDelegate?
[〜#〜] arc [〜#〜]必要なすべての作業を行いますただし、プロパティ(初期化、ブロック)を正しく書き込むのを忘れたと思われる場合。 。)私はこのようないくつかの関数も使用しましたデバッグフェーズ用:
func cleanScene() {
if let s = self.view?.scene {
NotificationCenter.default.removeObserver(self)
self.children
.forEach {
$0.removeAllActions()
$0.removeAllChildren()
$0.removeFromParent()
}
s.removeAllActions()
s.removeAllChildren()
s.removeFromParent()
}
}
override func willMove(from view: SKView) {
cleanScene()
self.removeAllActions()
self.removeAllChildren()
}
@ user2857148のような同様の問題が発生しました。 VC with:
[self presentViewController:myViewController animated:YES completion:nil];
の中に @implementation myViewController
私が持っていた:
- (void)viewDidLayoutSubviews
{
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
self.ballonMGScene = [[MBDBallonMiniGame alloc] initWithSize:skView.bounds.size andBallonImageNames:self.ballonObjectsArray];
self.ballonMGScene.parentVC = self;
self.ballonMGScene.scaleMode = SKSceneScaleModeAspectFill;
self.ballonMGScene.physicsWorld.gravity = CGVectorMake(0, 0);
// Present the scene.
[skView presentScene:self.ballonMGScene];
}
問題は:
self.ballonMGScene.parentVC = self;
以来:
@interface MBDBallonMiniGame : SKScene <SKPhysicsContactDelegate>
parentVCはstrongで宣言されました:
@property (nonatomic,strong) WBMMiniGameVCTemplate *parentVC;
解決策1:解決策1:
に変更します:
@property (nonatomic,weak) WBMMiniGameVCTemplate *parentVC;
私のために問題を解決しました。
説明:myViewController
であったparentVC(UIViewController
)への参照がどこかに保管されています。このVCはSKSceneを強く参照していたので、一緒に保存されました。このSKSceneからのコンソール出力も、まだアクティブであるように見えました。なぜこれが起こったのかについての私の最高の質問は私が最も強い指針を持っていること。
解決策2:解決策2:
私のmyViewController
の下:
- (void)viewDidDisappear:(BOOL)animated
私は呼びました :
self.ballonMGScene.parentVC = nil;
現在のVC(myViewController
)を離れるときに、ポインタをnilに設定し、メモリとそれに伴うすべてのものを削除します。
これらの2つのソリューションは私のために働いた。デバッガーでテストしました。メモリ消費量は正しく上下しました。
これが問題と解決策の理解に役立つことを願っています。
シーンを削除する前に、sceneViewを保持してみてください。
-(void)dealloc
{
[sceneView presentScene:nil];
[sceneView release];
[super dealloc];
}
いくつかの手順が必要でしたが、問題は完全に解決しました。
1)My ViewControlで、すべての子を強制的に破棄するメソッドを作成しました。
-(void)destroyAllSub:(SKNode*)node
{
if(node == nil) return;
if(![node isKindOfClass:[SKNode class]]) return;
[node removeAllActions];
for (SKNode *subNode in node.children) {
[self destroyAllSub:subNode];
}
[node removeAllChildren];
}
2)シーンで強力なプロトコルを作成し、それをViewControlで参照し、シーンも強力だったため、次のようにすべての参照を破棄しました。
[self.mainScene.view presentScene:nil]; //mainScene: the name of the Scene pointer
self.mainScene.myProt = nil; //myProt: The name of the strong protocol
@autoreleasepool {
[self destroyAllSub:self.mainScene];
self.mainScene = nil;
}