MonoTouch/MT Garbage Collector: how to correctly release my views/class members (simple example)? - xamarin.ios

Have a look at the sample below which is not completely implemented but demonstrates what I'm talking about:
public class MyClass : UIView
{
private UIView SubView;
private UILabel ALabelInTheSubView;
public override ViewDidLoad()
{
}
// Imagine our view has a button. When clicked, a new sub view is added.
public void HandleButtonAddView()
{
// Assign a new view to the SubView member and add a label.
this.SubView = new UIView();
this.ALabelInTheSubView = new UILabel("A label");
this.SubView.AddSubView(this.ALabelInTheSubView);
// Add the SubView to myself.
this.AddSubView( this.SubView );
}
// Imagine our view has another button. When clicked, the sub view is removed.
public void HandleButtonRemoveView()
{
this.SubView.RemoveFromSuperView();
}
}
If I click the button to add the subview, the member variables SubView and ALabelInTheSubView are assigned new instances of UIView and UILabel.
If I then click the button to remove the subview, SubView gets removed from super view.
After this.SubView.RemoveFromSuperView() the members SubView and ALabelInTheSubView still have references to the view or label, hence no memory will be released yet. Correct so far?
If I now click the button to add the sub view again, the members will be overwritten with NEW instances of UIView and UILabel.
QUESTION: Does the GC now know that it can now safely dispose the previously assigned UIView and UILabel? I mean, all references should be gone. Or do I have to call this.SubView.Dispose() and this.ALabelInTheSubView.Dispose() after removing from superview? And is disposing the label necessary at all, since it is a child node of the UIView, which just got removed and disposed (which would mean I always have to dispose from bottom to top)?
ADD. SIDE-QUESTION:
If I call Dispose() on an object this IS still referenced - is that a a problem?

The answer is yes, the garbage collector will now consider the two previous instances of the objects to not be reachable from anywhere, and will be collected the next time the GC runs.
You do not need to call Dispose() manually. The only reason to call Dispose() manually is in cases where you know that the object you are pointing to is some large object and you want to make sure that the memory is released immediately, and not wait for the GC to kick-in at some point in the future.
What happens when you call Dispose () on the object, is that we internally release the reference to the Objective-C object and we set the Handle property on the object to null. This means that if you try to call another method on a disposed object, you will get a nice exception.

I would just reuse your SubView instead of making a new one each time. So create it in ViewDidLoad, store it in a member variable, and reuse it.
I don't think the extra memory will be a problem, and you won't have to worry about the GC at all.

Related

Binding changes in model reflect in TableView only after column resizing

First example work fine, if you run the next code you will see table.
Selected row is reflected in text field.
After saving changes their immediately reflecting in tableview due model properties to table cell binding.
In the second sample
after double clicking table view is hiding and edit form opened.
But after saving changes their reflect in table view only after column resizing.
How to fix this issue?
The issue appears to be that the TableView is not updated because it is not part of the Scene graph when the property is changed. (This would be a bug.) It does work fine in JavaFX8 (though you need to modify your code to ensure that the FX Nodes are initialized on the FX Application Thread; i.e. in the initComponents() method).
For a (fairly ugly) workaround to make this work in JavaFX 2.2, try the following:
private void openTable() {
Platform.runLater(new Runnable() {
#Override
public void run() {
table.setItems(FXCollections.<Person>observableArrayList());
vbox.getChildren().setAll(label, table);
table.setItems(data);
}
});
}
(I streamlined your existing code a little; also, the code you posted doesn't need a Platform.runLater(...) here as openTable() is only invoked from the FX Application Thread. However, this may be different in your real application.)

javafx2.0 - force refresh the scene

In my app, one scene having the popup dialog which consists of some fields and buttons. If you click on the button then I want to dismiss the popup dialog as well as update the some fields in the scene. Indirectly I want to refresh scene. is it possible?
I used the following code.Here what I did is, I get the controller of that scene and then update the field using id. but it doesn't work.
URL location = AdmincontrolController.class.getResource("admincontrol.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
try {
Parent root = (Parent) fxmlLoader.load(location.openStream());
AdmincontrolController controller = fxmlLoader.getController();
System.out.println("AdmincontrolController: "+controller);
controller.setEmail(item.getEmail());
} catch (IOException ex) {
Logger.getLogger(Add_loginController.class.getName()).log(Level.SEVERE, null, ex);
}
Scenario:
scene
Popup - If we clicks on the add then we need to dismiss that dialog and change the email text on the previous scene.
As Alexander mentioned above, updating the underlying text property of the object you are using to display the email should Just Work. You need to make sure that you are working with the property (see Oracle Java FX Property Tutorial for more info). As a concrete example:
FXML
<Text fx:id="email" />
<TextField fx:id="emailInput" />
<Button onAction="#doSetEmail" text="Set Email"/>
In your controller, use the #FXML annotation to inject concrete instances of objects and set the handler to adjust the text:
Controller
#FXML
Text email;
#FXML
TextField emailInput;
#FXML
public void doSetEmail(ActionEvent ae) {
email.setText(emailInput.getText());
}
Alternatively, you could just bind the email text property to the email label property so that changes would automatically get propagated:
email.textProperty().bind(emailInput.textProperty());
You could do this in your controller initialize() method.
Now, the caveat to all this working depends on how you are handling the event and what you are doing in this. You still haven't posted the code for that as requested by the first answer, so you may be having issues there. Namely, if you are starting threads and then trying to update UI elements on the JavaFX thread from a worker thread, then you can get into trouble (potentially) with things not updating. This depends substantially on the structure of your objects, and you have not given enough information to comment in any meaningful way on that.
chooks
Everytime you have the Feeling that you manually want to update a Scene you should probably use a backgroundWorker Thread to do the work.
This way your UI Thread can use the time to update Labels etc.
JavaFX is built so that you don't need to directly call the scene update routine. All you need - update properties of scene components, and they will be updated on the nearest pulse.
So, all you need is to update properties. Or, is there any real trouble?
refreshing scene its not possible without closing...but if you can do class level declaration for control..i.e make them static its may work...
try this..
make a function in main file.
MainPanel.java
public static void SetMail(String email)
{
txtmail.setText(email);
}
LoginPanel.java
btnclear.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
MainPanel.SetMail(txtEmail.getText());
}
});

How do I restrict keyboard focus to controls within a Node?

I'm trying to implement a reusable method for displaying a node as a lightweight dialog. The node is added to the top of a StackPane while the background is then blurred. There are 2 problems:
1) Controls in the background node of the stackpane are still able to receive focus.
2) How do I give focus to the top-level node. I know there is a requestFocus method on Node, but I need to give it to a control nested within the node. Since this is intended to be a reusable method, I can't reference the controls directly.
(if I can sidestep the whole problem by finding an existing implementation, that would be best, but I haven't found a 3rd party solution yet)
Thanks
For:
1) 3 alternative suggestions,
a- Add a key event handler to the dialogbox pane to catch the Tab key pressings
dialogbox.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(javafx.scene.input.KeyEvent event) {
if (event.getCode() == KeyCode.TAB) {
System.out.println("TAB pressed");
event.consume(); // do nothing
}
}
});
b- Temporarily disable all children of the main StackPane except the lastly added dialogbox child.
// Assuming dialogbox is at last in the children list
for(int i=0; i<mainPane.getChildren().size()-1; i++){
// Disabling will propagate to sub children recursively
mainPane.getChildren().get(i).setDisable(true);
}
c- Same as b but manually disable focusing to controls by node.setFocusTraversable(false) (spot kick). Surely this will not be your choice..
2) Tell the dialogbox which node should take the focus after the dialog is shown (through constructor or setter method).
// init dialogbox and show it then
dialogbox.setFocusTo(node_in_dialogbox);

JavaFX 2.0 memory leak on creating multiple scenes

I created a sample application that provokes a memory leak.
The problem is that I need to "reload" scenes. If I have two scenes (Bar and Foo) with one button each (ButtonBar and ButtonFoo), the buttons changes the current scene creating a new one.
If I stay clicking ButtonBar and ButtonFoo for 5 minutes (or less), the memory consumption of that simple program gets higher and higher.
Bar.java
public class Bar implements Initializable {
#FXML
private Label label;
#FXML
private void toFoo(ActionEvent event) {
try {
Button button = (Button) event.getSource();
Parent root = FXMLLoader.load(getClass().getResource("Foo.fxml"));
Stage currentStage = (Stage) button.getScene().getWindow();
currentStage.setScene(new Scene(root));
} catch (IOException ex) {
Logger.getLogger(Bar.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
The Foo.java is the same changing de fxml loading.
The fxml contains only one button:
<Button id="buttonBar" layoutX="126" layoutY="90" text="Bar!" onAction="#toFoo" fx:id="buttonBar" />
Is there a real memory-leak problem? Does anyone knows another way to do this? I want that this app stay alive and doing changes forever, like a service.
Thanks
I'm with a similar problem.
When i move the mouse on scene. The memory usage increases and never down.
I believe this is associated with mouse events.
Good Luck!
I'm not sure about the memory leaks, but for performance reasons I would avoid the repeated loading of the resource.
What I do is keep that root Parent in a variable and add it to a scene, or in my case to a specific part of another scene as needed, updating the values via a static method of the controller class.
I have several of these preloaded scenes, that update a part of a bigger scene every time the user selects certain values from a list.
I suggest you try FX8. An amount of fixes for memory leaks have been made there already.
In case you can't update to the latest version of JavaFX though, I suggest you do not use onAction="#toFoo" in FXML.
Rather, declare an event handler and call the following: buttonBar.addEventHandler(MouseEvent.MOUSE_CLICKED, declaredEventHandler);.
Release then the event handler before exiting your current screen: buttonBar.removeEventHandler(declaredEventHandler);

UIView events and garbage collection

I have observed something that can affect any program's memory consumption and I would like some thoughts.
I have created a very simple test project with a UIViewController and a UINavigationViewController. I push my ViewController and then I pop it. The GC does it job and my ViewController is released (the destructor is called). But, if I create a UIButton and I registered to one of its event (ex: TouchInsideUp), then my ViewController is not released. I need to unregistered to the event in order to release my ViewController. To be sure it was not a timing problem, my test application has a button which calls GC.Collect().
What I don't understand is that an object will be kept alive if it is accessible from the stack of any thread or by a static variable. If my ViewController is eligible for garbage collection, then the UIButton will also be. The event should not cause the ViewController to be kept in memory, because UIButton is not reachable by the GC. In my case, the ViewController is only used by the NavigationController, so once it is popped it should always be collected.
With the help of the new profiler (mono 2.10) I will maybe find a logic answer, but for now, I'm perplex. Any idea?
Edited: Here's some code to help understand my case.
My test ViewController is pretty simple
public class TestViewController : UIViewController{
~TestViewController(){ Console.WriteLine("Finalizer called"); }
public UIButton Button {get; set;}
public override ViewDidLoad(){
base.ViewDidLoad();
// If I remove the event registering, my TestViewController is collected.
Button = new UIButton();
Button.TouchUpInside += ButtonTouchEventHandler;
View.AddSubview(Button);
}
void ButtonTouchEventHandler(object sender, EventArgs e){}
}
My MainWindow has a NavigationController and it does the following:
It pushed a new instance of TestViewController (thus only the NavigationController has a reference to the TestViewController instance)
TestViewController is popped via the standard back button (if I don't register to TouchUpInside, TestViewController's finalizer gets called)
When I return to the MainWindow, a button allows me to call GC.Collect just to be sure.
Yes, there is a possibility of the object graph getting locked in this pattern, I've fixed it in the next major release of MonoTouch (MonoTouch 4)

Resources