How to dealloc a singleton? - iphone

I have a class named 'Capture' that is a subclass of NSObject and it also adds views to a superview.
I have added a Settings.bundle which consists of slider. When the slider value is 'ON/1' I show the views in 'Capture' in the main window as subview. When the slider value is 'OFF/0' I have to remove the view added from 'Capture' and release all the objects present in 'Capture'.
I simply don't want to hide the views created by 'Capture' class on to the main view, when the slider value is 'OFF/0'.
I want to dealloc the 'Capture' when the app in sent to background and not when the app terminates, so i cannot use the notification UIApplicationWillTerminateNotification to dealloc a singleton.
If i just hide the views displayed by 'Capture', there will be unnecessary use of memory for the 'Capture' class
So now how do i dealloc a singleton.
'Capture' class views will be on top of every view controller, even when the events occurs.
It is allocated once by class method called in AppDelegate.
singleton is created this way:
static ScreenCapture *sharedScreen = nil;
+(Capture *)sharedScreenCapture
{
if (!sharedScreen)
{
sharedScreen = [[Capture alloc] init];
}
return sharedScreen;
}

Why not to create some ReLoad method which will clear all your cashes and nullify your private variable?
Something like this:
+(void) reLoad {
#synchronized(self) {
// clear all the cashes...
sharedScreen = nil;
}
}
and to call it after receiving UIApplicationWillTerminateNotification
[ScreenCapture reLoad];

You can add another method:
+ (void)destroyScreenCapture
{
sharedScreen = nil;
}
Consider whether singleton is actually the correct pattern for you to use and if you really need to destroy the singleton or whether you should just 'clean' it to release all of the memory for the data that it isn't currently using.

Related

why wont my UILabel display a NSInteger

I have a ViewController, and GameController class which is just subclassed from NSObject. The view controller has one button linked to it and fires a IBAction that inits a GameController class. In the init of the GameController class is a CADisplayLink that adds one to an int named score. In the debugger the score is going up, but the label will only display 0.
in ViewController
-(void)setScore:(NSInteger)gameScore{
NSString *string = [NSString stringWithFormat:#"%i", gameScore];
scoreLabel.text = string;
NSLog(#"the score is %i", gameScore);
}
-(IBAction)play:(id)sender{
gameController = [[GameController alloc] init];
//in my header is GameController* gameController;
}
in GameController
-(id)init{
self = [super init];
if (self) {
viewController = [[ViewController alloc] init];
//in header ViewController* viewController;
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(gameLoop:)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes];
//in the header CADisplayLink* displayLink;
}
return self;
}
-(void)gameLoop:(CADisplayLink *)sender{
deltaScore++;
if (deltaScore >= 20) {
deltaScore -= 20;
score++;
//deltaScore and score are NSIntegers
[viewController setScore:score];
}
}
score is not updated because you are creating new instance of view controller in game controller, you somehow need to pass the object of view controller to game controller and then use the same object to call method setScore.
Look carefully at your code. Your ViewController's ‘-play:‘ method creates a new GameController. Your GameController's ‘-init‘ creates a new ViewController. Is that really what you want?
You haven't given us much to go on, but it seems likely that either your game controller or your view controller should be "in charge" and create one instance of the other. There are about a million questions here on SO related to how to get one object to send some data to the another, but they all boil down to this: to pass information from object a to object b, a needs a pointer to b. A can then use that pointer to send messages to b.
So, for example, your view controller creates the game controller. That means the view controller has a pointer to the game controller, and it can use an instance variable or property to save that. If the game controller needs to send messages back to the view controller, it'll need a pointer to the view controller. The view controller can supply that pointer (to itself) when the game controller is created, or at some point afterward. To facilitate that, the game controller needs a method that can accept the pointer to the view controller. Maybe you decide to do it at creation, so you change the game controller's initialize toon method to ‘-initWithDelegate:(id)‘, define a GameControllerDelegate protocol, and then adopt that protocol in your ViewController class. You'd do this rather than just using ‘-initWithViewController:(ViewController*)‘ because its good style to prevent GameController from depending on the particular class of the object that's helping it -- GameController probably only cares that its helper implements a few methods, it doesn't need to know that it's helper is a ViewController.
In GameCenter the Game Score is of type long long int so use long long in your methods and return type if using anywhere.
When you want to so it on the label use
yourLabel.text = [NSString stringWithFormat:#"%lld",yourscore];
Usually, when the debugger gets information correctly but the UI isn't updating is because the operation to update the UI isn't performed on the main thread. Try this:
dispatch_async(dispatch_get_main_queue(), ^{ [viewController setScore:score]; });
%i is an invalid format specifier; use %d for signed 32-bit integer or %u for an unsigned 32-bit integer
Have you tried?
NSLog(#"the score is %lu", gameScore);
It should work for you.

why does my UIViewController get retained an extra time when using initWithNibName?

I'm working on an app that has a Main view that wants to spawn a child view when a button is touched. So when I receive the button event, the MainViewController spawns the child view by calling initWithNibName and storing the ChildViewController in an ivar. I then show the ChildView by attaching an animation and setting childVC.view.hidden = NO.
This works, but I noticed that the ChildViewController was never getting released after closing the ChildView. I realized that the ChildVC's retain count went from 1 to 2 when I first access the child view. So something in the nib loading guts appears to be retaining my ChildVC again (in addition to the initial retain I expect during object initialization).
Can somebody help me figure out why the ChildVC is getting retained the extra time, and how can I make sure that it gets fully released when I want to close the child view?
Edit: here's some code, only slightly simplified. These are methods on the parent view controller.
-(IBAction)onLaunchChildButtonTouched:(id)sender
{
m_childViewController = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
[m_childViewController setParentDelegate:self]; // this is a weak reference
// m_childViewController retain count here is 1, as expected
m_childViewController.view.hidden = YES;
// m_childViewController retain count is now 2, not expected
[self.view addSubview:m_childViewController.view];
[self addTransitionEntrDir:YES]; // code omitted
m_childViewController.view.hidden = NO;
}
-(void)onChildWantsToClose:(id)child
{
NSAssert( child == m_childViewController, #"unexpected childVC" );
// if child view is now hidden, we should remove it.
if( m_childViewController != nil && m_childViewController.view.hidden )
{
[m_childViewController.view removeFromSuperview];
[m_childViewController release]; m_childViewController = nil;
// BUG: m_childViewController retain count is still 1 here, so it never gets released
}
}
Without code it is difficult to say exactly, but are you sure you are not assigning your ChildVC to a retain property of some other object? This would explain the unexpected retain you see.
Sorry for the previous answer, where I tried to convey this same message but I mixed everything up.
OLD ANSWER:
keep in mind that the view property of a UIViewController is retained:
view
The view that the controller manages.
#property(nonatomic, retain) UIView *view
so, if you assign to it like this:
childVC.view = [[xxxxx alloc] initWithNibName:...];
this explains what you are seeing.
Use instead:
childVC.view = [[[xxxxx alloc] initWithNibName:...] autorelease];
I found the problem, the leaky ChildViewController was instantiating an object that retained a ref back to it.
The interesting part is that I wasn't simply forgetting to release this reference. I did have a call to release it, but that code was never running because it assumed that viewDidUnload would run and give me a chance to release everything, but it didn't. I put me deinit code inside dealloc instead, and it works now.

Cant access an int value

i declared an int value as my imageIndexForSend through the following code in myView class.
int imageIndexForSend;
#property int imageIndexForSend;
#synthesize imageIndexForSend;
after on a button click i am displaying a popover which is PopOver calss.
there is table view with multiple indexes in popover class.when i click on any row in PopOver class table it set myView class imageIndexForSend as
In PopOver
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
myView *obj = [[myView alloc] init];
[obj getImageForSend:indexPath.row];
[staticSceneController release];
}
in myViewClass
-(void)getImageForSend:(int)index{
imageIndexForSend = index;
}
then i am return to myViewClass after dismissing popover (popOver class) and doing some actions in myViewClass.
then i am clicking a send button.but the integer value imageIndexForSend is zero.cant get the old value which i set from PopUp.
can any one tell me a way to get the old imageIndexForSend value.may i know what mistake i done.
First, you name a method with get to set the value, it's bad.
Second, you use a property and synthesize it, so you don't need to rewrite the set method unless you need to have a custom set method.
And finally you create a new view on each selection of tableview cell !
1) Remove your getImageForSend: method, you don't need that with property
2) Instead using : [obj getImageForSend:indexPath.row];, use : obj.imageIndexForSend = indexPath.row;
3) Don't create a new view on each selection, assign the value on the existing view.
A better way to transmit data from your popover to your view (controller ?) is to have a delegate property in your popover class and set it with your view object, create a delegate protocol with a method that is called when a cell is selected in popover with an int argument (the index) then make your view class adopts the protocol and do a obj.imageIndexForSend = argument; in your protocol method.
It seems you're allocating a myView instance and assigning that to a local variable (obj), but then you don't keep a pointer to that new instance anywhere.
From what I understand, you already have an existing instance of myView, so what you need to do is to set the variable on that instance, and not create a new one every time.
Each instance have their own set of variables, so changing it in a new instance won't affect any other instances.
You are instantiating a new MyView whenever the user taps on any row of your UITableView. You should try to access the original MyView instead (or whatever object shall retain that setup value).
Within your popover, you should find a way to access the instance that holds the actual index-value. How exactly that is achieved depends a lot on your implementation.
In other words, do not instantiate something within an object that has a shorter lifetime than the object that will access that very instance.
If you're trying to access the index of the selected row in the UITableView you can just use the following:
int index = [myTableView indexPathForSelectedRow].row;

How to change the UIImage of a UIImageView from a subview?

I want to change an image on a view, from a popup dialog of 4-6 icons (imagine like changing your image on a messenger application).
The way I implement this modal popup is by creating a new view at IB, with opacity on the background, and then I load this as a subview:
IconsViewController *iconsViewController = [[IconsViewController alloc] initWithNibName:#"IconsView" bundle:nil];
[self.view addSubview:iconsViewController.view];
So, when the user touches an icon, I have
- (IBAction)iconIsSelected:(id)sender {
switch ([sender tag]) {
case 1:
[(ParentViewController*)[self superview] changeIcon];
break;
case 2:
// same here..
break;
default:
break;
}
[self.view removeFromSuperview];
[self release];
}
The changeIcon just sets the image to a corresponding icon.
As you can guess, this is not working - the changeIcon message never works.
I can't understand what am I doing wrong, any help much appreciated!
You have a few choices here...
First one is create a property on your IconsViewController of type ParentViewController*, for example:
#property (readwrite,nonatomic,assign) ParentViewController* parentController; // weak reference
To break this down further:
readwrite because we want to be able to access the value via [self parentController] but also change it via [iconsViewController setParentController:self]
nonatomic because I'm not too worried about threading
assign to make it a "weak reference" where the parent will not be retained by the child. If they each retain the other, it could lead to memory leaks later because unless explicitly released you'd end up with a retain circle causing neither object to hit a zero retain count.
When you load from nib, set the property:
IconsViewController *iconsViewController = [[IconsViewController alloc] initWithNibName:#"IconsView" bundle:nil];
iconsViewController.parentController = self;
Then, call to it from inside of iconIsSelected like this:
[[self parentController] changeIcon];
Alternatively, you can create a delegate protocol:
#protocol IconViewSelectedDelegate (NSObject)
- (void) changeIcon;
#end
And use that protocol as a property, instead of the parent view controller type. This is more abstract, but it keeps the design cleaner. The parent view controller would then implement that delegate protocol, as one of many others.
Another option is to use NSNotificationCenter and publish/subscribe to events from your dynamic view. This is the "loosest" coupling between the two objects, but it might be overkill for this scenario.
The superview of a view is a view, not a view controller, yet you cast the superview to be of class ParentViewController. If the view has no superview, it returns nil, and message to nil are no-ops (which explains why you don't crash there).
BTW, that [self release] at the end is highly suspicious.

how do i add two delegates to a ui element at run time?

im trying to implement some behaviors when a mapview element scrolls... by coding a delegate for the scrollview inside of a mapview.
so, right now, i got a pointer to the scroll view used by the map view in my code.
however, i wish to set the delegate of this scroll view inside the map view, but the issue is that the mapview already sets up a default delegate for this scroll view inside the map view.
can i make my delegate implement all of the messages of the protocol, explicitly sending them to the mapview's default delegate while also implementing my own behaviors?
how else can i go about adding my own delegate behavior, to an already existing default delegate....?
thanks everyone,
michael
You could just get the existing delegate and save a reference for yourself:
origDelegate = [theView delegate];
And then set the object you want as the delegate:
[theView setDelegate:self];
Then when getting a delegate message, call the same method on origDelegate, modify the response if you want to (or if necessary), and then return the modified response:
- (BOOL)shouldViewDoSomething:(id)theView
{
BOOL result = [origDelegate shouldViewDoSomething:theView];
if (decision1)
{
result = !result;
}
return result;
}