When writing an abstract class, or a class that doesn't get instantiated directly... do you tend to write a dealloc method in the abstract class and release where appropriate, and then allow for children to call [super dealloc] and then worry about only instance variables they add which aren't part of the super class?
How do you folks manage memory with abstract classes?
I'm thinking something along the lines of:
#interface ClassA : NSObject {
NSArray *foo;
}
#end
#implementation ClassA
- (void) dealloc {
[foo release];
[super dealloc];
}
#end
#interface ClassB : ClassA {
NSArray *bar;
}
#end
#implementation ClassB
- (void) dealloc {
[bar release];
[super dealloc];
}
#end
Please pardon any syntactical errors, I just wrote this up on the fly. Does the above make sense or should memory be managed differently? GC isn't an option as I'm doing this on the iPhone.
Yes, you take responsibility for yourself, not for super or subclasses.
Saying the same thing as Stephan, but from a different angle: Avoid putting alloc and release in different places as much as possible (init and dealloc being the main exceptions). That goes double for putting them in different classes, as in your case of a class and its superclass.
The superclass should not release objects that its subclasses create.
Related
it seem like when messaging a super class from a subclass that has overridden some methods, you can't get the "original" implementation by just using super. any work arounds or should i init that super class?
here some code showing what i mean:
#interface ClassA : NSObject
- (void)method1;
#end
#implementation ClassA
- (void)method1
{
[self method2];
}
- (void)method2
{
NSLog(#"Hello it's ClassA");
}
#end
#interface ClassB : ClassA
- (void)method3;
#end
#implementation ClassB
- (void)method2 // overriding method2
{
NSLog(#"Hello it's ClassB");
}
- (void)method3
{
[self method1]; // logs "Hello it's Class B" as expected
[super method1]; // still logs "Hello it's ClassB" instead of "Hello it's ClassAā€¯!?
}
#end
thanks in advance for any help :)
It is behaving as expected; you've instantiated an instance of ClassB and, this, when method1 calls [self method2], the method dispatch follows the normal lookup path. self is an instance of ClassB.
Copy/paste this into all your methods:
NSLog(#"%s %p %#", __PRETTY_FUNCTION__, self, self);
That should make it clear what is going on.
any work arounds?
Yes, don't do this as it is poor design. A superclass second guessing inheritance is a sure fire way to end up with an unmaintainable mess of a code base.
Is there any way to delegate to two objects at a time in Objective-C? I know that delegation pattern implies one response at a time and for multiple listeners and broadcasting there is notification center but notification does not return any value.
If I have a heavily network-based iOS project and need to delegate to multiple listeners and required to return values from them, in this scenario what approach should be the best?
In every class the delegate is one, so one delegate is informed about the event. But nothing forbids you to declare a class with a set of delegates.
Or use Observation instead. A class may be observed by multiple classes.
Example
As requested from the OP, since also some code would be useful, here is a way of doing it:
#interface YourClass()
#property (nonatomic, strong, readwrite) NSPointerArray* delegates;
// The user of the class shouldn't even know about this array
// It has to be initialized with the NSPointerFunctionsWeakMemory option so it doesn't retain objects
#end
#implementation YourClass
#synthesize delegates;
... // other methods, make sure to initialize the delegates set with alloc-initWithOptions:NSPointerFunctionsWeakMemory
- (void) addDelegate: (id<YourDelegateProtocol>) delegate
{
[delegates addPointer: delegate];
}
- (void) removeDelegate: (id<YourDelegateProtocol>) delegate
{
// Remove the pointer from the array
for(int i=0; i<delegates.count; i++) {
if(delegate == [delegates pointerAtIndex: i]) {
[delegates removePointerAtIndex: i];
break;
}
} // You may want to modify this code to throw an exception if no object is found inside the delegates array
}
#end
This is a very simple version, you can do it in another way. I don't suggest to make public the delegates set, you never know how it could be used, and you can get an inconsistent state, specially with multithreading. Also, when you add/remove a delegate you may need to run additional code, so that's why making the delegates set private.
You may also a lot of other methods like delegatesCount for example.
PS: The code has been edited to be a NSPointerArray instead of a NSMutableSet, because as stated in the comments a delegate should be held with a weak pointer to avoid retain cycles.
In addition to Ramys answer you could use a [NSHashTable weakObjectsHashTable] instead of a
NSMutableSet. This would keep only a weak reference to your delegates and prevents you from running into memory leaks.
You will get the same behavior you already know from standard weak delegates #property (nonatomic, weak) id delegate;
#interface YourClass()
#property (nonatomic, strong) NSHashTable *delegates;
#end
#implementation YourClass
- (instancetype)init
{
self = [super init];
if (self) {
_delegates = [NSHashTable weakObjectsHashTable];
}
return self;
}
- (void) addDelegate: (id<YourDelegateProtocol>) delegate
{
// Additional code
[_delegates addObject: delegate];
}
// calling this method is optional, because the hash table will automatically remove the delegate when it gets released
- (void) removeDelegate: (id<YourDelegateProtocol>) delegate
{
// Additional code
[_delegates removeObject: delegate];
}
#end
Robbie Hanson wrote a multicast delegate implementation. Looks like what you need. He talks about it in more detail here, and how it is used in the XMPPFramework. He has some good discussion about one of the main problems which is how to handle the case where the multiple delegates implement a given method who's return value determines the class' behaviour (and the multiple delegates return different values). Relevant bits:
What is a MulticastDelegate?
The xmpp framework needs to support an unlimited number of extensions.
This includes the official extensions that ship with the framework, as
well as any number of extensions or custom code you may want to plug
into the framework. So the traditional delegate pattern simply won't
work. XMPP modules and extensions need to be separated into their own
separate classes, yet each of these classes needs to receive delegate
methods. And the standard NSNotification architecture won't work
either because some of these delegates require a return variable.
(Plus it's really annoying to extract parameters from a notification's
userInfo dictionary.)
So a MulticastDelegate allows you to plug into the framework using the
standard delegate paradigm, but it allows multiple classes to receive
the same delegate notifications. The beauty of this is that you don't
have to put all your xmpp handling code in a single class. You can
separate your handling into multiple classes, or however you see fit.
If you're writing the function that will call the delegates, you can have as many as you want. But if you're using a class (that you can't change) that calls the delegates, then you can't have more delegates than the class supports.
You could, if it worked out for you, have one delegate call another. Set up the first delegate so it will call the second delegate (whose pointer is stored in the first delegate object). This can be simple, with it pre-defined as to which calls are "passed on", or quite complex, using the dynamic call mechanisms of Objective-C.
One delegate can be setting for only one object but it's possible to store delegates in array.
Variant of Ramy Al Zuhouri is good but I want to say that it may be a problem to release delegates from array because NSArray (like NSMutableArray) classes retain all added objects but delegate in most cases is an assign property without retainCount. Retaining the delegate can bring to consequences that class with delegate implementation will have retainCount + 1.
Solution of this is store delegates in NSMutableArray like pointers to delegate methods.
I'm using singletone class with delegate header.
//YourClass.h file
#protocol YourDelegateProtocol <NSObject>
-(void)delegateMethod;
#end
#interface YourClass : NSObject
+(YourClass *)sharedYourClass;
- (void) addDelegate: (id<YourDelegateProtocol>) delegate;
- (void) removeDelegate: (id<YourDelegateProtocol>) delegate
#end
//YourClass.m file
#interface YourClass()
#property (nonatomic, retain) NSMutableArray *delegates;
-(void)runAllDelegates;
#end
#implementation YourClass
#synthesize delegates = _delegates;
static YourClass *sharedYourClass = nil;
+(YourClass *)sharedYourClass {
if (!sharedYourClass || sharedYourClass == nil) {
sharedYourClass = [YourClass new];
sharedYourClass.delegates = [NSMutableArray array];
}
return sharedYourClass;
}
-(void)addDelegate: (id<YourDelegateProtocol>) delegate{
NSValue *pointerToDelegate = [NSValue valueWithPointer:delegate];
[_delegates addObject: pointerToDelegate];
}
-(void)removeDelegate: (id<YourDelegateProtocol>) delegate{
NSValue *pointerToDelegate = [NSValue valueWithPointer:delegate];
[_delegates removeObject: pointerToDelegate];
}
-(void)runAllDelegates{
//this method will run all delegates in array
for(NSValue *val in sharedYourClass.delegates){
id<YourDelegateProtocol> delegate = [val pointerValue];
[delegate delegateMethod];
}
}
-(void)dealloc{
sharedYourClass.delegates =nil;
[sharedYourClass release], sharedYourClass =nil;
[super dealloc];
}
#end
//YourClassWithDelegateImplementation.h file
#include "YourClass.h"
#interface YourClassWithDelegateImplementation : NSObject <YourDelegateProtocol>
#end
//YourClassWithDelegateImplementation.m file
#implementation YourClassWithDelegateImplementation
-(id)init{
self = [super init];
if(self){
//...your initialization code
[[YourClass sharedYourClass] addDelegate:self];
}
return self;
}
-(void)delegateMethod{
//implementation of delegate
}
-(void)dealloc{
[[YourClass sharedYourClass] removeDelegate:self];
[super dealloc];
}
#end
If you want to call callbacks for classes B and C from a class A with only one delegate, you could create a delegate wrapper DWrap which has references to the classes B and C. Then class A calls the callbacks on B and C through DWrap.
Does calling [super init] do the same thing in a category as a subclass? If not, what's the difference?
In order to understand this, it's probably important to understand the way an object is stored during runtime. There is a class object1, which holds all the method implementations, and separately, there is a structure with the storage for the instance's variables. All instances of a class share the one class object.
When you call a method on an instance, the compiler turns that into a call to objc_msgSend; the method implementation is looked up in the class object, and then run with the instance as an argument.
A reference to super takes effect at compile time, not run time. When you write [super someMethod], the compiler turns that into a call to objc_msgSendSuper instead of the usual objc_msgSend. This starts looking for the method implementation in the superclass's class object, rather than the instance's class object.2
A category simply adds methods to the class object; it has little or no relation to subclassing.
Given all that, if you refer to super inside of a category, it does indeed do the same thing that it would inside of a class -- the method implementation is looked up on the class object of the superclass, and then run with that instance as an argument.
Itai's post answers the question more directly, but in code:
#interface Sooper : NSObject {}
- (void) meth;
#end
#interface Sooper ()
- (void) catMeth;
#end
#interface Subb : Sooper {}
- (void) subbMeth;
#end
#interface Subb ()
- (void) catSubbMeth;
#end
#implementation Sooper
- (void) meth {
[super doIt]; // Looks up doIt in NSObject class object
}
- (void) catMeth {
[super doIt]; // Looks up doIt in NSObject class object
}
#end
#implementation Subb
- (void) subbMeth {
[super doIt]; // Looks up doIt in Sooper class object
}
- (void) catSubbMeth {
[super doIt]; // Looks up doIt in Sooper class object
}
#end
1 See Greg Parker's writeup [objc explain]: Classes and meta-classes
2One important thing to note is that the method doesn't get called on an instance of the superclass. This is where that separation of methods and data comes in. The method still gets called on the same instance in which [super someMethod] was written, i.e., an instance of the subclass, using that instance's data; it just uses the superclass's implementation of the method.
So a call to [super class] goes to the superclass object, finds the implementation of the method named class, and calls it on the instance, transforming it into the equivalent of [self theSuperclassImplementationOfTheMethodNamedClass]. Since all that method does is return the class of the instance on which it was called, you don't get the superclass's class, you get the class of self. Due to that, calling class is kind of a poor test of this phenomenon.
This whole answer completely ignores the message-passing/method call distinction. This is an important feature of ObjC, but I think that it would probably just muddy an already awkward explanation.
No, they do different things. Imagine a class structure like this: NSObject => MyObject => MySubclass, and say you have a category on MyObject called MyCategory.
Now, calling from MyCategory is akin to calling from MyObject, and therefore super points to NSObject, and calling [super init] invokes NSObject's -init method. However, calling from the subclass, super points to MyObject, so initializing using super invokes MyObject's -init method, which, unless it isn't overridden, behaves differently from NSObject's.
These two behaviors are different, so be careful when initializing using categories; categories are not subclasses, but rather additions to the current class.
Given the below example, super will call UIView init (not UINavigationBar init method)
#implementation UINavigationBar (ShadowBar)
- (void)drawRect:(CGRect)rect {
//draw the shadow ui nav bar
[super init];
}
#end
If you subclass it, [super init] will call UINavigationBar init method.
So yes, if there are additional things you will do in UINavigationBar init (extra from UIView) they do different things.
Edit: the following is built on a flawed premise, please look at josh's answer.
not deleting, still an interesting reference for something that could potentially lead you astray.
They are the same thing... without referencing any outside dicussions we may have had where you stated that I should ..."answer an academic question with an academic answer"
#implementation categoryTestViewController (ShadowBar)
- (void)viewDidAppear:(BOOL)animated {
//draw the shadow ui nav bar
NSLog(#"super's class = %#, self's class %#",[super class],[self class]);
if ([self class] == [super class]) {
NSLog(#"yeah they are the same");
}
}
#end
outputs:
2011-05-29 08:06:16.198 categoryTest[9833:207] super's class = categoryTestViewController, self's class categoryTestViewController
2011-05-29 08:06:16.201 categoryTest[9833:207] yeah they are the same
and calling the [super viewDidAppear:] will result in calling nothing... not a loop, so I don't know what it is really doing there.
I have three Objective-C classes:
#interface ClassA : NSObject{
IBOutlet id<ClassAProtocol>delegate
//other instance variables
}
//methods
#end
#interface ClassB : ClassA{
//instance variables
}
//methods
#end
#interface ClassC : ClassA{
//instance variables
}
//methods
#end
My objective is so that when an instance of ClassA is called for in either code or InterfaceBuilder, an instance of ClassB or ClassC is actually created. Whether it will be ClassB or ClassC depends on a return value of a method in ClassAProtocol as implemented by the delegate object.
#implementation ClassA
static BOOL _initFromSubclass = NO;
-(id)init{
if(_initFromSubclass){
_initFromSubclass = NO;
self = [super init];
}else {
_initFromSubclass = YES;
if([delegate shouldInitClassB]){
self = [[ClassB alloc] init];
}else{
self = [[ClassC alloc] init];
}
}
return self;
}
//other methods
#end
This doesn't work the way I wanted because at the init call, the delegate (set in Interface Builder) is still nil, and so the object created is always ClassC. Also, a ClassA object is created first, then, in its init call, creates a new ClassC object, with a different memory address, and no ClassA object is dealloced. I have three questions:
1)What happens to the original ClassA object? (I think it's leaked, but I want to know).
2)How do I avoid the leak?
3)How do I accomplish what I actually want? By the time the delegate is set (say, in awakeFromNib method), it's too late to reset the object.
Yes I think it will be leaked because it has a retain count of +1 after the alloc but nothing will release it.
You can use [self release] before reassigning the value of self. Some argue that it should be [self dealloc] directly, but I personally prefer the former.
Objects instantiated from nibs are sent initWithCoder: messages I think, so override that instead of init. Although, at this point, I'm still not sure whether delegate will be set.
I have read a number of snippets that mention you should never use dot-notation within your init or dealloc methods. However, I can never seem to find out why. One post did mention in passing that it has to do with KVO, but no more.
#interface MyClass : NSObject {
SomeObject *object_;
}
#property (nonatomic, retain) SomeObject *object;
#end
This implementation is bad?
#implementation MyClass
#synthesize object = object_;
- (id)initWithObject:(SomeObject *)object {
if (self = [super init]) {
self.object = object;
}
return self;
}
#end
But this is good?
#implementation MyClass
#synthesize object = object_;
- (id)initWithObject:(SomeObject *)object {
if (self = [super init]) {
object_ = [object retain];
}
return self;
}
#end
What are the pitfalls of using dot-notation inside your init?
Firstly, it's not the dot notation specifically, it's the accessors that you shouldn't use.
self.foo = bar;
is identical to
[self setFoo: bar];
and they are both frowned upon within init/dealloc.
The main reason why is because a subclass might override your accessors and do something different. The subclass's accessors might assume a fully initialised object i.e. that all the code in the subclass's init method has run. In fact, none of it has when your init method is running. Similarly, the subclass's accessors may depend on the subclass's dealloc method not having run. This is clearly false when your dealloc method is running.
The reasons I've heard mainly crop up due to when you write your own setters/getters. When using the default #synthesized versions of the methods it won't cause much of an issue. When you write your own setter though, it is generally going to have a sideeffect on your class. This sideeffect is probably not wanted in the init, or even is going to cause issues if it references other ivars that haven't been created yet. Same issue in the dealloc, if you have a sideeffect, it has potential to blow up.