问题
I occasionally instantiate a class from my view controller by passing in the view controller instance itself so that the objects that I create can invoke methods of the controller to update the view.
Is that always, often, or never a bad practice?
Concretely:
ViewController.h has
-(void)updateButtonValue:(NSString*)value;
MyObject.h has
-(id)initWithViewController:(ViewController*)aViewController;
I instantiate that class from my view controller with:
[[MyObject alloc] initWithViewController:self];
thus allowing that MyObject instance to update a button value in my view by a simple call like:
MyObject.m
[self.viewController updateButtonValue:@"example"];
It does not seem ideal since I am passing to MyObject much more (the view controller itself) than it should need, but it is certainly quick and functional. If there is a cleaner approach, such as relying on protocols, that is also succinct, a brief code sample would be much appreciated.
回答1:
It is always bad practice to pass a class-typed pointer in, as you are tightly coupling your objects together (each object needs to know the class of the other, they might as well be a single object). This is what the delegate pattern is for. It minimises the info MyObject needs (minimally, nothing more than a pointer type id
- preferably, a protocol specified by MyObject to offer it some behavioural guarantees)
So to translate your example
MyObject.h
replace...
-(id)initWithViewController:(ViewController*)aViewController;
with...
-(id) init;
(which you can dispense with if you have no further reason to override)
and...
@property (nonatomic, weak) id delegate;
Instantiation in myViewController (which does need to #include MyObject
) ...
MyObject* object = [[MyObject alloc] init];
Followed by
object.delegate = self;
(Note that object
gets a pointer to myViewController without needing to know anything else about it)
Now you can do this from inside object
:
[self.delegate updateButtonValue:@"example"];
However ... you will want to ensure that your delegate can receive the message updateButtonValue
:
To do this, you declare a protocol in MyObject.h with the signature of this method
@protocol MyObjectDelegate
- (void) updateButtonValue:(NSString*)string;
@end
And in your viewController, declare that you conform to this protocol using <> in the interface line
@interface ViewController <MyObjectDelegate>
(this is no big deal, ViewController already has to #include MyObject
to alloc/init it, so needs no more info to do this)
And expand your property declaration thus:
@property (nonatomic, weak) id <MyObjectDelegate> delegate
Now you have given the compiler enough information for it to ensure that you can only pass conformant messages around. The brilliant thing here is that MyObject can confidently pass messages to MyViewController without needing to know anything about MyViewController other than that it is reached via the delegate pointer.
来源:https://stackoverflow.com/questions/14866979/is-passing-a-controller-in-a-construtor-always-a-bad-practice