I am initializing AVPlayerItem with "initWithUrl:" method.
Issue is that when initialization is interrupted (e.g. connection is lost)
Let's say we have following:
self.avPlayerItem = [[AVPlayerItem alloc] initWithUrl:url];
1.
What should be done? What happens to instance of avPlayerItem when connection is lost meanwhile?
Is there some protocol to implement in order to identify if initialization was successful or not?
2.
What I have noticed is that once initialization is interrupted then next "successful" initialization of avPlayerItem doesn't have tracks at all (they are present when no interruption is done before)
To initialize avPlayerItem with tracks again from the source user needs to close and open the app again.
When dealing with code that relies on internet connection you have to make sure there are failsafes that kick in when something goes wrong, because at some point it will.
What should be done?
1) Perform all code that relies on internet connection on a background thread. You don't want to block up the UI.
2) If possible verify the downloaded file before using.
There are different ways of going about this but off the top of my head I think I would use NSURLConnection or similar API to download the file into a temp folder. ON A BACKGROUND THREAD.
When downloaded I would initialise an AVAsset using the temp URL. AVAsset has some nice properties like playable that will help you check that the file downloaded OK. (NSURLConnectionDelegate also has a method that notifies if there was an error downloading.)
If you've got this far then you can create an AVPlayerItem with your AVAsset and away you go. Remember to wipe the contents of your temp folder at some point if you're not hanging on to the downloaded content.
Remember that you want to play your file on the main thread but all other loading and checking is probably best done on a background thread; you definitely want to use NSURLConnection from a background thread.
Store the AVPlayerItem in an NSPurgeableData object first, and play from that; store the data object in an NSCache object to automatically evict the object from memory after it has played, or when the connection is dropped, and the former AVPlayerItem is replaced by a new one (all of these things you should be doing anyway, regardless of the particular problem you describe). Here's some code to get you started:
void (^cachePlayerItem)(AVPlayerItem *, NSCache *, NSString *) = ^(AVPlayerItem *playerItem, NSCache *cache, NSString *key) {
NSURL *fileURL = [(AVURLAsset *)playerItem.asset URL];
NSPurgeableData *data = [NSPurgeableData dataWithContentsOfURL:fileURL];
[data beginContentAccess];
[cache setObject:data forKey:key];
[data endContentAccess];
[data discardContentIfPossible];
};
Put this block anywhere in an implementation file, defining it in the header file with:
typedef void (^cachePlayerItemBlock)(AVPlayerItem *, NSCache *, NSString *);
Call it within a method with:
cachePlayerItem(playerItem, playerItems, phAsset.localIdentifier);
Whereas, playerItem is the AVPlayerItem, playerItems is the NSCache cache, and, depending on what kind of asset from which you are acquiring the AVPlayerItem, an identifier of some kind unique to it (or, in the example above, its associated asset).
By the way, I set up my caches in AppDelegate accordingly:
- (NSCache *)thumbnailCache {
__block NSCache *t = self->_thumbnailCache;
if (!t) {
t = [[NSCache alloc] init];
[t setName:#"Thumbnail Cache"];
[t setEvictsObjectsWithDiscardedContent:TRUE];
[t setCountLimit:self.assetsFetchResults.count];
self->_thumbnailCache = t;
}
return t;
}
This ensures not only global access to, but also one instance of, the cache, which is particularly important when caching AVPlayerItems, as they can be large in size.
To create a globally accessible cache in AppDelegate, add these to your header and implementation files, respectively:
+ (AppDelegate *)sharedAppDelegate;
...and:
+ (AppDelegate *)sharedAppDelegate
{
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
To reference the cache in other class files, import the AppDelegate header file, and, if desired, create a shortcut to the AppDelegate's sharedApplication method:
#import "AppDelegate.h"
#define AppDelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate])
Insodoing, the cache can be referenced by...:
AppDelegate.thumbnailCache
...instead of:
AppDelegate.sharedAppDelegate.thumbnailCache
Related
I was using cloud ant Db for my iOS app with name tasks_master with almost 1000 documents. When I tried to fetch data from cloud ant it's taking almost 30 sec to fetch data i have tried with below code.
- (NSURL*) replicatorURL {
AppDelegate *app = [[UIApplication sharedApplication]delegate];
NSString *db_name = #"tasks_master";
NSString *url = [NSString stringWithFormat:#"https://%#:%##%#.cloudant.com/%#",
username,
password,
username,db_name];
return [NSURL URLWithString:url];
}
- (void) sync:(UIViewController *)sender {
[self pullReplication:sender];
[self pushReplication:sender];
}
-(void)pullReplication:(UIViewController *)sender {
[self log:#"Starting pull replication"];
NSURL *url = [self replicatorURL];
AppDelegate *delegate1 = (AppDelegate *)[[UIApplication sharedApplication] delegate];
CDTReplicatorFactory *factory = delegate1.replicatorFactory;
CDTReplicator *replicator = [factory onewaySourceURI:url targetDatastore:delegate1.datastore];
[self startAndFollowReplicator:replicator label:#"pull"];
}
- (void) pushReplication:(UIViewController *)sender {
[self log:#"Starting push replication"];
NSURL *url = [self replicatorURL];
AppDelegate *delegate1 = (AppDelegate *)[[UIApplication sharedApplication] delegate];
CDTReplicatorFactory *factory = delegate1.replicatorFactory;
CDTReplicator *replicator = [factory onewaySourceDatastore:delegate1.datastore targetURI:url];
[self startAndFollowReplicator:replicator label:#"push"];
}
when i call tasks
-(void)fetchtasks{
[[[CDTTodoReplicator alloc]init]sync];
self.indexManager = [[CDTIndexManager alloc] initWithDatastore:self.datastore
error:&error];
indexName= [self.datastore ensureIndexed:#[#"originator",#"members",#"meeting_status"] withName:#"meeting_index"];
query=#{#"$and":#[#{#"meeting_status":#"Scheduled"}, #{#"$or":#[#{#"originator":app.userName},#{#"members":app.userName}]}]};
result = [self.datastore find:query];
}
can any one help me out how to fetch data faster .
I've written a little helper class to cut down on some of the boiler plate for this kind of thing. It's the one used in the video referenced above.
Cloudant.h https://gist.github.com/xpqz/f8b304353080c3963a45
Cloudant.m https://gist.github.com/xpqz/62b5267c250f04c30f9b
When you add documents to a Cloudant database, they become available through the so called primary index, also known as All Docs. This can easily be checked with 'curl' from the command line, e.g.:
% curl https://skruger.cloudant.com/routes/_all_docs
Every document you store will appear in the returned data from the primary index.
Design documents are there for a different purpose - they define secondary indexes, called views, mostly written as little javascript functions that define a map-reduce operation to expose some facet of your data. Views allow you to index your documents in additional ways to the document id that you get for free.
You can create your own views directly by creating design documents yourself, but client software accessing the database sometimes also create design documents and views automatically which sometimes can be confusing when you see stuff appearing under _design that you didn't explicitly create.
In summary: every document appear in the primary index, _all_docs. Design documents are there to hold javascript functions used to generate secondary indexes, called views. These can be created explicitly, or generated automatically 'behind your back' by client software.
More info:
Primary index and all docs
Design documents and secondary indexes
Search indexes and Lucene
Stefan
Good day.
I am developing an app to play multiple musics but I'm stuck where the music stop when I select share at the sidebar (I want the music to continue to play because user didn't pause it)
I am using api from RESideMenu and I suspect initRootController is the cause to made the music stop.
Someone suggested me to put the music at the appDelegate because the music might be deallocated when it switch view controller. However, I think that this is not a good way to do as I will later add on more musics with different image background and the architecture of the app will be very messy as I stock each music in ThemeObject and call the music in cafeViewController.
Is there a better way to do this?
This is my code >>> source.
I've check your repo and the sound seems to happen inside your ThemeObject and the only place where you create and link one of those is inside your CafeViewController. So every time the CafeViewController gets unloaded this will remove the only reference to your ThemeObject and it will be garbage collected. To check if the CafeViewController gets unloaded you could put a breakpoint inside this method:
- (void)dealloc {
// Just a line where you can put your breakpoint
}
The advice to put it inside the AppDelegate isn't completely backwards as indeed you would be better off to put it inside an object that is around all of the time. However to abuse AppDelegate as a dumping ground for all your centralised features is a bad practice. For simple apps you might be better off with a Singleton approach, where you always have one instance of an object and that object maintains itself during the existence of your application.
This is what a typical singleton looks like:
#interface ThemeManager : NSObject
#property NSArray *themes;
+ (id)sharedManager;
// Add other methods here
#end
#implementation ThemeManager
+ (id)sharedInstance {
static ThemeManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
ThemeObject *cafeTheme = [[ThemeObject alloc] initWithBackgroundImg:#"cafeBG.png" audio:#"cafeAudio"];
ThemeObject *cafeTheme1 = [[ThemeObject alloc] initWithBackgroundImg:#"cafeBG.png" audio:#"cafeAudio"];
// Create as much as you need in the same way
self.themes = #[cafeTheme, cafeTheme1]; // And add them to the array of themes
}
return self;
}
// Implement other methods
#end
So you never init directly but always ask for the shared instance by calling something like
MusicManager *manager = [MusicManager sharedInstance];
ThemeObject *firstTheme = (ThemeObject *) [manager.themes firstObject];
[firstTheme setAudioPlay];
You can start, pause, stop and change songs with this central object without worrying about the lifecycle of your ViewControllers. You also can start a song from for example CafeViewController and you can stop the song CafeViewController started from the HotelViewController when you start the Hotel song.
Consider the following example:
- (void)exampleFunction
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
SomeClass *classObject = [[SomeClass alloc] init];
[classObject someFunctionThatDoesBackgroundWorkAndCallsACompletionBlock:^(NSDictionary *results) {
SomeNeatObject *object = appDelegate.someNeatObject;
[object doSomeNeatStuff:results];
}];
}
Making an assumption that the completion block is executed on the main/UI thread to avoid additional insanity, here's what I'm unsure of:
Does the appDelegate variable get copied onto the stack, as per normal block rules when referencing variables inside the block?
If yes to #1, is this a good / safe practice? I could see this possibly causing problems if you did this in a block that was subsequently dispatched to a GCD queue, as .someNeatObject could have changed state / gone to nil?
I realize that the appDelegate should not be overused, but it is useful for it to hold information about the current state of the application, such as Core Data-related objects if you use Apple's templates for initializing a Core Data stack (at least on iOS projects). That particular case (Core Data) has me concerned as much of that stack is not thread-safe nor is it a good idea to be copying it.
Thanks in advance.
Your example will not compile because delegate is not defined. I assume you mean "SomeNeatObject *object = appDelegate.someNeatObject;"
In this example, appDelegate is a variable, whose value will be captured by the block. It's no different than any other variable.
It's not unsafe, because [[UIApplication sharedApplication] delegate] always returns the same thing, unless you change the delegate, which you probably don't.
The fundamental concepts here are: appDelegate is a variable which points (or refers) to an object (or instance) of type AppDelegate. In an iOS app there is one such instance, which is returned by [[UIApplication sharedApplication] delegate]. If you create a reference to appDelegate inside a block, you are making a copy of the variable, not the object. So in your code's block:
SomeNeatObject *object = appDelegate.someNeatObject;
This is semantically the same as putting the following code in the block (casts omitted):
SomeNeatObject *object = [[UIApplication sharedApplication] delegate].someNeatObject;
The SomeNeatObjects being referred to are the same thing.
Slightly more advanced concept: any object has an address in memory (an integer, usually seen in hexadecimal). If two variables have the same hexadecimal value, they point to the same object. If they have different values, they point to different objects. In your example, appDelegate (outside block) and appDelegate (inside block) have the same value, therefore point to the same object.
If you were to do this:
AppDelegate * otherDelegate = [appDelegate copy];
Then you would be making a copy of the object pointed to by appDelegate. But don't do this, please.
It's retain count does get incremented. The remedy is to tag it with the __weak attribute:
- (void)exampleFunction {
__weak AppDelegate *weakDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
SomeClass *classObject = [[SomeClass alloc] init];
[classObject someFunctionThatDoesBackgroundWorkAndCallsACompletionBlock:^(NSDictionary *results) {
SomeNeatObject *object = weakDelegate.someNeatObject;
[object doSomeNeatStuff:results];
}];
}
I'm new to ARC but understand how it works and I'm trying it out. I'm on iOS so memory is a severe concern.
I have a MyObject class which contains lots of big data. I want to release it, and load a new set of data.
MyObject *object;
object = [[MyObject alloc] initWithData:folder1]; // load data from folder1
// later...
object = [[MyObject alloc] initWithData:folder2]; // load data from folder2
This works fine without leaks, and I'm guessing the ARC inserts a [object release] before the new assignment. My problem is the data inside 'object' is released after the new set is allocated, and I run out of memory. What I really want to be able to do is:
object = nil;
<function to pop the pool, wait till everything is deallocated>
object = [MyObject alloc] initWithData:folder2]; // load data from folder2
but I'm not sure how to do that. I could run the new allocation on a performselector afterdelay, but it feels like I'm shooting in the dark and a bit of hack. There's probably a proper way to do this?
P.S I've tried searching for an answer, but all results are about memory leaks and how to make sure variables go out of scope and set variables to nil etc. My issue isn't about that, it's more of a timing thing.
UPDATE
Thanks for the answers, I'd already tried
object = nil;
object = [MyObject alloc] initWithData:folder2];
and it hadn't worked. I wasn't sure whether it was supposed to or not. Now I understand that it is supposed to work, but I must have something else holding on to it for that fraction of a second. I have NSLogs in all of my init/dealloc methods, and I can see first all the inits of the new instances of classes (of MyObject's ivars) being called, and then almost immediately after (within a few ms), the dealloc of MyObject, followed by the deallocs of its ivars.
I also tried the #autorelease but the same thing happens.
I've searched throughout the project and pasted all the code which I think may be relevant to this.
#interface AppDelegate : UIResponder <UIApplicationDelegate>;
#property PBSoundSession *soundSession;
#end
//--------------------------------------------------------------
#implementation AppDelegate
// onTimer fired at 60Hz
-(void)onTimer:(NSTimer *) theTimer {
[oscReceiver readIncoming]; // check incoming OSC messages
// then do a bunch of stuff with _soundSession;
}
#end
//--------------------------------------------------------------
#implementation OscReceiver
-(void)readIncoming {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
// parse all incoming messages
if(bLoadNewSoundBank) {
NSString *newFolder = parseNewFolder();
appDelegate.soundSession = nil;
appDelegate.soundSession = [MyObject alloc] initWithData:newFolder];
}
}
#end
//--------------------------------------------------------------
#implementation GuiController
// onTimer fired at 10Hz
-(void)onTimer:(NSTimer *) theTimer {
PBSoundSession *soundSession = appDelegate.soundSession;
// update gui with received values
}
#end
I thought it might be the 'soundSession' local variable in the GuiController::onTimer holding onto the old appDelegate.soundSession for the duration of that method, but to my surprise commenting out all of the GUI code (in fact disabling the timer), made no difference.
Is there a way of finding out at that point who is still holding onto my appDelegate.soundSession? I placed a breakpoint where I set it to nil, but couldn't find any useful information. I tried Instruments in Allocation template, but couldn't find anything useful there either (probably because I don't know where to look).
This is what my allocations track looks like, you can see the memory is all deallocated a bit too late!
.
This might not be an an ARC problem. What you could be seeing is your autorelease pool not draining soon enough—your MyObject is getting released, but the data it loaded is getting held onto by the pool because of some internal -retain/-autorelease pair. Try wrapping your -initWithData: calls in an #autoreleasepool block, like this:
#autoreleasepool {
object = [[MyObject alloc] initWithData:folder1];
// do things
}
// later…
#autoreleasepool {
object = [[MyObject alloc] initWitData:folder2];
// do other things
}
Setting the object to nil immediately before setting it to something else as Gabriele suggests might cause the compiler to insert the appropriate release before the second -alloc/-initWithData:, but it might be smart enough to do that already—if that doesn’t work, it’s most likely the autorelease-pool thing.
There is no delay when draining an #autoreleasepool {...}; the objects in the pool have release invoked immediately. If an object survives that, it is because there is either a strong reference elsewhere or because the object was autoreleased into the next pool out.
If you do:
a = [[Foo alloc] initBigThing];
a = nil;
a = [[Foo alloc] initBigThing];
The first instance of Foo will be released prior to the allocation of the second
With one big caveat; if any of the code paths that a is invoked upon happen to retain/autorelease it, then it'll stick around until the pool is drained. Surrounding it in #autoreleasepool{ ... }; should do the trick.
Note that the compiler will sometimes emit retain/autorelease sequences in non-optimized builds that are eliminated in optimized builds.
A bit more general answer, I found how you can force release an object:
#import <objc/message.h>
// ---
while ([[object valueForKey:#"retainCount"] integerValue] > 1) {
objc_msgSend(object, NSSelectorFromString(#"release"));
}
objc_msgSend(object, NSSelectorFromString(#"release"));
But you shouldn't do this because ARC will probably release the object later and this will cause a crash. This method should be only used in debug!
Ive used SystemSound in my app in order to play simple sound effects. In addition to this I play a musicvideo through the MPMoviePlayerController - now when I turn the volume up/down the music from the video responds as intended (lowering the volume up/down).
But the system sounds that are played does not respond to the volume. Im playing the system sounds when the user taps certain areas in the app. Heres a snippet of my code for that:
- (void)handleTap:(UITapGestureRecognizer *)recognizer {
SystemSoundID completeSound = nil;
//yellow folder in xcode doesnt need subdirectory param
//blue folder (true folder) will need to use subdirectory:#"dirname"
NSURL *sound_path = [[NSBundle mainBundle] URLForResource: target_sound_filename withExtension: #"wav"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)sound_path, &completeSound);
AudioServicesPlaySystemSound(completeSound);
}
PS. I doublechecked that my "Settings->Sounds->Ringer and Alerts->Change With Buttons" is set to ON (as I read on some other SO answers that leaving this option OFF will cause systemsound to not respond to the volume buttons)
Further the reason for using systemsound is that it gave the most accurate and responsive results when playing multiple sounds (like in a game).
Id prefer to not use OpenAL if possible (even through 3rd party sound libraries like Finch or CocosDenshion)
Any ideas?
Use the AVAudioPlayer class for playing sounds that are controlled by the user's volume settings (non-system sounds).
You can retain instances of AVAudioPlayer for each sound file that you use regularly and simply call the play method. Use prepareToPlay to preload the buffers.
Cheers to Marcus for suggesting that i could retain instances of AVAudioPlayer for each sound file and use prepareToPlay to preload the sounds. It might be to help for others looking for the same solution so here is how I did it (feel free to comment if anyone have suggestions for improvements)
//top of viewcontroller.m
#property (nonatomic, strong) NSMutableDictionary *audioPlayers;
#synthesize audioPlayers = _audioPlayers;
//on viewDidLoad
self.audioPlayers = [NSMutableDictionary new];
//creating the instances and adding them to the nsmutabledictonary in order to retain them
//soundFile is just a NSString containing the name of the wav file
NSString *soundFile = [[NSBundle mainBundle] pathForResource:s ofType:#"wav"];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:soundFile] error:nil];
//audioPlayer.numberOfLoops = -1;
[audioPlayer prepareToPlay];
//add to dictonary with filename (omit extension) as key
[self.audioPlayers setObject:audioPlayer forKey:s];
//then i use the following to play the sound later on (i have it on a tap event)
//get pointer reference to the correct AVAudioPlayer instance for this sound, and play it
AVAudioPlayer *foo = [self.audioPlayers objectForKey:target_sound_filename];
[foo play];
//also im not sure how ARC will treat the strong property, im setting it to nil in dealloc atm.
-(void)dealloc {
self.audioPlayers = nil;
}