I have a UIViewController subclass (say MyViewController).
MyViewController.h
#protocol TargetChangedDelegate
-(void) targetChanged;
#end
#interface MyViewController
#property (weak) id<TargetChangedDelegate> targetChangedDelegate;
-(void) doSomethingOnYourOwn;
#end
MyViewController.m
#implementation MyViewController <TargetChangedDelegate>
-(void) doSomethingOnYourOwn
{
// DO some stuff here
// IS THIS BAD ??
self.targetChangedDelegate = self;
}
-(IBAction) targetSelectionChanged
{
[self.targetChangedDelegate targetChanged];
}
-(void) targetChanged
{
// Do some stuff here
}
#end
Based on certain conditions a class that instantiates an instance of MyViewController may decide to set itself as the delegate or not.
Foo.m
#property(strong) MyViewController *myVC;
-(void) configureViews
{
self.myVC = [[MyViewController alloc] init];
[self.view addSubview:self.myVC];
if (someCondition)
{
self.myVC.targetChangedDelegate = self;
}
else
{
[self.myVC doSomethingOnYourOwn]
//MyViewController sets itself as the targetChangedDelegate
}
}
With reference to the code snippet above, I have the following question:
Is it a violation of MVC/delegation design pattern (or just a bad design) to say:
self.delegate = self;
There's absolutely no problem with setting the delegate to self. In fact it is a good way to provide default delegate functionality if a delegate is not set by somebody else.
Obviously, the delegate property has to be declared weak otherwise you get a reference cycle.
To expand a bit, having read the wrong answer and wrong comments above, if you allow an object to be its own delegate, your code is cleaner because you do not have to surround absolutely every single delegate call with
if ([self delegate] != nil)
{
[[self delegate] someMethod];
}
else
{
[self someMethod];
}
Its not proper way to assign self.delegate = self.
for your functionality, you can do this:
-(void) doSomethingOnYourOwn
{
// DO some stuff here
self.targetChangedDelegate = nil;
}
and when using delegate:
if(self.targetChangedDelegate != nil && [self.targetChangedDelegate respondsToSelector:#selector(targetChanged)]
{
[self.targetChangedDelegate targetChanged];
}
else
{
[self targetChanged];
}
It is bad design to set self.delegate = self; it should be another object. Delegation via protocols are an alternative design to subclassing and you can read more about delegation here:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
And here is more on protocols:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Protocol.html
Related
I am calling the block from second class which has been declared and maintained in first class.
In ViewController.h
#property (copy) void (^simpleBlock)(NSString*);
In View Controller.m
- (void)viewDidLoad {
[super viewDidLoad];
self.simpleBlock = ^(NSString *str)
{
NSLog(#"Hello My Name is: %#",str);
};
}
In SecondViewController.m
In ViewDidload
ViewController *VC = [[ViewController alloc]init];
VC.simpleBlock(#"Harjot");//bad execution error
Please suggest me some solutions because the code is giving me bad execution error.
How can i call the block in any another way?
It's the correct way of run the block. However if you try to run a block that is nil you'll have a crash - so you should always check that it's not nil before calling it:
ViewController *vc = [[ViewController alloc] init];
if (vc.simpleClock) {
vc.simpleBlock(#"Harjot");//this will not get called
}
The reason why in your case the block is nil is because you set it in viewDidLoad - however viewDidLoad is not called until its view is ready to go on screen. For testing purposes try to move the assignment from viewDidLoad to init and this should work:
- (instancetype)init
{
self [super init];
if (self) {
_simpleBlock = ^(NSString *str)
{
NSLog(#"Hello My Name is: %#",str);
};
}
return self;
}
I am currently working on an implementation where many blocks are used. Every block needs to communicate with self.
Currently I am doing this:
#implementation Foo
- (void) bar
{
__weak Foo *weakSelf = self;
[self doBlockStuff:^(id something) {
[weakSelf doSomething];
}];
}
#end
I have many functions that do the same with the weak instantiation.
Is it right to instantiate the weak property once in the interface block and use it everywhere?
It's working but is it an accepted practice?
#interface Foo ()
{
__weak Foo *_weakSelf;
}
#end
#implementation Foo
-(instancetype) init
{
self = [super init];
if(self) {
_weakSelf = self;
}
return self;
}
- (void) bar1
{
[self doBlockStuff:^(id something) {
[_weakSelf doSomething];
}];
}
- (void) bar2
{
[self doBlockStuff:^(id something) {
[_weakSelf doSomething];
}];
}
- (void) bar3
{
[self doBlockStuff:^(id something) {
[_weakSelf doSomething];
}];
}
- (void) bar4
{
[self doBlockStuff:^(id something) {
[_weakSelf doSomething];
}];
}
#end
Edit after Testing with new Informations:
I did wrote a little test case and now i can demonstrate why the second one is not working.
In my Testclass a imake a dispatch after 5 seconds with the relevant self usage and i logged when my dealloc was called.
#implementation Foo
- (void)dealloc
{
NSLog(#"dealloc");
}
- (instancetype)init
{
self = [super init];
if (self) {
}
return;
}
- (void)bar
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self doSomething];
});
}
#end
If the class loses the holder, because the controller is closed or whatever and the function is still running, the class will dialoged after the dispatch is done.
#interface Foo ()
{
__weak Foo *_weakSelf;
}
#end
#implementation Foo
- (void)dealloc
{
NSLog(#"dealloc");
}
- (instancetype)init
{
self = [super init];
if (self) {
_weakSelf = self;
}
return;
}
- (void)bar
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_weakSelf doSomething];
});
}
#end
This one will also on dealloc if the dispatch is done. Because the _weakSelf property is still holing by the class, a shorthand for using self->_weak. Self means self :)
#implementation Foo
- (void)dealloc
{
NSLog(#"dealloc");
}
- (instancetype)init
{
self = [super init];
if (self) {
}
return;
}
- (void)bar
{
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf doSomething];
});
}
#end
this one will dealloc immediately because the weak reference is only existing in this function given to the block. The function is over and if the class loses his reference the block has no property that is holing anyone. But the weak property is still usable when the reference class is available.
To be sure, that this weak property will be alive, we can set a strong cycle in the block.
This doesn't at all do what you think it does. That __weak instance variable in those methods? That's just a shorthand for self->_weak. All of those methods using the proposed manner still capture self strongly.
Stick to what you were doing before.
This is really bad then if the __weakSelf still holds a strong reference. The question is that if instantiated in the method and used in the method does it still really have a weak anchor to self or is it holding a strong reference in that very moment. Based on the documentation you can instantiate a weak reference outside the block and even make it strong inside the block if you want. Take a look here
http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html
my question at this point in time is that why does a weak self instantiated dealloc it's self when the real self deallocs..? Because I tried to hold the weak self in another viewController and then dealloc the real self. But the weak self was dealloc as soon as I dealloc the real self.
I have a class named IGMapViewController
In that I have
static IGMapViewController *instance =nil;
+(IGMapViewController *)getInstance {
#synchronized(self) {
if (instance==nil) {
instance= [IGMapViewController new];
}
}
return instance;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// more code
instance = self;
}
return self;
}
If use the object in more then 1 class but only use initWithNibName in one class.
In a class named IGRouteController in the init method i use _mapViewController = [IGMapViewController getInstance]; this happens before the initWithNibName gets executed in another class.
In IGRouteController I have a method updateRouteList in that method I use:
[_mapViewController drawSuggestedRoute:suggestedRoute];
It all does run but I can't see the result.
If i use:
IGMapViewController *wtf = [IGMapViewController getInstance];
[wtf drawSuggestedRoute:suggestedRoute];
Then it does work great.
So is it possible to get a instance and init later with a nib?
I believe I see what you are trying to accomplish. You want to initialize a singleton instance of your class from a nib. Correct?
When you initialize your instance, you are using [IGMapViewController new] which is presumably not the intended behavior. How about this (untested...)?
+ (id)sharedController
{
static dispatch_once_t pred;
static IGMapViewController *cSharedInstance = nil;
dispatch_once(&pred, ^{
cSharedInstance = [[self alloc] initWithNibName:#"YourNibName" bundle:nil];
});
return cSharedInstance;
}
clankill3r,
You should avoid creating singleton UIViewControllers (see comments in this discussion UIViewController as a singleton). This has been also highlighted by #CarlVeazey.
IMHO, you should create a UIViewController each time you need it. In this case your view controller would be a reusable component. When you create a new instance of your controller, just inject (though a property or in the initializer the data you are interested in, suggestedRoute in this case).
A simple example could be the following:
// YourViewController.h
- (id)initWithSuggestedRoute:(id)theSuggestedRoute;
// YourViewController.m
- (id)initWithSuggestedRoute:(id)theSuggestedRoute
{
self = [super initWithNibName:#"YourViewController" bundle:nil];
if (self) {
// set the internal suggested route, e.g.
_suggestedRoute = theSuggestedRoute; // without ARC enabled _suggestedRoute = [theSuggestedRoute retain];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self drawSuggestedRoute:[self suggestedRoute]];
}
For further info about UIViewControllers, I really advice to read two interesting post by #Ole Begemann.
Passing Data Between View Controllers
initWithNibName:bundle: Breaks Encapsulation
Hope that helps.
I want to implement a UIScrollView subclass to present some custom formatted content. I just set a model object property of the scroll view and it handles all the required layout and rendering to display the content.
This works fine, but now I'd like to include zooming. According to the documentation, to support zooming you have to set a delegate and implement the viewForZoomingInScrollView: method. I guess I could set the delegate to the scroll view itself and implement that method in the subclass. But doing that I would lose the ability to have an external delegate (like an encapsulating UIViewController) that can be notified about scroll events.
Assuming the documentation is right and there is absolutely no (documented) way to implement zooming without a delegate, how could I still retain the possibility of having a regular, unrelated delegate?
Building upon H2CO3's suggestion of saving a hidden pointer to the real delegate and forwarding all incoming messages to it, I came up with the following solution.
Declare a private delegate variable to store a reference to the "real" delegate that is passed in to the setDelegate: method:
#interface BFWaveScrollView ()
#property (nonatomic, weak) id<UIScrollViewDelegate> ownDelegate;
#end
Set the delegate to self to be notified about scrolling events. Use super, so the original setDelegate: implementation is called, and not our modified one.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[super setDelegate:self];
}
return self;
}
Override setDelegate: to save a reference to the "real" delegate.
- (void)setDelegate:(id<UIScrollViewDelegate>)delegate {
_ownDelegate = delegate;
}
When the UIScrollView tries to call a method of its delegate, it will first check to see if the delegate respondsToSelector:. We have to forward this to the real delegate if the selector is part of the UIScrollViewDelegate protocol (Don't forget to #import <objc/runtime.h>).
- (BOOL)selectorIsScrollViewDelegateMethod:(SEL)selector {
Protocol *protocol = objc_getProtocol("UIScrollViewDelegate");
struct objc_method_description description = protocol_getMethodDescription(
protocol, selector, NO, YES);
return (description.name != NULL);
}
- (BOOL)respondsToSelector:(SEL)selector {
if ([self selectorIsScrollViewDelegateMethod:selector]) {
return [_ownDelegate respondsToSelector:selector] ||
[super respondsToSelector:selector];
}
return [super respondsToSelector:selector];
}
Finally, forward all delegate methods to the real delegate that are not implemented in the subclass:
- (id)forwardingTargetForSelector:(SEL)selector {
if ([self selectorIsScrollViewDelegateMethod:selector]) {
return _ownDelegate;
}
return [super forwardingTargetForSelector:selector];
}
Don't forget to manually forward those delegate methods that are implemented by the subclass.
I'd abuse the fact that I'm being a subclass (on purpose :P). So you can hack it. Really bad, and I should feel bad for proposing this solution.
#interface MyHackishScrollView: UIScrollView {
id <UIScrollViewDelegate> ownDelegate;
}
#end
#implementation MyHackishScrollView
- (void)setDelegate:(id <UIScrollViewDelegate>)newDel
{
ownDelegate = newDel;
[super setDelegate:self];
}
- (UIView *)viewForScrollingInScrollView:(UIScrollView *)sv
{
return whateverYouWant;
}
// and then implement all the delegate methods
// something like this:
- (void)scrollViewDidScroll:(UIScrollView *)sv
{
[ownDelegate scrollViewDidScroll:self];
}
// etc.
#end
Maybe this is easier to read and understand a couple of weeks later :)
(sample code for intercepting locationManager:didUpdateLocations: in a subclass)
Other than that the same handling for setting self as delegate to the superclass and intercepting setDelegate in order to save the user's delegate to mDelegate.
EDIT:
-(BOOL)respondsToSelector:(SEL)selector {
if (sel_isEqual(selector, #selector(locationManager:didUpdateLocations:)))
return true;
return [mDelegate respondsToSelector:selector];
}
- (id)forwardingTargetForSelector:(SEL)selector {
if (sel_isEqual(selector, #selector(locationManager:didUpdateLocations:)))
return self;
return mDelegate;
}
One of 35 header files in project (Handed over to me by some other developer; All of them contains same delegates declaration)
#interface ActivityDetailsCN : UIViewController <NSXMLParserDelegate,
AccountStatusDelegate, AccountTypeDelegate, DirectionDelegate, RecipientDelegate,
PriorityDelegate, DurationDelegate, CurrencyDelegate, OppTypeDelegate,
OppCategoryDelegate, DatePickerDelegate, SalutationDelegate, DepartmentDelegate,
LeadTypeDelegate, OwnershipDelegate, MailingDelegate, SourceDelegate,
StateDelegate, CommentsDelegate, CityDelegate, ZipCodeDelegate>
{
//Declaration of iVars goes here...
}
All the delegates declared here contains the same functions. Even their definitions, too.
Each of these delegates are declared before their respective ViewController header file Like this:
#protocol AccountStatusDelegate <NSObject>
- (void)cancelTapped;
- (void)doneTapped;
- (void)selectTapped:(NSString *)string;
#end
#interface AccountStatusVC : UIViewController <NSXMLParserDelegate> {
}
#property (unsafe_unretained) id <AccountStatusDelegate> delegate;
Implementation of cancelTapped:
- (void)cancelTapped {
[objPopOver dismissPopoverAnimated:YES];
}
Implementation of cancelTapped:
- (void)doneTapped {
[tblView reloadData];
[objPopOver dismissPopoverAnimated:YES];
}
Implementation of cancelTapped:
- (void)selectTapped:(NSString *)string
{
if ([string isEqualToString:#"US"])
isTextField = FALSE;
else if([string isEqualToString:#"Other"]) {
appDelegate.strCountry = #"";
isTextField = TRUE;
}
[tblView reloadData];
[objPopOver dismissPopoverAnimated:YES];
}
Now, Coming to the Question: I don't want to repeat it in each and every class (as it is now); I want to make it in cleaner way, Is there any possible solution available ?
Implement the delegate methods in a common superclass, and refactor all of the protocols to be one common TapCallbackDelegate protocol