Is there a way to turn a weak reference into a strong one?

290 Views Asked by At

I have an object that is set as the delegate of another object, whose delegate property is weak.

- (YYService *)service
{
  XXHandler *handler = [[XXHandler alloc] init];

  // YYService's "delegate" property is weak
  return [[YYService alloc] initWithDelegate:handler];

  // The XXHandler is deallocated because there are no strong references to it
}

Since nothing else references the delegate it ends up getting deallocated, but I want it to live for as long as the parent object does as if the parent had a strong reference to its delegate. Is there a simple way to accomplish this?

1

There are 1 best solutions below

0
On

The easy why to "solve" that problem is to subclass YYService, giving the subclass an additional strong property and set that one in -initWithDelegate:.

But this "solution" would deepen a problem in your design instead of solving that.

Let's have a look, why delegates are usually hold weakly:

The delegating class has a general – or no – behavior which might not fit in the class' user's case, i. e. if something happens. (An operation completes, an error occurs, $whatever) So the delegating class gives you the opportunity to customize the behavior including running custom code. Delegating is in competition with subclassing, but in difference to subclassing is on a per instance basis (instead of a per class basis) and at run time (instead of compile time).

Because it works on per instance basis, the instance creating the delegate typically holds the delegating instance strongly. This code knows the customization that should apply to the delegating instance:

-(void)createDelegate
{
  self.delegating = [Delegating new]; // I create and hold the instance strongly
  delegating.delegate = self;         // I customize it
}

Then the delegating instance cannot hold the delegate strongly, because this would be a retain cycle.

In your snippet that does not work, because -service returns the newly created delegating instance. And even it would be possible to return both instances, I wouldn't like it, because creating the delegating object and installing the delegate would be a two-step operation, even it is semantically a one-stepper. So If you do not have self as the delegate, you should do the whole installation process in one method:

-(void)installService
{
  self.handler = [[XXHandler alloc] init]; // Hold the handler strongly
  self.service = [[YYService alloc] initWithDelegate:handler];
}

If you do not know the concrete instance object acting as delegate, pass it as argument:

-(void)installServiceWithDelegate:(id)delegate
{
  self.delegate = delegate;
  self.service = [[YYService alloc] initWithDelegate:delegate];
}

…

[self installServiceWithDelegate:[YourConcreteClass new]];

But you should not try to turn things upside down or inside out.