I have problem with AdMob and sprite kit.
Everytime ad will show or load, it will increase memory usage a lot!
When EndGameScene open it will call "showAd", and when replay button is pressed "loadAd".
Ads works well.
When game starts memory usage is something like 50mb and few ads later it is at least 70-100mb!
So what i do wrong:
GameViewController.m
- (void)viewDidLoad{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"showAd" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"loadAd" object:nil];
self.interstitial = [self createAndLoadInterstitial];
self.interstitial = [[GADInterstitial alloc] initWithAdUnitID:#"ca-app-pub-<id>"];
GADRequest *request = [GADRequest request];
// Requests test ads on test devices.
request.testDevices = #[#"0000000"];
[self.interstitial loadRequest:request];
//etc
}
- (void)handleNotification:(NSNotification *)notification{
if ([notification.name isEqualToString:#"showAd"]) {
if (self.interstitial.isReady) {
[ self.interstitial presentFromRootViewController:self];
}
else {
}
}
if ([notification.name isEqualToString:#"loadAd"]) {
self.interstitial = [self createAndLoadInterstitial];
}
}
- (GADInterstitial *)createAndLoadInterstitial {
GADInterstitial *interstitial =
[[GADInterstitial alloc] initWithAdUnitID:#"ca-app-pub-<id>"];
interstitial.delegate = self;
[interstitial loadRequest:[GADRequest request]];
return interstitial;}
Thanks!
You are creating everytime a new GADInterstitial object, which starts a load request. I guess because it is waiting for an answer it will never be released from memory again.
A better way would be to store the GADInterstitial in a global property and just call a new loadRequest, if you need a new ad.
Related
I'm working on application for video streaming. It is built with ARC for iOS5.
To display view I use MPMoviePlayerViewController this way:
.h
#interface EpisodesTableViewController : UITableViewController<EpisodeUrlResolverDelegate> {
NSTimeInterval playbackTime;
EpisodeUrlResolver *episodeUrlResolver;
}
#property (strong, nonatomic) MPMoviePlayerViewController *player;
#end
.m
#implementation EpisodesTableViewController
#synthesize episodes, player;
- (void)viewDidLoad
{
[super viewDidLoad];
episodeUrlResolver = [[Soap4MeEpisodeUrlResolver alloc] init];
[episodeUrlResolver setDelegate:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterForeground) name:#"WillEnterForeground" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterBackground) name:#"WillEnterBackground" object:nil];
}
- (void)viewDidUnload
{
episodeUrlResolver = nil;
player = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"WillEnterForeground" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"WillEnterBackground" object:nil];
[super viewDidUnload];
}
- (void)url:(NSURL *)url WasResolvedForEpisode:(Episode *)episode {
player = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
player.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
player.moviePlayer.allowsAirPlay = YES;
[self presentModalViewController:player animated:YES];
}
- (void)willEnterBackground {
if (player) {
playbackTime = player.moviePlayer.currentPlaybackTime;
}
}
- (void)willEnterForeground {
if (!player)
return;
if(player.moviePlayer.playbackState == MPMoviePlaybackStateInterrupted || player.moviePlayer.playbackState == MPMoviePlaybackStateStopped || player.moviePlayer.playbackState == MPMoviePlaybackStatePaused)
{
[self continuePlayback];
}
}
- (void)continuePlayback {
[self presentModalViewController:player animated:YES];
[player.moviePlayer setInitialPlaybackTime:playbackTime];
NSLog(#"%f", player.moviePlayer.initialPlaybackTime);
[player.moviePlayer play];
}
Few words about presented code:
when url for video is resolved I create the MPMoviePlayerViewController object for video playback. Playback is started. Then when app is going background with home button MPMoviePlayerViewController automatically pauses playback and dissapears from screen (weird behavior, as for me). Thats why I have to save playback time when app is going background and show existing MPMoviePlayerViewController again with restored playback time when app is back to foreground.
It works fine when user presses home button and then is going back to application.
But it doesn't work when user locks the device. WillEnterBackground notification is fired when
applicationWillEnterForeground is called, so for locking event it is called too.
But in case when user locks the device MPMoviePlayerViewController wasn't being dismissed properly as in situation when user presses home button. It is hidden from the screen, but calling the code
[self presentModalViewController:player animated:YES];
throws the exception
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller <EpisodesTableViewController: 0x91b3950>.'
I tried to change the code to dismiss controller:
- (void)willEnterForeground {
if (!player)
return;
if(player.moviePlayer.playbackState == MPMoviePlaybackStateInterrupted || player.moviePlayer.playbackState == MPMoviePlaybackStateStopped || player.moviePlayer.playbackState == MPMoviePlaybackStatePaused)
{
if (self.presentedViewController) {
[self dismissViewControllerAnimated:YES completion:^{
[self continuePlayback];
}];
}
else {
[self continuePlayback];
}
}
}
Now it doesn't throw the exception, but displays warning
wait_fences: failed to receive reply: 10004003
Also completion block is never called.
How to properly to solve the background/foreground continue playing with modally displayed MPMoviePlayerViewController?
Thanks for replies.
I solved the problem.
Method -willEnterBackground should look like:
- (void)willEnterBackground {
if (player) {
playbackTime = player.moviePlayer.currentPlaybackTime;
[self dismissModalViewControllerAnimated:NO];
}
}
We have to dismiss MPMoviePlayerViewController manually when app is entering background mode.
Pay attention to use only
[self dismissModalViewControllerAnimated:NO];
instead of
[self dismissMoviePlayerViewControllerAnimated];
Thats because MPMoviePlayerViewController was shown with
[self presentModalViewController:player animated:YES];
I am developing a system to keep track of achievements and for that I use NSNotificationCenter. When an achievement is unlocked in a object in the app a notification is sent to JMAchievementHandler which sets a string to YES and saves the progress in NSUserDefaults. My problem is that the notifications are not working probably. Here is my code in JMAchievementHandler:
- (id)init {
self = [super init];
if (self) {
//Set strings to NO
achievementOne = #"NO";
achievementTwo = #"NO";
achievementThree = #"NO";
achievementFour = #"NO";
//Add observers
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationWithName:) name:#"achievement1" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationWithName) name:#"achievement2" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationWithName) name:#"achievement3" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationWithName) name:#"achievement4" object:nil];
//Add observer to observe delegate methods
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationFromDelegate:) name:#"notificationLaunch" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receiveNotificationFromDelegate:) name:#"notificationEnterBackground" object:nil];
}
return self;
}
- (void)receiveNotificationWithName:(NSNotification *)notification {
if ([[notification name] isEqualToString:#"achievement1"] || [achievementOne isEqualToString:#"NO"]) {
//unlock achievement
NSLog(#"%# is unlocked", [notification name]);
achievementOne = #"YES";
}
else if ([[notification name] isEqualToString:#"achievement2"] || [achievementTwo isEqualToString:#"NO"]) {
//unlock achievement
NSLog(#"%# is unlocked", [notification name]);
achievementTwo = #"YES";
}
else if ([[notification name] isEqualToString:#"achievement3"] || [achievementThree isEqualToString:#"NO"]) {
//unlock achievement
NSLog(#"%# is unlocked", [notification name]);
achievementThree = #"YES";
}
else if ([[notification name] isEqualToString:#"achievement4"] || [achievementFour isEqualToString:#"NO"]) {
//unlock achievement
NSLog(#"%# is unlocked", [notification name]);
achievementFour = #"YES";
}
}
- (void)receiveNotificationFromDelegate:(NSNotification *)notificationDelegate
{
if ([[notificationDelegate name] isEqualToString:#"notificationLaunch"]) {
[self loadDataOnLaunch];
}
else if ([[notificationDelegate name] isEqualToString:#"notificationEnterBackground"]) {
[self saveDataOnExit];
}
}
- (void)loadDataOnLaunch
{
NSLog(#"loadDataOnLaunch");
}
- (void)saveDataOnExit
{
NSLog(#"saveDataOnExit");
}
When I try to post a notification the NSLogs are not called. I use the following code to send notifications from my AppDelegate and ViewController.
- (void)achievement1ButtonPressed:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:#"achievement1" object:self userInfo:nil];
}
I hope you guys can help me out. Thanks a lot
Jonas
Do you even have a method named receiveNotificationWithName? When you entered the code, the autocomplete should have barfed on that, offering receiveNotificationWithName: instead (unless you wrote it first, without NSNotification*, then added it later...
Anyway, there is a big difference between the two. Also, your handler is buggy. You should revisit that code.
There are lots of reasons to prefer blocks, and this points to one of them. You can just put your code right in the registration, and not worry about giving the wrong selector.
[[NSNotificationCenter defaultCenter] addObserverForName:#"achievement1"
object:nil
queue:nil
usingBlock:^{
// Handle achievement1 right in this little block.
}];
These look like one-shot notifications, so if you want to unregister the notification handler after it runs one time, do this (note the __block).
__block id achievement1 = [[NSNotificationCenter defaultCenter]
addObserverForName:#"achievement1 ":^{
object:nil
queue:nil
usingBlock:^{
// Handle achievement1 right in this little block.
// Remove observer from notification center
[[NSNotificationCenter defaultCenter] removeObserver:achievement1];
}];
All of this looks like it should work (besides the colon, like Andrea mentioned). Is it possible that your button callback isn't being called for a similar reason? I would add an NSLog in your achievement1ButtonPressed: method to make sure.
What I can see is that only one of your addObesrver method call the right selector with the ":", thus the right method signature.
I'm trying to play an HTTP live streaming video using a MPMoviePlayerController object, inside a modal view controller. If I run the code like this, everything works fine. I get a MPMoviePlaybackStatePlaying notification, and the video plays.
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
NSURL *url = [NSURL URLWithString:#"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"];
self.moviePlayerController = [[[MPMoviePlayerController alloc] initWithContentURL:url] autorelease];
self.moviePlayerController.view.frame = CGRectMake(0,0,320,416);
self.moviePlayerController.controlStyle = MPMovieControlStyleNone;
self.moviePlayerController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.moviePlayerController.view];
self.moviePlayerController.fullscreen = NO;
[self.moviePlayerController play];
}
I would prefer to use the prepareToPlay method, so that I can show a loading indicator, and for a nicer experience. However, I don't seem to be able to get this working. No MPMoviePlayerPlaybackStateDidChangeNotification ever gets called, and the stream doesn't play. The activity indicator just sits there spinning forever.
I've confirmed that prepareToPlay does get called, but nothing else seems to happen after that. I've also confirmed that this code works fine with a local movie file. It just seems to be with HTTP Live Streaming which fails. Can anyone see what I'm doing wrong?
- (void)viewDidLoad {
[super viewDidLoad];
[self.activityIndicator startAnimating];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
NSURL *url = [NSURL URLWithString:#"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"];
self.moviePlayerController = [[[MPMoviePlayerController alloc] initWithContentURL:url] autorelease];
[self registerForMovieNotifications];
}
- (void)registerForMovieNotifications {
//movie is ready to play notifications
if ([self.moviePlayerController respondsToSelector:#selector(loadState)]) {
//this is a 3.2+ device
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerLoadStateChanged:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
self.moviePlayerController.movieSourceType = MPMovieSourceTypeStreaming;
[self.moviePlayerController prepareToPlay];
} else {
//pre-3.2 device
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePreloadDidFinish:) name:MPMoviePlayerContentPreloadDidFinishNotification object:nil];
}
//movie has finished notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
}
- (void) moviePlayerPlaybackStateDidChange:(NSNotification*)notification {
NSLog(#"playbackDidChanged");
MPMoviePlayerController *moviePlayer = notification.object;
MPMoviePlaybackState playbackState = moviePlayer.playbackState;
if(playbackState == MPMoviePlaybackStateStopped) {
NSLog(#"MPMoviePlaybackStateStopped");
} else if(playbackState == MPMoviePlaybackStatePlaying) {
NSLog(#"MPMoviePlaybackStatePlaying");
} else if(playbackState == MPMoviePlaybackStatePaused) {
NSLog(#"MPMoviePlaybackStatePaused");
} else if(playbackState == MPMoviePlaybackStateInterrupted) {
NSLog(#"MPMoviePlaybackStateInterrupted");
} else if(playbackState == MPMoviePlaybackStateSeekingForward) {
NSLog(#"MPMoviePlaybackStateSeekingForward");
} else if(playbackState == MPMoviePlaybackStateSeekingBackward) {
NSLog(#"MPMoviePlaybackStateSeekingBackward");
}
}
//3.2+ devices
- (void)moviePlayerLoadStateChanged:(NSNotification*)notification {
NSLog(#"load state changed");
//unless state is unknown, start playback
if ([self.moviePlayerController loadState] != MPMovieLoadStateUnknown) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
self.moviePlayerController.view.frame = CGRectMake(0,0,320,416);
self.moviePlayerController.controlStyle = MPMovieControlStyleNone;
self.moviePlayerController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.moviePlayerController.view];
self.moviePlayerController.fullscreen = NO;
[self.moviePlayerController play];
}
}
//pre 3.2 devices
- (void) moviePreloadDidFinish:(NSNotification*)notification {
// Remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerContentPreloadDidFinishNotification object:nil];
[self.moviePlayerController play];
}
I don't know why, but I was able to solve this by moving the line self.moviePlayerController.controlStyle = MPMovieControlStyleNone; out of the moviePlayerLoadStateChanged: method, and into the viewDidLoad method.
Anyone have any ideas why this made a difference?
I'm trying the following:
Whenever an OAuth token changes, I want to update some of my view controllers. The view controllers I want to update all inherit from a view controller. This view controller will listen for notifications whenever a new access token is set or whenever the access token is cleared. Whenever the access token is set or cleared, I want to set a selector to a method that should be executed once the view controller will be displayed (i.e. on -viewWillAppear:).
Somehow the blocks inside the addObserverForName:object:queue:usingBlock: don't seem to get called. I can't get it to log even. Because of this the selectors never change. From what I've read using the __block attribute on an ivar should allow the ivar to be changed from within a block.
#interface SDViewController ()
- (void)reloadData;
- (void)clearData;
#end
#implementation SDViewController
{
__block SEL selectorOnViewWillAppear;
}
- (id)initWithDataSource:(id <SDViewControllerDataSource>)dataSource
{
self = [super init];
if (self)
{
selectorOnViewWillAppear = #selector(reloadData);
}
return self;
}
- (void)viewDidLoad
{
NSLog(#"view did load");
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserverForName:kAccessTokenChangedNotification object:self queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *note) {
NSLog(#"selector: test1");
selectorOnViewWillAppear = #selector(reloadData);
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kAccessTokenClearedNotification object:self queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *note) {
NSLog(#"selector: test2");
selectorOnViewWillAppear = #selector(clearData);
}];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (selectorOnViewWillAppear)
[self performSelector:selectorOnViewWillAppear];
}
- (void)reloadData
{
NSLog(#"reloadData");
selectorOnViewWillAppear = nil;
}
- (void)clearData
{
NSLog(#"clearData");
selectorOnViewWillAppear = nil;
}
#end
Fixed it by changing the observer code from this:
[[NSNotificationCenter defaultCenter]
addObserverForName:kAccessTokenClearedNotification
object:self
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
NSLog(#"selector: test2");
selectorOnViewWillAppear = #selector(clearData);
}];
To this:
[[NSNotificationCenter defaultCenter]
addObserverForName:kAccessTokenClearedNotification
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
NSLog(#"selector: test2");
selectorOnViewWillAppear = #selector(clearData);
}];
The Apple documentation related to this issue is found here. The object in the
-addObserverForName:object:queue:usingBlock: method should be the object that generates the notifications. Setting the object to nil fixed the issue.
I am facing a problem in ipad video incorporating. My code works fine I mean it plays the video, but once the video reaches to its end. The callback method is not called.
This method is called when play video button is pressed.
-(IBAction) playVideo : (id) sender
{
[self initPlayingVideo:#"toyVid.mp4"];
}
This method handles the playing of video.
-(void) initPlayingVideo: (NSString *) videoFile
{
NSString *moviePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:videoFile];
theMovie = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:moviePath]];
theMovie.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
theMovie.moviePlayer.controlStyle = MPMovieControlStyleFullscreen;
[self.view addSubview:theMovie.view];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(videoPlayerDidFinishPlaying
name:MPMoviePlayerPlaybackDidFinishNotification
object:theMovie];
videoPlayer = [theMovie moviePlayer];
[videoPlayer play];
}
This is the callback method.
-(void) videoPlayerDidFinishPlaying: (NSNotification*)aNotification
{
theMovie = [aNotification object];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:theMovie.moviePlayer];
[videoPlayer stop];
[theMovie.moviePlayer release];
[videoPlayer release];
[theMovie.view removeFromSuperview];
}
Where I am doing mistake? Please guide.
Regards
Ranjan
Did you miss the : and ) in your selector? I guess ) may be your typo otherwise you cannot compile your codes. Your selection takes one parameter. It should be:
selector:#selector(videoPlayerDidFinishPlaying:)
That will match to your instance method. I guess you don't have one without parameter.