I was looking at this question.
One of the answers shows how to use blocks to pass data backwards view the prepareForSegue method. My understanding is this method should really be used to pass data forward, not backwards.
I'm wanting to try blocks out for this purpose - passing data back to another viewController.
My question is: How do I do this, without using prepareForSegue method?
I could use for instance in a UITableView - didselectRowAtIndexPath and dismiss the view - but then how does the receiving view get "notified" there is data to come back, without using a delegate?
sent data backward
declare block in your secondViewController.h file
#property (nonatomic, copy)void(^myBlock)(NSString *);
call block wherever you need to pass data from .m file of secondViewController
myBlock(#"this will displayed in firstViewController");
3.import above .h file in your firstViewController .m file
and define your block as
secondViewController *ref =[[secondViewController alloc ]init];
ref.myBlock =^void(NSString *data)
{
self.labelOffirstviewcontroller=data;
};
In your view controller 1:
MyViewControllerClass2* vc2 = [[MyViewControllerClass2 alloc] initWithNibName:#"UIViewController" bundle:[NSBundle mainBundle] completion:^{
NSLog(#"view2 is closed");
}]];
[self.navigationController pushViewController:vc2 animated:YES];
In MyViewControllerClass2.m:
#interface MarryViewController ()
#property (nonatomic, copy) void(^completion)();
#end
#implementation MarryViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
}
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil completion:(void(^)())completion
{
self = [self initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if( self )
{
//store completion block
_completion = completion;
}
return self;
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
//call completion block
_completion();
}
In MyViewControllerClass2.h:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil completion:(void(^)())completion;
Just a few notes on how awesome block are:
MyViewControllerClass2 has no idea what is defined in _completion() which is the main point, as this is not of his concern
You could also call _completion() in -dealloc or even on some place where MyViewControllerClass2 will continue to run
You can pass arguments to block functions
You can pass arguments from block functions
Many more :)
I'm really encouraging people to get good overview on blocks and stat using them as they are pretty cool.
IMPORTANT!
While using block you do not declare delegate, the main idea and abstract result of delegate method and using block are the same. Moreover delegate pattern has it's advantages such as better documenting and more strictly usage. Still blocks are way more flexible and(when get used to) easier to use.
Regards,
hris.to
I know that you asked specifically for a solution that did not involve prepareForSegue but this appears to be based on the assumption that prepareForSegue is for passing data forward only.
There is something called an unwind segue which might be helpful in your situation. There is a detailed discussion on SO here.
If you specifically want to use blocks for this, you can simply add a block property to your child controller and have the parent controller set the block. The child controller would have to invoke the block when it is being dismissed. Beware of retain loops if you do this. It doesn't sound to me like blocks are the best solution in this context but it's difficult to say something like that with authority without having a lot more context.
Yes methods can send data forward as well as backwards for that you can use Blocks or Delegates
For more info about blocks in ios use this link
CLICK HERE
Hope it will help you, thnx
Related
Today, I upgrade my Xcode to the latest version(Version 5.1 (5B130a)) to support iOS 7.1.
After doing this, I run my project in Xcode as usually. Then app crashes.
I didn't change any code before upgrading the SDK version.
The code is running perfectly in iOS 5.x, 6.x, 7.0.x.
I am simply presenting another view controller in the current view controller.
they are both initialized by storyboard.
While processing presentViewController method, it gets a error message "Thread 1: EXC_BAD_ACCESS (code=2, address=0x60)". I have checked the variables, they are both alive, not a released garbage.
What's the problem with iOS 7.1??
the project is using non-ARC mechanism.
Here is my code:
#property (nonatomic, retain) ArticleViewController *articleView;
....
self.articleView = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"ArticleViewController"];
...
[self presentViewController:self.articleView animated:NO completion:^() {
log(#"has shown article page...");
}];
but it works fine if presenting another view by using addSubView function:
[self.view addSubView:self.articleView.view];
I really don't know why this happens.
This happened to my app while presenting a view controller with modalPresentationStyle = UIModalPresentationCustom;
Here's how my code looks like on iOS 7.0:
//Inside my MyPresentedViewController:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[self setupContent];
}
return self;
}
- (void)setupContent
{
//TransitionManager adopts UIViewControllerTransitioningDelegate and UIViewControllerAnimatedTransitioning
TransitionManager *transitionManager = [[TransitionManager alloc] init];
transitionManager.presenting = YES;
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = transitionManager;
}
However, the above code crashes on iOS 7.1 so I changed the implementation to:
#interface MyPresentedViewController
#property (nonatomic, strong) TransitionManager *transitionManager;
#end
...
//Inside my MyPresentedViewController:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[self setupContent];
}
return self;
}
- (void)setupContent
{
//TransitionManager adopts UIViewControllerTransitioningDelegate and UIViewControllerAnimatedTransitioning
self.transitionManager = [[TransitionManager alloc] init];
_transitionManager.presenting = YES;
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = _transitionManager;
}
Basically, instead of declaring a transitionManager object inside the setupContent method, I created a private property (strong reference) for it.
Hm the only thing I can think of is ensuring that your articleView property is a strong reference.
#property (nonatomic, strong) ArticleViewController *articleView;
I ran into a similar issue with a picker view. In my case the pickerview was set to nil after the selection handler block, although this caused no issues as recently as last night, it was causing the app to crash this morning. Removing that line fixed the issue, however I have converted this project to ARC in the last month so you may have to find a better way to handle clean up.
This may be completely unrelated to the stated problem or answers but I ran into a similar problem when accessing an NSString on an iPhone 5S. The exact same code runs fine on an iPad and iPad at the same time.
To start out with I think it's important to state that I'm running under ARC, and I have been told repeated not to "release" any objects I instantiate in my functions. I've had problems with this before so I use some of my C# background to set almost all of my local variables to nil (null in C#). Yes, I don't trust MS GC either.
Back to the problem at hand; I have an NSString called 'data'. 'data' is read as a result from another method in a different class. Using NSLog I can see the contents of 'data'. On the next line I convert 'data' to an array to use it in scanf. That still works. On the third line I try to NSLog 'data' again, but then I get the EXC_BAD_ACCESS error. Each time it has a different address. I'm even less comfortable with ARC than I am with the Microsoft Garbage Collector so I wrapped the function in a try..catch..finally. 'data' is now sitting outside of the try..catch..finally. I'm setting 'data' to nil in the finally, which seems to fix the problem.
I know this was a bit long winded, but I would really appreciate it someone could explain why this would happen. I'm expecting to see a lot of these problems to popup all over my code now.
I'm still fairly new to Objective C so please bear with me:
My app has a Delegate, a Navigation Controller, and a View.
I also use a Singleton for "global" variables.
I know I can do this but it seems ungainly:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
-(void)myMethod{
GlobalData *globDat=[GlobalData getSingleton];
junk=globDat.someValue;
}
-(void)myOtherMethod{
GlobalData *globDat=[GlobalData getSingleton];
moreJunk=globDat.someOtherValue;
}
I'd like to this but it can't:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
GlobalData *globDat=[GlobalData getSingleton]; //Compiler won't allow this line
-(void)myMethod{
junk=globDat.someValue;
}
-(void)myOtherMethod{
moreJunk=globDat.someOtherValue;
}
However I can do this:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
GlobalData *globDat;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
globDat=[GlobalData getSingleton];
}
return self;
}
-(void)myMethod{
junk=globDat.someValue;
}
-(void)myOtherMethod{
moreJunk=globDat.someOtherValue;
}
So, is there a standard/common/proper "init" method to use for all Classes?
Should I just do this in every Class?
-(id)init{
if(self=[super init]){
globDat=[GlobalData getSingleton];
}
return self;
}
I know I can do this but it seems ungainly...
You seem to be asking how to do away with the part where you retrieve the value you want from your singleton. The best way to do that is to eliminate the singleton in the first place.
You say that you have an app delegate, a navigation controller, and a view. You probably also have a view controller. If those are the main objects in your application, you might consider storing your data in your view controller. Or, convert your singleton into a legitimate data model, and let your view controller keep a reference to it in a property. Then you can say something like:
-(void)myMethod{
junk = self.model.someValue;
}
which is pretty close to what you seem to be asking for.
It depends on how much you will use GlobalData.
extensive use in many methods
If you plan to make an extensive use of GlobalData in your class (in many methods), then, you should better add
#propertu (nonatomic, readwrite, weak)GlobalData *globalData in your .h
(or, better, in the class extension). In your main init... method, you set the value self.globalData = [GlobalData getSingleton];. You can also do as you did, with an instance variable, GlobalData* myGlobalData in your .h or in the class extension.
Make sure your init... method starts with init and make sure there is a "main" init... method (we call it the "designated initializer"). You can simply override the init method of NSObject. But, if you need, you can define
- (id)initWithString:(NSString*)str
{
self = [super init] ; // or another one ; here you should use the designated init. of the parent class
if (self){
self.string = str ;
self.globalData = [GlobalData getSingleton] ;
}
return self;
}
extensive use in few methods
If you intensively use GlobalData in one or two methods, you don't need to do so. You will just define a local variable GlobalData * globalData = [GlobalData getSingleton]; and use it.
The third code you propose is not objected-oriented.
I'm not 100% sure what you're question is really about (singletons as a common pattern vs common -init method?), but it is common to create what is called a designated initializer for your class. If you're not using a singleton pattern an init method signature that takes a value might look like this:
-(id)initWithValue:(NSString*)newValue;
As for using a singleton pattern to provide access to your variables I would recommend a more loosely coupled approach, especially if the number of values your singleton class manages begins to grow. I would put those variables into their own classes (separated logically) and use them as needed, by passing them to constructors, or instantiating them. That's an opinion/philosophy issue, though.
I ended up using this to resolve the question.
(Again I was primarily trying to clean out the redundancy of code)
In my Class modules I did the following:
#import "GlobalData.h"
GloablData *globDat;
-(id)init{
if(self=[super init]){
globDat=[GlobalData getSingleton];
}
return self;
}
...
-(void)someMethod{
int junk=globDat.junkvalue;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
fetched = NO;
self.title = #"Nearby";
// Custom initialization
liked = NO;
categories = [[NSArray alloc] init];
fundings = [[NSArray alloc] init];
stages = [[NSArray alloc] init];
}
return self;
}
I use the code above. am I during anything wrong by initializing all of my varialbes in the initWithNibName function? Is that the correct place to do so?
You will probably not run into issues, but you can use a ViewController also with the pure init in which case the NIB is not loaded and also your initialization will not be called. Another place to make the init for that is the viewDidLoad which is called only after the init.
You can do it in the init method, that's fine. You could also do it (or some of) in the viewDidLoad method. The difference would (generally) be that viewDidLoad is called when your view is actually getting ready to be shown, so you need everything setup. init on the other hand could be called well in advance. There isn't a hard and fast answer, it depends what the items you're setting up are and if they can be unloaded when the view isn't on display. With newer versions of iOS that concern changes anyway as the view generally isn't unloaded. You really need to read about the view and controller life cycles and decide when to create and destroy your attributes.
Im using a Base view controller for some other view controllers,
Im making it the base as I have 4 to 6 of other view controllers that will be showing the same label and image...
so I tried to make this base page and subClass the other ViewControllers,
my doubt is that if I leave the dealloc for the strings that contain the value for the label and other string, when dealloc is called for that page, then I get an exception
pointer being freed was not allocated
but I dont want just to comment out the deallocs for the labels,
so What im i doing wrong, missing?
here the BasePage
#import "BasePage.h"
#implementation BasePage
#synthesize titleString = _titleString;
#synthesize justifyTitleString = _justifyTitleString;
- (void) dealloc {
[_titleString dealloc];
[_justifyTitleString dealloc];
[super dealloc];
}
- (id)initWithParams:(NSString *)title :(NSString *)justifyTitle
{
self = [super init];
if (self) {
self.titleString = title;
self.justifyTitleString = justifyTitle;
}
return self;
}
my app uses a navigation controller,
so when I call a page i use:
CentresVC *centresVC = [[[CentresVC alloc]initWithParams:[NSString stringWithFormat:#"Centre %d", indexPath.row]:#"center"]autorelease];
[self.navigationController pushViewController:centresVC animated:YES];
and pop the views when navigating back,
So I have the doubt of when using
[_titleString dealloc];
[_justifyTitleString dealloc];
where the pointer for those labels get saved?, and if i just commented out wich doesnt seem nice, would i get then a memory leak that would crash?
how to fix this?
thanks!
As Richard said, you shouldn't be calling other objects' dealloc method. Ever.
If titleString and justifyTitleString are retained/strong properties, then this version below would work. Assuming that no other objects had retained titleString and justifyTitleString, their retain counts would go to 0, and their dealloc methods would be called automatically.
- (void) dealloc {
[_titleString release];
[_justifyTitleString release];
[super dealloc];
}
This next option would work too, since the synthesized setter for a strong/retained property sends release to the old value of the property before assigning the new one. Also, this would be preferred if you had overridden your setter to do additional cleanup of the old value.
- (void) dealloc {
self.titleString = nil;
self.justifyTitleString = nil;
[super dealloc];
}
Finally, if you were using ARC, and you didn't need additional cleanup like in the second case above, you wouldn't need to override dealloc at all. But if you did need to override dealloc, you would omit [super dealloc] since that would be provided automatically by the compiler.
i'm a little bit confused with memory management in view controllers.
Lets say i have header file like this:
#interface MyController : UIViewController {
NSMutableArray *data;
}
#property (nonatomic, retain) NSMutableArray *data;
#end
and .m file looks like that:
#implementation MyController
#synthesize data;
- (void)dealloc
{
[self.data release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[self.data release];
self.data = nil;
}
Is that ok from the correct memory management point of view? Will that work after dealloc via Memory Warning? How You do that in your apps?
Thanks for your answers ;)
While the alloc-retain calls balance out in viewDidLoad and viewDidUnload and should prove no problem memory-wise, it would be cleaner to take ownership only once and relinquishing it once rather than twice.
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [NSMutableArray array];
}
and
- (void)viewDidUnload
{
[super viewDidUnload];
self.data = nil;
}
You are not guaranteed that viewDidUnload will ever get called. Unlike init/dealloc, which get called in pairs, viewDidUnload is undeterministically called. viewDidUnload is only called if there is a low memory situation and your view is not the active view.
Depending on how your model is created and the implications of it remaining in memory, it may make more sense for you not to get rid of it. An example of this may be that recreating that data may involve an expensive web service call. It therefore would be a bad user experience to have to wait for that data to get recreated. If it must absolutely go, a better strategy may be to cache the data to disk so that you can easily reconstruct it.
viewDidUnload should only contain cleaning up your IBOutlets and flushing easily recreatable data.
These lines from -viewDidUnload both release data:
[self.data release];
self.data = nil;
Since you're using the property setter in the second line, and data is a retained property, the setter will release data. This is an over-release, and it'll cause a crash either right away or later, depending on whether other objects also retain that object. To fix, simply delete the first line and rely on the setter to do the right thing.
The -dealloc method, on the other hand, shouldn't use the setter as it does now. You should change:
[self.data release];
to:
[data release];
data = nil; // this line isn't strictly necessary, but often considered good form
The reasoning here is that it's conceivable that this class could be subclassed, and someone might override the property setter in such a way that it has some side effects that could cause problems when the object is being deallocated. You should access the ivar directly -- notice that I left off the "self." so that we're dealing with the ivar and not the property accessor. (-init and -dealloc are the only places where you have to worry about that; use the property accessors everywhere else.)