I got a customized UILabel which register itself to a communication instance during creation. This part works perfect, the UILabel will . But when I remove the UILabel will update itself after called by the communication instance. I have of course remove the listener delegate too. But as ViewDidUnload is not call anymore, I do not know where.
Here the code sample:
#implementation MyValueLabel
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate addBalanceListener:self];
}
return self;
}
-(void)communicationUpdate
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
}
// This is the missing Method
-(void) DestructionOfTheLabelWhichIDidNotFound
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate removeBalanceListener:self];
}
#end
Technically you seem to be looking for the -dealloc method which is called on an object when it is to be deallocated. However I do not believe that is an adequate solution.
The example provided here seems to be doing a number of unexpected things which I believe will result in code which is surprising to other developers, requires fighting against common patterns in the UIKit framework, and be difficult to maintain.
Normally iOS view controllers are responsible for mediating or coordinating how data reaches view objects. A controller supplies a view with either the data it should currently display or provides it with a source of that data. This allows a single view class to be used in many different locations and to display data of the same type regardless of its source. Instead here we have a view which reaches out to obtain it's own data. There's no way for a controller to determine when an instance of this view class should update or what data it should display. For example the controller cannot decide to pause updates, or cause other view elements to update at the same time.
In addition this view makes several assumptions about the source of it's data and how that source may be obtained. The view assumes that the current application's app delegate is specifically an AppDelegate class so it will not work if you want to use this view in another application. Since the view also obtains this AppDelegate instance via a call to + sharedApplication there's no hint to users of this view that it has this dependency.
Consider instead allowing classes which use this view class to provide it with the data it should display.
If the property where addBalanceListener: stores the reference is weak, you don't need to do anything. Weak properties are automatically set to nil when the instance they are pointing to is dealloced.
Related
I am a newbie to objective c and xcode so please pardon the mistakes in usage of the right technical terms.
I made a new project and have an appdelegate (.h and .m files).
In the app delegate I have some variables (settings). One of the variables is mainImage.
I put it in the appdelegate as I want it to be accessible from any view controller.
I added a viewcontroller called MainViewController (.h and .m files).
In my MainViewController (.m) I have some custom methods.
In the viewDidLoad method I have the following code
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[currentImage setImage:appDelegate.mainImage];
Question:
In any of the methods of the MainViewController, whenever I wanted to read the "mainImage" I have to do the above 2 lines.
Is there a place in the .m file that you can do it once and it is available in all the methods of that file?
Thanks a lot in advance,
Prasad.
You could write a method in MainViewController with those two lines it in that returns an object of type UIImage or whatever it is, and then just call that. Invocation would be something like [self mainImage] and the method signature would be - (UIImage *)mainImage.
You can put these two lines in the viewDidLoad method of your MainViewController as example:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[currentImage setImage:appDelegate.mainImage];
That's done once, then each time you need currentImage content, you just call it:
UIImage *img= [[UIImage alloc]init];
img = self.currentImage;
Assuming currentImage is declared as property in MainViewController.
#Malloc and #jack...that was pretty close to what I needed..nevertheless, I have quite a few properties..hence I ended including the call in the header file...
#define appDelegate ((MyAppDelegate *)[UIApplication sharedApplication].delegate)
Then I call call appDelegate.any_of_my_properties
From my C# experience, I know that its probably a bad practice to include a global variable in app delegate and reference it this way..however, this one time..I think it makes sense..given that the object is really not tied to any particular view controller (the image was just an example) in reality..I have some dynamic value holders.
I have my main app delegate
I have a few UIViewController derived instances driven by a Storyboard
Say I'd like to provide a centralized persistence layer for my application - perhaps Core Data of SQLite. Where would I put those objects? I'm missing some centrally accessible "Application" class you can access from all the UIViewController instances.
Is there a pattern to follow here?
you should check the singleton pattern:
In software engineering, the singleton pattern is a design pattern
that restricts the instantiation of a class to one object. This is
useful when exactly one object is needed to coordinate actions across
the system. The concept is sometimes generalized to systems that
operate more efficiently when only one object exists, or that restrict
the instantiation to a certain number of objects. The term comes from
the mathematical concept of a singleton.
here is a source for a example implementation: What should my Objective-C singleton look like?
and here is the direct link for the modern solution:
https://stackoverflow.com/a/145395/644629
What you're describing is your model layer. There are two main ways to manage the model:
At application startup, create the main model object and hand it to the first view controller.
Make the main model object a Singleton.
The "main model object" in both cases is generally some kind of object manager. It could be a document, or it could be a PersonManager if you have a bunch of Person objects. This object will vend model objects from your persistence store (generally Core Data).
The advantage of a Singleton here is that it's a little easier to implement and you don't have to pass around the manager. The advantage of a non-Singleton is that it's easier to have more than one (for a document-based system), and it's easier to test and reason about non-singletons than singletons. That said, probably 80% of my projects use a singleton model manager.
As a side note, that you appear to already understand: never store the model in the application delegate, and never use the application delegate as a "rendezvous point" to get to the model. That is, never have a sharedModel method on the application delegate. If you find yourself calling [[UIApplication sharedApplication] delegate] anywhere in your code, you're almost always doing something wrong. Hanging data on the application delegate makes code reuse extremely difficult.
Go with a singleton pattern, which has scope of application lifetime.
#interface DataManager ()
#end
#pragma mark -
#implementation DataManager
#pragma mark - Shared Instance
static DataManager* sharedInstance = nil;
#pragma mark - Singleton Methods
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (DataManager*)sharedInstance
{
#synchronized([DataManager class])
{
if (!sharedInstance) {
//[[self alloc] init];
sharedInstance = [[DataManager alloc] init];
}
return sharedInstance;
}
return nil;
}
+ (id)alloc
{
#synchronized([DataManager class])
{
NSAssert(sharedInstance == nil, #"Attempted to allocate a second instance \
of a singleton.");
sharedInstance = [super alloc];
return sharedInstance;
}
return nil;
}
#end
Declare your properties in .h file and synthesize them here in .m file.
To use that property just call:
// set value
[[DataManager sharedInstance] setSharedProperty:#"ABC"]; // If its a string
// get Value
NSLog(#"value : %#", [[DataManager sharedInstance] sharedProperty]);
Hope this is what you required.
Enjoy Coding :)
Background:
I asked a question last week about using adWhirl across multiple viewControllers but unfortunately I'm still not getting it.
I took the suggestions and now use one adWhirl instance, created in the app delegate, in each viewController but I don't see how I can respond to the adWhirl delegate events in my viewController (adWhirlDidReceiveAd:, adWhirlDidFailToReceiveAd: etc.) since the app delegate doesn't know which viewController is currently displaying its adView.
Hence my confusion...
Is there a design pattern for this? I would think there must be. I don't want to lard all my viewControllers with boilerplate adWhirl delegate code if I can avoid it.
What I've got now in each viewController is below. I tried setting up a method in the app delegate that each view controller could call to "register" that is was taking ownership of the app delegate's adView - thinking that when the delegate receives events like adWhirlDidReceiveAd, it could do the work below. This sort of worked but lead me to think I was reinventing the wheel.
Question: Is there a design pattern for using the adWhirl view across multiple viewControllers?
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[self.view addSubview:appDelegate.adView];
[self.view bringSubviewToFront:appDelegate.adView];
CGSize adSize = [appDelegate.adView actualAdSize];
CGRect onScreenFrame = appDelegate.adView.frame;
onScreenFrame.size.height = adSize.height; // fit the ad
onScreenFrame.size.width = adSize.width;
onScreenFrame.origin.x = (self.view.frame.size.width - adSize.width)/2; // center
onScreenFrame.origin.y = (self.view.frame.size.height - adSize.height);
appDelegate.adView.frame = onScreenFrame;
}
- (void)viewDidDisappear:(BOOL)animated
{
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[appDelegate.adView removeFromSuperview];
}
The obvious answer is to make a Controller class that all your view controllers will become subclasses of. So then
AdviewDelegateController : UIViewController
and
YourViewController : AdviewDelegateController
then you put all your adview logic in the AdviewDelegateController.
As mentioned before - I'm an Objective-C newbie of the first order but having read 4 physical books on the subject and a bucket-load of ebooks and documentation I still can't find what I'm looking for.
I have a top-level content view controller that wants to configure its view property from the physical dimensions of the window property of the application delegate. This is something that several people have already asked questions on. ([UIScreen mainScreen] doesn't cut it for reasons already aired many times before on this forum). Therefore, the logical approach would be for the content view controller to read the frame of the application delegate's window. Now, the only answer I've found that comes close to this is to use [[[UIApplication sharedApplication] window] frame] - however, this only works once the window property has been made keyAndVisible. The content view controller needs to read the app delegate's window property before it gets to makeKeyAndVisible. The code goes in this order....
App Delegate:
- (BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *) launchOptions {
// This next line is a test window frame for R&D purposes....
[self setWindow: [[UIWindow alloc] initWithFrame: CGRectMake(0.0f, 20.0f, 320.0f, 320.0f)]];
if ([self window]) {
contentViewController = [[ContentViewControl alloc] initWithNibName: nil bundle: nil]; // Content view controller instantiated here
if (contentViewController) {
[[self window] addSubview: [contentViewController view]];
[[self window] layoutSubviews];
[[self window] makeKeyAndVisible]; // Window made key and visible here
return YES;
}
}
return NO;
}
In my content view controller's initWithNibName: nil bundle: nil method I have the following test code...
- (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil {
self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil];
if (self) {
NSLog(#"%#", NSStringFromCGRect([[[UIApplication sharedApplication] keyWindow] frame]));
// This does not work.
}
return self;
}
This does not work due to the fact that App Delegate's window is not yet key and visible.
So, my question is thus; What is the name of my App Delegate Class' instance? I know the App Delegate's Class name defaults to myApplicationNameAppDelegate but I'm after the instance name. I want to replace the call to [[UIApplication sharedApplication] keyWindow] by something like;
[myAppDelegatesInstanceName window].
Expanding this a little, how does one access methods in other target objects that are not scope descendants of the object doing the querying?
Like I said, I'm a total noob to all of this and this is probably another dumb noobie question but it's one that nobody yet appears to have answered in a simple way.
(Procedurally - my home turf - there are a plethora of ways to get the window stuff down into other levels of scope ranging from making the window globally accessible to the whole program suite to passing it down as a specific parameter through the various function hierarchies - but this Objective stuff seems to depart from established, procedural practice).
If anyone can help, I'd be really grateful. This stuff is definitely not intuitive!
V.V.
You can access the app delegate through the singleton UIApplication instance:
[[[UIApplication sharedApplication] delegate] window];
This is a special case, though, because you can access an object (the UIApplication instance) whose delegate you know is the object you want to access. The general case:
Expanding this a little, how does one access methods in other target objects that are not scope descendants of the object doing the querying?
You need to pass a reference to the target object to the object from which you want to access it. This is sometimes necessary but it also means that you introduce a tight coupling between these two objects, which you generally want to avoid if you can. So think about each case and see if there are other possible designs.
Swift 3
if let window = NSApplication.shared().windows.first {
// access window. properties now
}
Use this for Swift:
let window:UIWindow? = (UIApplication.sharedApplication().delegate?.window)!
I am well into building a Core Data tab-based iPad application. I am passing in my NSManagedObjectContext to my root view using the following in my app delegate class.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.rootViewController.managedObjectContext = self.managedObjectContext;
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;
}
My question is: how do I set the same managedObjectContext on all my tabs? It would also be good if I could set up some of my service classes in the app delegate and use the same instance across all my tabs. How can this be done?
Thanks in advance!
A "tab" is simply another view controller. As you initiate the VCs for each tab, you can hand them a managed object context in exactly the same way you set rootViewController.managedObjectContext, assuming that they have managedObjectContext properties.
Some people use singleton objects to provide Core Data objects to their classes; what I did in the app I'm currently working on was to declare a protocol, CoreDataSource, with getters for my NSManagedObjectContext, NSManagedObjectModel, and NSPersistentStoreCoordinator, and implemented that protocol on my appDelegate. My view controllers that need to use core data have member variables of type NSObject <CoreDataSource>, and as they create each other they set the property. They're all actually pointing to my appDelegate, but they don't know it, so they're not tightly coupled to an upstream object.
The simplest solution is to add super class for you tab view controllers with a managedObjectContext attribute and a custom getter method like:
- (NSManagedObjectContext *) managedObjectContext{
if (managedObjectContext !=nil) {
return managedObjectContext;
}
MyAppDelegateClass *appDelegate=(MyAppDelegateClass *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext=appDelegate.managedObjectContext;
return managedObjectContext;
}
If all your tab view controllers inherit this method they will all automatically find the app delegate's managed object context. And you're done.