I am relatively new to programming and there is one thing which I can not manage to wrap my hand around. That is, what are blocks and why/when would you use them? What is the difference between a block and a method? To me, they seem like they kind of do the same thing.
Can some explain this to me?
Yes, I did spend hours on Google before finally coming here to ask.
Blocks are the anonymous function.
Blocks are used to executed at a later time but not the function can be used to execute later.
Blocks are commonly used for call back (No need to use delegates)
Blocks are objects but functions are not objects.
Suppose you want to perform an operation like the animation on view and wanted to be notified after completion. Then you had to write this code:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:context:)];
[UIView commitAnimations];
But you need to few lines of code if you are using a block like below:
[UIView animateWithDuration:2.0 animations:^{
// set up animation
} completion:^{
// this will be executed on completion
}];
Hope you are clear now about the use of the block.
The major feature of the blocks is that you can determine it in the method's place where you are. It can be very convenient for reading and understanding a logic.
The blocks are the alternative for callbacks.
The blocks can capture state from the lexical scope within which it is defined.
Related
Function NSBeginAlertSheet(...) has all the events I need to have especially the didDismiss: callback, but I really need to be able to do the same sheet action with any window I want, so I discovered this notification:
NSWindowDidOrderOffScreenAndFinishAnimatingNotification
Which is posted whenever a sheet is closed AND done with animations
now, my question is can I use that? Or is there a better way?
I use ARC and I load the windows from .xib using NSWindowController.
Overall what I need is to show a window as sheet and catch all events.
What's wrong with
- (void)beginSheet:(NSWindow *)sheet modalForWindow:(NSWindow *)docWindow modalDelegate:(id)modalDelegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo
This calls the optional didEndSelector which should look like this:
- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
This is all in the NSApplication documentation. There are two methods for ending the sheet:
- (void)endSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode
- (void)endSheet:(NSWindow *)sheet
So you could just do whatever you wanted to right before calling endSheet: or you could in the sheetDidEnd: method.
Edit:
Here is an example project showing that after calling [window orderOut:self] then animation is finished and you can do what you'd like.
NSWindowDidEndSheetNotification It is posted whenever a sheet is finished animating out.
Starting with 10.9, the correct way is to call beginSheet:completionHandler: on a NSWindow object.
This method has the advantage, that the completion handler is a block, so it can keep all the objects alive that are required as long as the sheet is still displayed and once the sheet is done and the block has been executed, the block itself is released and thus all objects it was keeping alive are as well.
To make sure a block keeps objects alive, use the objects within that block or if there is no way to use them in a meaningful fashion, put all of them into a NSMutableArray and within the block call removeAllObjects on that array; this requires the block to keep the array alive and the array keeps the rest alive -> memory management made easy.
Please see above screen shot. I want to know how can I do effect like that.
You can have more information here.
This is a native ViewController called UIPageViewController. There are plenty of tutorials on the net.
if you want only animation effect then not exact but similar effect you can get:
[UIView beginAnimations:#"animationCurl" context:nil];
[UIView setAnimationDuration: 0.5];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:NO];
[UIView commitAnimations];
Otherwise you must need to take -
Page-Based Application which contains UIPageViewController
As far i know
OK, so Apple brought ARC to us, which is great. After refactoring my Application to ARC almost everything works fine and it is a lot easier now to develop and maintain.
There is just one problem I still can't figure out.
My job management program shows different detail information of proposals, orders and so on in their own windows. So I have a special class where WindowControllers gets allocated and initiated with initWithWindowNibName and then the window is shown with showWindow:
DetailWindowController *proposalWindowController = [[DetailWindowController alloc] initWithWindowNibName:#"ThePorposalWindow"];
[proposalWindowController showWindow:nil];
Before ARC the Instance of the WindowController did the release like shown in the documentation:
- (void)windowWillClose:(NSNotification *)notification
{
[self autorelease];
}
But now with ARC this is not possible anymore and what makes it even worse, in my special class where the WindowController is allocated and initiated, the same windowController is released by ARC because there is no pointer to the windowController.
My idea was to copy the windowController into an mutuable array:
[proposalWindowArray addObject:proposalWindowController];
[[proposalWindowArray lastObject] showWindow:nil];
And in the windowControllers delegate method windowWillClose I post a notification to my special class:
- (void)windowWillClose:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"ProposalWindowWillClose" object:[[self window] windowController] userInfo:nil];
}
In my special class I listen to the notification and remove the object from the array:
- (void) proposalWindowWasClosed: (NSNotification *) notification
{
[proposalWindowArray removeObjectIdenticalTo:[notification object]];
}
It works, but I still do not believe that this is the correct way.
Does anybody has the same problem or a tip to make it better?
I'd probably use a delegate approach rather than notifications. Generally it is better to have an external object that keeps track of the open windows. Self-retaining objects, like your old system, break the basic points of object ownership and make it hard to find things (such as "give me a list of open windows"). Non-Singletons that are just "floating" out there often come back to bite you in your architecture (I've had to fix this often enough).
That said, sometimes self-ownership is at least convenient, and at worst not-the-end-of-the-world. So self-own. The only difference is that you need to do it explicitly rather than matching a leak and an over-release (which is what your old code was doing).
Create a private strong property. Assign self to it. That will create a retain loop that will keep you around until you set the property to nil.
I think your alternative approach should be correct, but I don't think you need the second notification. You should be able to do:
- (void)windowWillClose:(NSNotification *)notification
{
[proposalWindowArray removeObjectIdenticalTo:self];
}
Assuming the "proposalWindowArray" is a static NSMutableArray.
Without hacks, there is no elegant way to keep an object retained other than having a strong reference to it in some other object. For example, you could keep a static NSMutableArray/NSMutableSet, add your controller there, and remove it in windowsWillClose:. This will be shorter than posting a notification. To make this reusable, create a WindowControllerRegistry singleton with an array, where you add controllers like this one, and which will automatically listen to NSWindowWillCloseNotification and remove them from its array thus releasing ownership.
As a quick workaround, you could perform retain/autorelease calls from non-ARC file:
my_retain(self);
my_autorelease(self);
// ArcDisabled.mm
void my_retain(id obj) { [obj retain]; }
void my_autorelease(id obj) { [obj autorelease]; }
I had this same issue when I switched to ARC. Your solution works, but you're making it too complicated. You can essentially do what you were doing before by having the window release itself when it closes, but in an ARC compatible manner.
The solution is to simply create a property of your class within the class itself. For your example, in DetailWindowController, you would add the following property:
#property (strong) DetailWindowController *theWindowController;
Then when you create the window with your code above, add one line like so:
DetailWindowController *proposalWindowController = [[DetailWindowController alloc] initWithWindowNibName:#"ThePorposalWindow"];
[preferenceController setTheWindowController:proposalWindowController];
[proposalWindowController showWindow:nil];
Then finally, to have ARC release the window when it is closed like you did with the autorelease pre-ARC, in the DetailWindowController class, simply do:
- (void)windowWillClose:(NSNotification *)notification
{
// Let ARC tear this down and clean it up
[self setTheWindowController:nil];
}
i am setting up a simple UISlider to animate it's progress:
[UIView animateWithDuration:songLength
delay:0.0
options:UIViewAnimationOptionRepeat|
UIViewAnimationOptionAllowUserInteraction|
UIViewAnimationOptionBeginFromCurrentState
animations:^{
[UIView setAnimationDuration:10.0];
[myUISlider setValue:10.0];
} completion:nil
];
[UIView commitAnimations];
when the user presses a button i want to stop the animation at it's place.
i understand i need to query the presentation layer to figure out the value, however, the presentation layer is of type CALayer and not UISlider. hence, it has layer properties, like it's x/y position on the screen, but not the value of the slider itself.
it makes sense that by design the presentation layer can access all the current animated data of a layer, but i'm not sure how to work that out in code.
any ideas?
i figured out one way of resolving this matter, in case you are dealing with the same issue.
by design there is no apparent way to query an animated thumb of a UISlider. animation querying works well for a layer, so one way is to create your own background and animated a layer that is the thumb.
the way i worked around this is the use of NSTimer class. once i want the progress bar to start animating i set an NSTimer for 0.1s interval and call a function to update the thumb location. the animation before/after the thumb (as it progresses the left part is blue and what's remained is still white) is taken care of for you automatically.
here is the code:
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateCurrentTime) userInfo:p repeats:YES];
so every .01 of a second this method is called and it redraws the thumb, thus creating the animation. no need for an animation block.
this works well and i am happy with the results. i am concerned with performance and will measure how resource intensive this implementation is. it maybe a good idea to go with the first option i've mentioned above.
I have a document-based application and I have sub-classed NSDocument and provided the required methods, but my document needs some extensive clean-up (needs to run external tasks etc). Where is the best place to put this? I have tried a few different methods such as:
close
close:
canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo
dealloc
If I put it in dealloc, sometimes it gets called and other times it does not (pressing Command+Q seems to bypass my document's deallocation), but it is mandatory that this code gets called without failure (unless program unexpectedly terminates).
Have each document add itself as an observer in the local notification center for NSApplicationWillTerminateNotification. In its notification method, call its clean-up method (which you should also call from dealloc or close).
The correct answer here didn't fit my use case, but the question does. Hence the extra answer.
My use case: closing a document (which may be one of several that are open) but not closing the application.
In this case (at time of writing and unless I'm just looking in the wrong place) the documentation is not as helpful as it could be.
I added a canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: override in my NSDocument subclass and called super within it. The documentation doesn't say whether you must call super, but a bit of logging shows that the system is providing a selector and a context. This method is called just before the document is closed.
- (void) canCloseDocumentWithDelegate:(id)delegate shouldCloseSelector:(SEL)shouldCloseSelector contextInfo:(void *)contextInfo;
{
if ([self pdfController])
{
[[[self pdfController] window] close];
[self setPdfController: nil];
}
[super canCloseDocumentWithDelegate:delegate shouldCloseSelector: shouldCloseSelector contextInfo: contextInfo];
}
There is some useful discussion of this method on CocoaBuilder. If there's downsides to this approach or better ways of doing this, please comment.