I'm using the Facebook sdk for ios and I'm facing an issue on how to safely set the delegate to nil. My facebook object is a property of the appDelegate and I'm using this object in multiple different objects to retrieve different information. So, say that object A, B and C conform to the FBRequest protocol and are set as the delegate of the facebook object. Now in the dealloc method of object A I want to make sure that object A is no longer a delegate, so I could do: facebookObj.delegate = nil. However, I feel that this would affect the other delegate objects B and C. Am I thinking correctly? Is there any way to remove only the object A from the facebook delegates without affecting objects B and C?
Hope the question makes sense.
Thanks very much.
I think you have an separate FBRequest object for each request that you fire off from your A, B and C objects, no? From what i understand, the FBRequest object only lives while the request is processing. Once its complete or fails the object goes away, no? Now of course, if you keep a reference to this request it will look like its there because you have an address to it but if you try sending messages it will crash (Unless using ARC with weak references which i doubt you are using).
Try this from each of your objects (A, B & C):
NSLog(#"FBRequest address %#", your_fbrequest_object);
And see if it always prints the same address or different ones.
Related
Following on from this question: Is there a reason that Swift array assignment is inconsistent (neither a reference nor a deep copy)? -
I have been playing with passing objects in Swift and noticed some strange results.
To clarify the kind of behaviour i'm used to (prior to Swift) would be that of Objective C.
To give an example in one of my Applications (written in Obj C) I have the concept of a 'notification list'. - really just an array of custom objects.
In that App I often pass my global array of 'notifications' to various viewControllers which provide a UI to update the list.
When I pass the global array to a child viewController I assign it to a local array variable in the recipient object. Then, simply by updating/changing the local array these changes are reflected in the global array on the rootViewController. I understand this behaviour is implicit in Objective C as objects as passed by reference, but this is really handy and I have been trying to replicate this behaviour in Swift.
However whilst I have been rewriting my App in Swift I've hit a wall.
I first tried to pass a Swift array of strings (not NSMutableArray) from the rootViewController to a child viewController (as described above).
Here is the behaviour when passing in the array of Strings the child viewController:
I Pass in:
[Bill, Bob, Jack] and then assign this passed array to a local array for local modification,
Then I append the String “Frank” to the local array
The results are:
Local array = [Bill, Bob, Jack, Frank]
Global array = [Bill, Bob, Jack]
No changes to the local array are reflected back to the global array. - The SAME result occurs for a change of element (without changing the length of the array.)
I have also tried the above experiment with a more real world example - passing in an array of my custom 'notification' objects to a child viewController. The SAME result occurs with none of the changes to the locally assigned array of custom objects being reflected to the original global array that was passed in.
This behaviour is not desirable to me, I assume the best practice here is to use delegate protocols to pass the modified array (or whatever object) back to the parent object and then to manually update the global array?? - if so this creates quite an extra workload over the Objective C style behaviour.
Finally I did try the inout keyword, which effectively lets you directly modify the function parameter var thats passed to the destination object.
Changes are reflected back to the global array (or object) However the problem is, if the input parameter is assigned to a local variable (to edit outside of scope of the init function) changes to the local variable are still not reflected in global scope.
I hope the above makes sense - It's really stifling my productivity with Swift.
Am I missing something or is this schizophrenic behaviour expected?
If so what is best practice on passing modified data back, delegates?
The linked question provides the answer - it is for performance.
The behaviour may not be desirable for you, but I would say that relying on side-effects from calling methods to modify parameters is the behaviour that is not considered desirable - particularly in a multi-threaded, multi-core environment where data structures can be corrupted.
A design that relies on side-effects is flawed, in my opinion.
If functions need to modify the "global" then they should either return the new value, or if that isn't possible then you should wrap your array inside an object and provide appropriate functions to manipulate the data values.
Swift blurs the lines between intrinsic and object somewhat with arrays, which makes it a little confusing - in Objective-C an NSMutableArray is an object so it always passed by reference.
For notifying other objects that the data has changed you can use an observer pattern. The typical delegate pattern only has a single registered delegate - With an observer pattern you can have multiple registered observers.
You can do this through NSNotificationCenter or an array of "delegates". The former has the advantage of decoupling the code more than delegation
Why don't you create a Model class that contains the array as a var. Add methods to the Model class to manipulate the array and store the new instance in the property. Create a single instance of the Model class at startup and pass it to the view controllers. They all access the array through the Model or through methods in the Model class. The behavior of Swift (where it copies the array on change of size) will be hidden from all of the view controllers.
i have a view controller object A, that asks another object B for internet data. Object B implements: connectionDidFinishLoading:connection:
When object B is ready and has its data, the method above is called. But i want to update the UI of object A. Whats the best way to do this? I think i must use a protocol?
There are different ways to handle this. Setting up object A to be the delegate of object B would be a perfectly valid way. you'd then define a protocol that object B would use to communicate back to Object A.
Another way to pass a completion block from object A to object B in the call that asks for the data. Then in object B's connectionDidFinishLoading:connection: method, it would check if the completion block is nil and if it's not nil, call the block.
I have been using the block-based approach more and more lately. There is less setup, and the completion code is right in the method in Object A that invokes Object B, so it's easier to follow what's going on.
You might want to set up a block that takes a success/failure parameter, as well as an NSError object. That way if the connection fails, you can handle it gracefully, and even report the error to the user if you decide to.
this is a very hard question to ask because I don't want to flood you with all of my code, being that most of it is not pertinent to the problem. So I won't be to surprised if this goes unanswered. Although, it could be something hella simple that I am missing haha. Here it goes:
So my app is storing an array via [encoder] in my appDelegate. The app is full of objects that are creates in a separate NSObject class. Think of it this way for examples sake:
I have my main viewController class. And in appDelegate I define/encode an array of objects.
Now in my main, I fill the array with 10 "cars". My car class has variables such as color, make, model, etc.. Now when I save and relaunch the app, the array that I have saved is now an array containing 10 elements, but it seems to have forgotten all of the variables for each instance of the car class.
When I relaunch the app, If I call NSLog(#"%#",array in appDelegate); It prints 10 lines that look a lot like this:
""
So I know the array is being stored with 10 elements, and 10 elements are saved, but like i said, all of the variables are gone.
Note: On the first run of the app, and the array is being filled for the first time, I can access all the variables perfectly, and nothing is wrong.
Thank you for any help that I can get!!
We need to see the code for your implementation of initWithCoder and encodeWithCoder on the "car" class. If you haven't implemented them, that's your problem.
What is probably happening currently is that only the superclass implementation of these methods is being invoked. This means the correct class will be recreated but no data will be saved or restored.
NSCoding protocol reference doc.
Your main class as well as all the objects you put into the array all need to conform to NSCoding. If the objects in the array aren't NSCoding compliant, they won't get coded automatically.
I came around a interesting issue while working on my app. Imagine the scenerio where
There exist one object, Lets call it A.
A, then creates an object(B) of some delegation based class, say NSURLConnection.
A assigned itself as delegate of B, provided A has implemented all the required delegate methods.
A asks B to start its processing. In our example i.e. fetching data from some server.
As soon as B finished fetching data, it will call some specified method of A.
In the last step, suppose while calling the methods of A, B finds that the A object doesnt exist anymore. Then what happens???
I'm not sure but does it cause crash?
If yes, then please suggest me how to avoid the situation.
In my case I assigned the viewcontroller as delegate of some object, say X in viewDidLoad method. There are cases when viewcontroller get destroyed before X calls the delegate methods defined in the viewcontroller.
If assigning X's delegate to nil solves the problem. Then where
should i do that.
In short, which method is called only once while unloading phase of view controller likewise
viewDidLoad in its loading phase.
The best way to achieve this kind of communication between classes (where class A could be deallocated at any time) is listening to NSNotifications.
As you stated , using weak(assign) delegates is dangerous and requires extra thought.
Using strong delegates could as well create a memory bloat (why should we retain a view controller so long after popping it from the view anyway?).
For more on NSNotificationCenter and notifications , you can find a lot of info in the SDK docs.. for specific questions, you know where to ask..
You should not reach a situation where one object holds a reference to another object which may be deallocated somewhere else without the owner object being notified.
Either when deallocating object A notify object B (by making member a nill in object B for example) or modify your design/flow to never allow A to be deallocated before B finishes (e.g. retain A when assigning as a delegate in B if possible)
Checking against a valid delegate object should be sufficient enough.
if (delegate)
[delegate method];
I've read that an object can only have one delegate at once.
But is that really true?
Let's say I make an object with a protocol and from that object I want to gather a lot of data from several other objects. I add every object that conforms to my protocol to an array. Then I just loop through it and call my methods on every delegate.
NSMutableArray *collectFromDelegates = [NSMutableArray alloc]init];
//in delegateArray I keep pointers to every delegate.
for(id delegate in delegateArray){
[collectFromDelegates addObject:[delegate someProtocolMethod]];
}
Is this wrong?
That's not really delegation.
Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled.
It doesn't make much sense to have more than one object handle an event for you, since it has already been handled. The only reason I could see to have multiple delegates is that if the first fails to handle an event, it can be passed to the next, continuing until some object handles it.
In your example, the objects are acting as data sources. This makes more sense than multiple delegates, but could easily be implemented by having a single data source combine data from multiple objects, which means the object asking for the data doesn't have to worry about how to combine it.
The other case where you would often want multiple objects is receiving notifications of an event. This is not delegation because the objects are not working for the object, just acting on something that happened to the object. This is better implemented using notifications or observing.
Apple's convention is to only have one "delegate" object. But you can set up your own class to have an array of delegates if that's what you need. You might want to call them something else for clarity.
In your example, calling them "dataSources" might be more appropriate.
A class only really needs one delegate, if you have more than one you are solving a different problem. The delegate pattern is used to modify the behaviour of a class. Say for instance we have a Dog class which can bark, but different types of dogs bark in different way. A delegate would be one way of changing the barking behaviour.
If you need more than one you are probably more interested in OBSERVING what your class is doing, it needs to NOTIFY others of current EVENTS. As several other classes might be interested in the behaviour of one you would need an array. In iOS SDK this is already done for you with notifications. This is called the Observer pattern.
Different use cases...
I've read that an object can only have one delegate at once. But it's that really true?
Where did you read that? No, it's not true. For instance, UITableView has two delegates, one to supply the data, the other to handle actions.
A delegate is just an abstract concept - you can have as many delegates as you want. However, this is rarely required and often a poor pattern.
Apple make good use of a source and delegate pattern. Source ivars (a form of delegate) provide data, while delegate ivars are invoked for logical responses. Perhaps this is a better solution?
Alternatively you can use NSNotification to inform many listeners of a single event.
Hope this helps!
Generally, when you want to message multiple classes that are interested in what you class does, you would use NSNotifications. That will however not allow them to return data unless you allow them to send a message to the object of the notification. I'm not sure if that would be a cleaner solution though.
One approach beside the mentioned Notifications could be, that your delegate implementation holds an array of objects conforming to the protocol and calls the protocols method on this as a wrapper.