A function on a UIViewController declares and initializes a reference to a class object. That class has a weak delegate ref. which has been set to the UIViewController. After this function finishes execution the object gets deallocated and its no longer calling the delegate methods.
func registerDevice() {
prepareForLoading(true)
let notificationModel: NotificationModel = NotificationModel(delegate: self)
notificationModel.registerDevice()
}
Is there a way to keep this object alive until the UIViewController dies, without having a global class ref. on the UIViewController to it. This is important because there isn't any use for this object in any other function or in any other situations.
Since you are declaring notificationModel as a constant within the scope of the registerDevice function, the lifetime of that object is limited to that function in the absence of any other strong reference being held.
You say "there isn't any use for this object in any other function or in any other situations.", but this isn't correct; Since your view controller implements delegate functions for this object and you expect those delegate functions to be called, your view controller needs to take responsibility for keeping the model object in memory.
It can do this by simply declaring an instance property
var notificationModel: NotificationModel?
and assigning the object to that property in your function:
func registerDevice() {
prepareForLoading(true)
self.notificationModel = NotificationModel(delegate: self)
self.notificationModel!.registerDevice()
}
Now the lifetime of your model object is (at least) the lifetime of your view controller.
Note, that this property is not a class property or a global; it is scoped to the specific View Controller instance.
Related
My sample code like this (just a sample):
[self.view touchActionWithCompeletion:^(NSSting *text){
self.label.text = text;
}];
The block is a parameter of a method, the method is a instance method of self.view, then I access self in block. If self.view is a strong property, will this situation create retain cycle? And will self.view strong reference the block?
Adding my comment above as answer, after testing the code myself to confirm the logic I mentioned,
I think it should not, dead lock ( I mean memory leak, two strongly held objects holding the reference to each other and never being de-allocated hence I mentioned deadlock) will happen only if you pass a strong reference of an object to the block (in this case self) and then the passed object holds a strong reference to block itself (directly or indirectly).
Hoping that method touchActionWithCompeletion will not save the block passed to it with a strong reference this should not result in a retain cycle
EDIT:
Tested your code and deinit gets called as expected.
Here is what I tried,
class MyView : UIView {
func touchActionWithCompeletion(block :(NSString)->()) {
block("abcd");
}
}
class ThirdViewController: UIViewController {
var myViewInstance = MyView()
#IBOutlet var c: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.myViewInstance.touchActionWithCompeletion { (abcd) in
self.c.text = abcd as String
}
}
deinit {
print("deinit called")
}
}
As expected deinit called.
Only thing to notice here, method touchActionWithCompeletion will not store the block passed to it with strong reference. It simply executes it. So my answer above holds true in this case.
EDIT 2:(Clarifying my statement in answer)
I happened mention the passed object holds a strong reference to block itself (directly or indirectly) I guess I need to explain why I mentioned indirectly.
Consider this case, Deadlock will happen if View holds a strong reference to the block passed to its method. Though the strong object passed here to the block is self and self does not hold the reference to block directly, it still result in deadlock if View holds a strong reference to block.
Reason Self - holds a strong reference -> View - holds a strong reference -> Block - holds a strong reference -> Self
Hence deadlock. Though self does not hold the block directly, because it holds the block indirectly, hence deinit on self will not be called. Hence I happened to mention the passed object holds a strong reference to block itself (directly or indirectly)
Hope it helps
I have two .swift files so I want one of them to modify the text of an IBoutlet label that is the other class.
Class 1:
class class1: UIViewController {
#IBOutlet var label1: UILabel!
}
Class 2:
class class2: SKScene {
var cool_variable = 1
func coolFunc () {
class1.label1.text = "\(cool_variable)"
}
}
by doing this I'm getting the following error message:
Instance member "label1" cannot be used on type "class2"
Thanks in advance!
The distinction and relationship between classes and instances is absolutely crucial to object-oriented programming. Thus, consider your proposed code:
func coolFunc () {
class1.label1.text = "\(cool_variable)"
}
The problem here is that class1 is the name of a class. (That would be more obvious if you just, please, follow the elementary rule that class names start with a capital letter.) But label1 is an instance property. Thus what you need here is the name of an instance of the class1 class — presumably a reference to the actual existing instance that is already part of the view hierarchy.
You never create an instance of class1 in class2 to access the variables.
My guess is that you are using Storyboards. In this case you wouldn't want to create an instance of class1. Instead you would use delegation (This would also be a good idea if you are not using Storyboards).
Delegation can be a complicated topic, so I will try to keep this simple.
First, you start with a protocol. Usually you name it something like <CLASS-NAME>DataSource, so you would do something like:
protocol class2DataSource: class {}
The class keyword is required for delegation protocols.
Then you would add the methods to it that you want called in other classes when you call a method in the class the protocol delegates for, so, for example, willCoolFunc:
protocol class2DataSource: class {
func willCoolFunc(with variable: Int)
}
You have the parameter so you can access the variable cool_variable as you are trying to.
Next, you need to create a a variable in class2 that is of type class2DataSource:
weak var dataSource: class2DataSource?
Make sure the variable is weak and an optional.
Next, call the method, you would do it in coolFunc:
func coolFunc () {
dataSource?.willCoolFunc(with: cool_variable)
}
Now you, to access cool_variable when the function is called, you need to implement class2DataSource on class1. Create an extension of class1 that implements class2DataSource and add the function willCoolFunc:
extension class1: class2DataSource {
func willCoolFunc(with variable: Int) {
}
}
Now you can access the variable cool_variable in class1! The reason why is because when you call class2.coolFunc(), the willCoolFunc method is called with cool_variable passed in. Any class that implements the class2DataSource can access cool_variable with the willCoolFunc method.
To finish of the method, here is what the extension would look like:
extension class1: class2DataSource {
func willCoolFunc(with variable: Int) {
self.label1.text = "\(variable)"
}
}
We are almost done, but not quite. We still have to set the class1 as the data source for class2DataSource. To do this, I will reference Nikolay Mamaev from this post:
Go to the Interface Builder.
Type "Object" in the search text field of the Objects Library and drag an 'Object' to your view controller containing that connects to
class1 (i.e. do the same as you add any view or view controller to
storyboard scene, with the exception you add not a view or view
controller but an abstract object).
In the left-side 'Scenes' panel of your storyboard, highlight just added Object; in right-side panel go to the 'Identity Inspector' and
type class2DataSource instead of pre-defined NSObject. In left side
panel ('Scenes'), 'Object' will be renamed to 'Class2 Data Source'
automatically.
In the left-side 'Scenes' panel of your storyboard, control-drag from your UIView [*not the controller*] to the 'Class2 Data Source';
in pop-up appeared, select dataSource outlet.
There! You now set class1's label1's text to the value of cool_variable when you call class2.coolFunc()!
I do not know the exact problem you're trying to solve, but I'm just going to consider the part that you want to access the variable in class1 in class2. There are two basic ways to go about this, one is Inheritance and the other one is by creating an object. These are basic Object Oriented Programming concepts which you need to be familiar with.
Swift does not support multiple inheritance. So that rules out inheritance for solving you problem, since you are inheriting two classes SKScene and UIViewController.
Create an object in the class1 and call the function coolFunc
class class1: UIViewController {
#IBOutlet var label1: UILabel!
func modifyLabel(){
let someObject = class2()
someObject.coolFunc()
}
}
Of course this solution might not be the one you're looking for. You will have to explain more about the problem you're facing if you need a solution that will work for you.
I have a class TestClass and a class & instance method inside it
class TestClass {
class func classMethod(){
print("how do i call instance method from here")
}
func instanceMethod(){
print("call this instance method")
}
}
My question is how do i call that instanceMethod from classMethod??
One way i have noticed is
class func classMethod(){
TestClass().instanceMethod()
}
But,Is this the good way to do?
What you are trying to do rarely makes any sense from a design perspective.
By definition, an instance method operates on an instance of an object.
For example, it might require access to some instance members, or somehow meddle with the state of the object you call the method on.
class methods on the other hand do not require an instance to be able to call them - and should in general only operate on the given parameters, not be dependant on shared state.
If you need to call instanceMethod() in classMethod(), and instanceMethod() does not require any state - why is it not also a class method, or a (global) pure function?
You can pass the instance object as a parameter to the class method, and then call the instance method of the object:
class TestClass {
class func classMethod(obj:TestClass){
print("how do i call instance method from here")
obj.instanceMethod()
}
func instanceMethod(){
print("call this instance method")
}
}
To call the instance method, you need an instance of TestClass. That's what TestClass() is getting you when you call TestClass().instanceMethod().
If you want to call it from a specific instance, you could pass it in as a parameter to the class function: class func classMethodUsingInstance(instance: TestClass)
If you don't need a specific instance for instanceMethod(), maybe consider making it a class method as well.
I have a method that gets called occasionally. It relies on a property that I don't want to instantiate unless that method is called, but at the same time I don't want to reinstantiate the property if it is already instantiated. Is this what lazy loading is for?
I'm currently using:
property = property ?? Property()
and that seems to be working fine, but I want to do a sanity check on this approach.
All you have to do is declare the property as lazy, and it will only be initialized when needed.
class MyClass {
lazy var myLazyArray = [String]()
}
var myObject = MyClass() // myObject.myLazyArray still not initialized
myObject.myLazyArray.append("hello") // Now we're in business!
print(myObject.myLazyArray)
My question is two-part:
First, Say I have a class:
MyClass.h
#interface MyClass: NSObject
-(id)initWithName:(NSString*)name;
#property(nonatomic, strong) NSString *name;
#end
MyClass.m
#implementation MyClass
-(id)initWithName:(NSString*)name
{
if (self = [super init])
{
self.name = name;
}
return self;
}
#end
My question: I know that self will hold the name property strongly. But how will the name property relate to self? What I mean is that I can access name as self.name but while class instantiation, how is the children of self (which in this case is name) related to self? I am imagining the structure of the class as a tree, with the parent holding strong reference to the children and the children holding a weak reference to the parent. I want to know if I am thinking about it correctly or not. My guess is it will be a weak relationship.
Second, if I add a method which has a block that references the name property. So my updated implementation of MyClass.m is:
MyClass.m
#implementation MyClass
-(id)initWithName:(NSString*)name
{
if (self = [super init])
{
self.name = name;
}
return self;
}
-(void)doSomeStuff
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.name = #"Name changed inside block";
}];
}
#end
My second question is: I am not referencing self directly inside my block. So, I guess there is no retain cycle here. But I am referencing name property which is held by self. So does this create a retain cycle?
I know that self will hold the name property strongly. But how
will the name property relate to self?
Each property will have a backing instance variable, conventionally named the same as the property with a leading underscore and a getter and/or setter method. There is no relationship; the property generally makes the class instance larger (due to additional instance variable) and the class larger (due to additional methods).
I am not referencing self directly inside my block. So, I guess there
is no retain cycle here. But I am referencing name property which is
held by self. So does this create a retain cycle?
Yes you are referencing self directly, so a retain cycle is possible. However a retain cycle can only happen under certain circumstances, and it's often just safer to avoid this by creating a weak reference to self and using that within the block.
First: The name property holds no relationship to MyClass, weak or otherwise. (That is, if you pass name to some arbitrary method, it doesn't carry any reference to the MyClass instance where it was a property.)
Second: Since you're simply executing the block rather than storing it, I don't see an opportunity for a retain cycle.
1: The MyClass instance has retained the name property, name property itself has no idea what is MyClass and therefore , there is nothing referring from String-name to the MyClass itself.
2: In the following code
-(void)doSomeStuff
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.name = #"Name changed inside block";
}];
}
self.name = #"Name changed inside block"; is the same as [self setName:#"Name changed inside block"];
So you are actually retaining MyClass instance inside Block and then executing its method to update the name , ( block needs pointer to this Name to change it , right ? block retains class object which contains this property ) , you are not retaining the property name itself.
But how will the name property relate to self? My guess is it will be a weak relationship.
The name property does not have a reference back to self, so the attributes strong and weak don't apply. An object instance is just a collection of instance variables, gathered into a struct. When we talk about object memory management, we are talking about the memory containing that struct. It doesn't make sense to say that a property (really the instance variable backing the property) has a reference to anything. It is simply one part of what self is.
My question: I know that self will hold the name property strongly. But how will the name property relate to self? What I mean is that I can access name as self.name but while class instantiation, how is the children of self (which in this case is name) related to self? I am imagining the structure of the class as a tree, with the parent holding strong reference to the children and the children holding a weak reference to the parent. I want to know if I am thinking about it correctly or not. My guess is it will be a weak relationship.
The children can have a reference to the parent and then it should be weak for the reasons you mentioned. But NSString instances does not have such an up-reference. So there cannot be a retain cycle.
In general it is up to you to manage such inverse relationship. (Core Data does it automatically in its default setters,if you insert a inverse relationship.) Nothing is done automatically, no definition of an up-reference, no setting of a up-reference.
My second question is: I am not referencing self directly inside my block. So, I guess there is no retain cycle here. But I am referencing name property which is held by self. So does this create a retain cycle?
You refer self inside the block, because you use it. Period.
But a retain cycle needs two references. As long as self is used inside the block, but the block is not stored in a property of self (directly or indirectly) no retain cycle can occur.