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.
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 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
I have a singleton class, and I have a property declared in it:
#property (nonatomic, strong) NSString *currentTableName;
+ (SuperNoteManager*)sharedInstance;
.m file:
+ (SuperNoteManager*)sharedInstance
{
static SuperNoteManager *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[SuperNoteManager alloc] init];
});
return _sharedInstance;
}
When I run my app for the first time, there is no data in the data base,so it shows the EmptyViewController.
#property (nonatomic, strong) SuperNoteManager *myManager;
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
_myManager=[SuperNoteManager sharedInstance];
}
-(void)changeRootView{
UIStoryboard *storyboard=[UIStoryboard storyboardWithName:#"Main" bundle:nil];
HomeViewController *hVC=[storyboard instantiateViewControllerWithIdentifier:#"HomeViewController"];
UINavigationController *mNavVC=[storyboard instantiateViewControllerWithIdentifier:#"MainNavigationController"];
mNavVC.viewControllers=#[hVC];
[[UIApplication sharedApplication].keyWindow setRootViewController:mNavVC];
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if ( [_myManager checkForDataInAllTables]) {
NSLog(#"All tables are empty");
}else{
//a note is saved, show home view controller
if (![_myManager isDatabaseEmpty]) {
[self changeRootView];
}
}
}
There is + button on NavigationBar on EmptyNotesViewController, and on tap '+',
NotesViewController is pushed from EmptyNotesViewController.
In the NotesViewController, after I write some notes, I save the notes in database:
NotesViewController:
#property (nonatomic,strong) SuperNoteManager *myManager;
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
_myManager.currentTableName=#"WorkTable";
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
if (self.isMovingFromParentViewController) {
NSLog(#"going back");
[self insertTextintoDatabase]; //Text is inserted . I double checked
}
}
And then When I go back to my EmpytNotesViewController, I check for data, and if data is present, I change the rootViewController as it is not EmptyNotesView anymore.
So When I go back to my EmptyNotesViewController:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if ( [_myManager checkForDataInAllTables]) {
NSLog(#"All tables are empty");
}else{
//a note is saved, show home view controller
//Put a breakpoint here
if (![_myManager isDatabaseEmpty]) {
[self changeRootView];
}
}
}
Here at the breakpoint _myManager.currentTableName is nil. why?
I set it in the NotesController, and it became nil when it come back to the EmptyNotesController.
I thought once a value is set in singleton, it will persist as long as the app is closed/killed.
Note: I have declared the property of my Singleton class as strong and also all the properties in the singleton are declared as strong.
It appears like you never get a reference to the SuperNoteManager singleton in NotesViewController, like you did in your EmptyNotesController.
Therefore the currentTableName property never gets set in the first place.
You want to insert:
_myManager = [SuperNoteManager sharedInstance];
in your -viewDidAppear: before you set the currentTableName property.
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 have a method that draws a one sprite on the screen with the animation effects,
if I call in init this method
if( (self=[super init]) ) {
// ....
[self myMethod];
// .....
}
Then he does it once on my project
When I call by schedule
-(void)schedulMyMethod:(ccTime)dt {
[self myMethod];
}
if( (self=[super init]) ) {
// ....
[self schedule:#selector(schedulMyMethod:) interval:0.5];
// .....
}
It runs for an unlimited times
I need so that I can call the my method some amount
You mean, you want to have it repeat N times? You'll need to keep state on times remaining for it to run.
#property (nonatomic, assign) NSInteger timesToRunMyMethod;
- (void)beginRunningMyMethod {
self.timesToRunMyMethod = 100; // N==100
[self myMethod];
}
- (void)myMethod {
self.timesToRunMyMethod--;
// do stuff
if (self.timesToRunMyMethod > 0) {
// i used native delayed execution, you can replace it with whatever cocos2d offers if you want
[self performSelector:#selector(myMethod) withObject:nil afterDelay:0.5];
}
}
And it's probably wrong to start this on init. Is it a view controller? Then you can use viewDidAppear or willAppear.