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.
Related
Currently I'm working on an app that uses four protocols for communication between classes. Three are working fine, but one is still not working. I've set it up same as the others but the delegate is always losing its ID. I'm quite new to Objective-C so I can't get to the bottom of it. Here is what I did:
I have a MainViewController.h with the delegate
#property (weak, nonatomic) id <PlayerProtocol> player;
and a MainViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[[Interface sharedInstance] Init];
NSLog(#"Player ID: %#", _player);
NSLog(#"viewDidLoad: %#", self);
}
- (void)sedPlayer:(id) pointer{ //sed is no typo!
_player = pointer;
NSLog(#"sedPlayer ID: %#", _player);
NSLog(#"sedPlayer: %#", self);
}
+ (instancetype)sharedInstance {
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
In the Interface.m (NSObject)
- (void)Init {
[[MainViewController sharedInstance] sedPlayer:self];
}
And of course a protocol.h but this is not of interest as the delegate does the trouble! When I run the code I get the following output on the console:
sedPlayer ID: <Interface: 0x1700ab2e0>
sedPlayer: <MainViewController: 0x100406e30>
Player ID: (null)
viewDidLoad: <MainViewController: 0x100409550>
So it is obvious that the singleton is not working as the instance of the MainViewcontroller is different. For the singleton I'm using the dispatch_once standard method as I do with the other protocols that work fine. ARC is turned on. Does anyone has a clue what is wrong here and why the singleton is not working?
Here's how I think you ended up with two instances of the MainViewController. The first one, I assume, is created when navigating to the screen associated with MainViewController. The second one is created when you call [MainViewController sharedInstance] in Interface.m.
As the ViewController view is lazy loaded ("View controllers load their views lazily. Accessing the view property for the first time loads or creates the view controller’s views." from the Apple docs under ViewManagement), you see the viewDidLoad: <MainViewController: 0x100409550> log only once, when the first MainViewController gets navigated to and loads up the view.
Here's my suggestion:
Since you do the Interface initializing in the - (void)viewDidLoad, you might as well set self.player = [Interface sharedInstance].
The code would look something like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.player = [Interface sharedInstance];
NSLog(#"Player ID: %#", _player);
NSLog(#"viewDidLoad: %#", self);
}
You should also get rid of - (void)sedPlayer:(id) pointer and + (instancetype)sharedInstance in your MainViewController. It is never a good idea to have a ViewController singleton, since you might end up messing up the navigation or having multiple states of it.
For a more in-depth article on avoiding singletons, you can check objc.io Avoiding Singleton Abuse
I have some bluetooth class that needs to be alive during all views .
That means, that i have to call functions in this class from different views, and ALSO get delegates from this class to different views .
So , i have to initialise it once to create connection, than later, i have to use it again from different views(classes) and register to get notifications from it.
I was thinking about a singleton, that HE will create an instance of that bluetooth class, and i can access him from anywhere .
But, i would also like that any view can get delegates from it .
How would i do that ?
i have read What should my Objective-C singleton look like?
But maybe singleton is not what i need ?
How can you create a class to always be alive, and register to get delegates from it from anywhere ?
(how can it be done with app delegate class ? )
I have had the similar query a while back
Problem : Multiple Classes need to receive delegate calls from single instance
Solution: I used a combination of sharedInstance , delegates and NSNotifications to handle the problem
SharedInstance
+ (SomeBluetoothClass *) sharedInstance {
static dispatch_once_t once;
static SomeBluetoothClass *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Delegate
#property (weak, nonatomic) id <SomeBluetoothClassDelegate> delegate;
Since delegate can respond to only one class at a time. Assign class in focus based on your logic. Then whenever you want send info to all send it across using NSNotificationCenter from the sharedInstance. Send the info through using userInfo dictionary of NSNotifications
Notifications
[[NSNotificationCenter defaultCenter] postNotificationName:SomeBluetoothClassNotification
object:self
userInfo:info];
Model the structure of SomeBluetoothClass to be thread safe and handle all notifications along with the delegates and it should work fine.
Have a lots way for your situation. Firstly, you should understand that creating object is not too heavy.
So if you want to use Delegate, you can create a Factory method
Ex:
+ (instancetype)bluetoothManagerWithDelegate:(id<delegate>)delegate {
return [self alloc] initWithDelegate:delegate];
}
So that you also don't care about conflict of concurrence. Because you have separate Bluetooth class.
If you still want to use Singleton, in this situation, it depends on how many object you want to notify.
Only 1 views, just use delegate, and set new delegate when you present new view.
More than one, you can use NSNotificationCenter or Observer, you can google these keywords, it have a lot of tutorial and document on the internet help you use it.
I think you can create NSMutableArray in your singleton with links on your views and call someMethod for all objects when is needed. Don't forget remove views from array when its don't need anymore. That is simple realization of pattern called "Observer".
You need to create a class that should be allocated memory once in a lifetime
I am posting a small code snippet which can help you.
In your .m file
#pragma mark - Shared Instance
static BlootoothClass *_sharedblootoothclass = nil;
+ (BlootoothClass *) sharedClass {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedblootoothclass = [[self alloc] init];
// DO YOUR STUFF TO INTIALISE YOUR CLASS
});
return _sharedblootoothclass;
}
in Your .h file
+ (BlootoothClass *) sharedClass
dispatch_once is the queue with dispatch the instance of your class one in a lifetime and you can access its function all over in the app any time.
Now to get any data from it you can get it from instance from any where like
[BlootoothClass sharedClass].anyObject
And you also can send post notification from here in any of its function
- (void)detectedBlootoothdevice{
[[NSNotificationCenter defaultCenter] postNotificationName:#"newdevicedetected" object:nil];
}
You should not use delegates as you cant call same delegate function at multiple class because last delegate get overwritten.
I want to initialize my singleton object which stores and manages the application settings over the entire class within my app. Also, the singleton instance should be initialized by loading the data from NSUserDefaults upon launch. However, I'm not fully sure where I should initialize the singleton upon launch.
In Cocoa app, I first wrote the singleton initialization code within applicationWillFinishLaunching:, taking parameters from NSUserDefaults. However, later I found that this doesn't work properly if I also write the singleton initialization code (taking no parameter!) within my initial view controller, set in storyboard, because the viewWillLoad:, viewDidLoad: etc. of the class of the view controller are called before the applicationWillFinishLaunching:.
So now I'm sure I should write the singleton initalization code within viewWillLoad: earlier than applicationWillFinishLaunching, but still not sure whether it is appropriate. Specifically, I know the NSApplicationMain is the first method to be called upon launch, but it seems that the next method is not anything within AppDelegate, at least if you use storyboard.
To summary, what I want to ask are the following:
What method from what class will be called after NSApplicationMain, if you use storyboard.
Where should I write my singleton initialization code within my app? I want to initialize it as soon as possible.
Does it differ between iOS and OS X app?
You should initialize it when it's first accessed. Something like this, maybe:
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
As a side note, if you're literally only using this class as an accessor to NSUserDefaults, you might want to consider using static methods instead.
+ (id)mySpecificDataPoint {
return [[NSUserDefaults standardUserDefaults] objectForKey:#"whatever"];
}
+ (void)setMySpecificDataPoint:(id)data {
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"whatever"];
}
Or maybe a more well-designed way might be to add a category to NSUserDefaults for this purpose.
#interface NSUserDefaults (MyData)
#property (nonatomic) NSString *someDataPoint;
#property (nonatomic) NSInteger somePrimitiveDataPoint;
#end
#implementation NSUserDefaults (MyData)
- (NSString *)someDataPoint {
return [self objectForKey:#"someDataPoint"];
}
- (void)setSomeDataPoint:(NSString *)someDataPoint {
[self setObject:someDataPoint forKey:#"someDataPoint"];
}
- (NSInteger)somePrimitiveDataPoint {
return [[self objectForKey:#"somePrimitiveDataPoint"] integerValue];
}
- (void)setSomePrimitiveDataPoint:(NSInteger)somePrimitiveDataPoint {
[self setObject:#(somePrimitiveDataPoint) forKey:#"somePrimitiveDataPoint"];
}
#end
You init the singleton when you have to use it. So as Daji Djan said: lazy wins. Just take attention that, you should not do a long-run process in your applicationWillFinishLaunching, it should return as soon as possible.
If the singleton is not mandatory during applicationWillFinishLaunching, you should call it in viewWillAppear of first view controller if you need to initialize it ASAP.
lazy always wins
if you can get away with it: as late as possible :) AND always do the minimum needed (but do as much as is reasonable to keep your code clean!)
Okay, so I was reading here declaring global variables in iPhone project and I noticed the line with this code: [[[UIApplication sharedApplication] delegate] myNSString];.
Basically, I want a user to input something in a text field on the flipside view, then have it stored in a variable which is accessed on the main view. Ideally, the main view would be able to just read the text field from the flipside view, but this seems to be impossible (I've spent several hours each day for the past few days scouring the web and various books for an answer about how to do this and no one seems to be able to give a definitive answer). Therefore, I'm resorting to using a global variable to tackle this.
Will the code that I printed above somehow allow me to do this? I've been trying to adapt it for the past hour, but have come up with nothing except No known instance method for selector 'myNSString' and I'm not quite sure what that means in this case.
Can someone please help me out? I feel like I can keep trying different things but without some sort of help, I'm just shooting in the dark here. Thank you!
You may want to think about using a singleton to hold your data if you're set on using a global variable. There's a good tutorial on singletons here: http://www.galloway.me.uk/tutorials/singleton-classes/ -basically it's a class that can be shared throughout the application and accessed/modified by different controllers. You'd be able to create a property on it, write to that property from the flip view, and then access that property from your main view.
#import "Singleton.h"
#implementation Singleton
#synthesize yourTextField;
#pragma mark Singleton Methods
+ (id)sharedManager {
static Singleton *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
yourTextField = #"";
}
return self;
}
You could call it in code by importing its header file and:
Singleton *mySingleton = [Singleton sharedManager];
the mySingleton object will have the text field attached. It can be accessed by:
mySingleton.yourTextField;
.h file:
#import <Foundation/Foundation.h>
#interface Singleton : NSObject
#property (nonatomic, strong) NSString *yourTextField;
+ (id)sharedManager;
#end
Singleton (remember about dispatch_once), static variables or NSUserDefaults. It really depends what you really need.
If you are using storyboards and just want to pass data between VC, then you can use "prepareForSegue" method (described here https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/prepareForSegue:sender:).
Segue has "destinationController" property, so you can setup VC before showing it.
I have a tab bar application that works fine, but if the app is opened without it having been running in the background, then the tabs are a little slower to open than usual, as they are loading plists.
Is it possible to load all the data into the views when the app launches?
I would recommend using a service class from which all of the view controllers can query. A common method of defining such a helper class is using the singleton design pattern. The singleton pattern allows only one instance of a singleton class to ever be instantiated. Using this method, you know that every other instance that will be making use of this service will be going through this one instance.
Here's the snippet I noramlly use, it might not be the optimal so I invite other users to suggest changes please:
//.h:
+ (MySingletonServiceInstance *)sharedInstance;
//.m:
static MySingletonServiceInstance *sharedInstance = nil;
+ (MySingletonServiceInstance *)sharedInstance{
#synchronized(self){
if(sharedInstance == nil)
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
- (id)init {
if ((self = [super init])) {
//Set up
}
return self;
}
Now in any other class (such as a view controller that needs some data), you can do something like this:
[[MySingletonServiceInstance sharedInstance] doSomething];
or
NSDictionary *myData = [MySingletonServiceInstance sharedInstance].data;
and it will call the same object. I often use singleton objects for loading data and suchlike, whether it's an interface to a web service or to the local CoreData. It's a really useful design pattern and I learned so much by picking it up.