iPhone - variables inside methods inside blocks - iphone

we know that we have to previously declare a variable using __block if we want a block to be able to change it. But what if we call the block like
dispatch_async(myQueue(), ^{
[self doStuff];
});
is the code inside doStuff subjected to this rule? Imagine doStuff is using variables or properties declared on .h. Do these variables/properties need to be declared using the __block prefix?

I dont think that makes much differnce. I think we can use the instance methods ,and global variables, as we use them in normal functions.
For eg:
dispatch_async(dispatch_get_main_queue(), ^{
liveImageView.image = image;
if (image) {
[self analyzeImage:image];
currentImage = image; //here currentImage is a global variable, declared in .h
}
mod++;
[image release];
});

You don't need to use the __block prefix on any instance variable to make that method call because the variables used in the method have nothing to do with the block. It is just a method call.
But be aware that the block will retain self.

Related

Making Xcode complain about a missing parameter

I am designing a new application by modernizing code I wrote in the past. This old code uses the class/delegate model and I am trying to transform them to use blocks as callbacks, not the delegate stuff.
What I do is to create a property like
#property (nonatomic, copy) void (^onTouch)(NSInteger index);
That would pass to the object using that class a block where code can be inserted and in this case executed on touch.
But my problem is this. When you use delegates and you have a method on the delegate protocol, Xcode will warn if you use that class and forget to implement the delegate protocols. Is that a way to do that with blocks? Or in other words: is there a way to make Xcode complain if a callback block is not defined by the caller?
I mean this would be the correct:
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = ^(NSInteger *index){ //call back code to be executed };
This would be OK too
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = nil;
but this would generate a message
MyClass *obj = [[MyClass alloc] init];
// no callback block defined.
Is this possible?
If you want to enforce setting a certain parameter, I would include it in the initializer.
MyClass *obj = [[MyClass alloc] initWithBlock:^(NSInteger *index) { /* code*/ }];
Then, in MyClass:
- (id)init {
// This will result in a runtime error if you use the wrong initializer.
NSAssert(NO, #"Use initWithBlock instead.");
}
- (id)initWithBlock(initWithBlock:^(NSInteger *)block) {
self = [super init];
if (self) {
self.onTouch = block;
}
return self;
}
Also note, attempting to execute a NULL block results in a crash, so make sure to do:
if (self.onTouch) { self.onTouch(); }
Wherever you run the block.
First, I strongly recommend defining types to represent your blocks - makes them a lot easier to work with, especially if you need to refactor the parameters.
You can't write code that distinguishes between "I set this property to nil" or "the runtime initialized this property to nil", at least not without some crazy runtime code to check the stack. Only option I can think of would be to use the null object pattern. Before I elaborate, bear in mind that I haven't actually tried to test this, but it should work. Define a block that means 'has no value' and set your property to point to that block on init. Then you can compare to that NullBlock at runtime to identify if someone explicitly set the property to nil (because it would be nil at that point) or gave it a real non-nil value.
Alternatively, if you don't mind manually writing your set accessors, you could have a BOOL that tracks if someone set the property explicitly. Then when you call the block just check if someone actually set the value or not.
#synthesize onTouchBlock=_onTouchBlock;
MyBlock _onTouchBlock;
BOOL _onTouchBlockWasSet;
- (void)setOnTouchBlock:(MyBlock)block {
_onTouchBlockWasSet = YES;
_onTouchBlock = block;
}
I would not recommend passing the value in the initializer because that makes it tied to the creation of that object type. If you wanted to change the block in code based on some condition, you'd be back to square one. Also, it prevents you from using storyboards which create that object.

custom setter for NSString

I have a NSString called fontType
and I am trying to have a custom setter for it:
- (void) setFontType:(NSString *) fType
{
if (self.fontType != fType){
[fontType release];
self.fontType = [fType retain];
//some more custom code
}
}
Is there any issue with this?
A few things that stand out for me:
do not use self. inside of custom accessors. access the variable directly
it's better use copy semantics for properties of a type that has a
mutable subtype
be careful with whatever is // some more custom code
My personal style preferences are like so:
-(void)setFontType:(NSString *)fontType_ {
if (fontType == fontType_) return; // get out quick, flatten the code
[fontType release];
fontType = [fontType_ copy];
// some more code
}
Cocoa with Love has a good article on this topic. It's worth a read.
When you do self.fontType = newFontType, you are doing [self setFontType:newFontType] (just with a different syntax), this means you are calling the method inside itself.
This is called recursion, but in this case, you don't have a base case in which the method will stop calling itself, so my guess is that this method will call itself until the app crashes. In short, recursion is not something you want here.
Just replace self.fontType = [fType retain] with fontType = [fType retain] (Assuming the var linked to the fontType property is called fontType as well).
PS. At the end of the question you asked
Is there any issue with this?
If you didn't try this, then you shouldn't even be asking that here on StackOverflow, and if you did try it, then you should have realized that this method didn't work, so that last line is pretty meaningless. Just saying.

Updating an NSUInteger property within a block. Warning: Block will be retained by

I have an NSUInteger defined as a property like so:
#property (nonatomic, assign) NSUInteger wordDisplayed;
I need to have this as a property because I need access to read/write to this variable from other methods and classes.
When trying to change the value of this property within a block, I get the following warning:
Capturing 'self' strongly in this block is likely to lead to a retain cycle
"Block will be retained by an object strongly retained by the captured object"
How can I update this variable property within a block?
The block I'm using is in a modified UIActionSheet that I'm using to make the UIActionSheet accept blocks.
https://github.com/zoul/Lambda-Alert
Here is an example of my code:
sectionHeadersAct = [[LambdaSheet alloc] initWithTitle:#"Book 2 Lesson 1"];
[sectionHeadersAct addButtonWithTitle:#"D. E. F. & G. Teach New Letters" block:^{
wordDisplayed = 15; //This is where I'm trying to change the value
}];
[sectionHeadersAct showInView:self.view];
This post deals with this certain issue: https://stackoverflow.com/a/8159567/656036
To summarize it: You should use a weak pointer to self. If you use ARC, you can solve this by writing this before your block:
__weak MyObject *weakSelf = self;
And using weakSelf instead of self in the block itself.
EDIT Try this:
__weak ViewController *weakSelf = self;
sectionHeadersAct = [[LambdaSheet alloc] initWithTitle:#"Book 2 Lesson 1"];
[sectionHeadersAct addButtonWithTitle:#"D. E. F. & G. Teach New Letters" block:^{
weakSelf.wordDisplayed = 15;
}];
[sectionHeadersAct showInView:self.view];
This is a common occurrence when using blocks. I assume you are accessing the property with the block by something like self.wordDispalyed.
Because blocks can be run asynchronously, they hold a strong reference to objects inside them. This is so that if they run at some time after you have released the object, at least the block can still send messages to it.
In your case, you are referencing a property within the block. So the block retains the the object, in this case self. But, your object (the self) is also retaining the block, since it has to run it. So you have a circular reference. The block retains the object, and the object retains the block. This might cause you problems - and the compiler is warning you of this.
To get around this you need to create a local variable that well be retained by the block, and use that to reference the property. It's a simple as writing:
__weak TheObject *blockSelf = self;
And then within your block you deal with the property as:
blockSelf.wordDisplayed …

How to pass string to handling main thread operation function

Hi i want to pass string to updateProgressBar function in my code to display some data. so how i can do it
[self performSelectorOnMainThread:#selector(updateProgressBar) withObject:nil waitUntilDone:NO];
and my function look like this
-(void)updateProgressBar
{
lbl.text = ?
}
There are different ways to pass argument on your updateProgressBar.
You can do like this
NSString * alpha;
//assign some value to alpha
[self performSelectorOnMainThread:#selector(updateProgressBar:) withObject:alpha waitUntilDone:NO];
-(void)updateProgressBar:(NSString *)beta
{
lbl.text = beta;
}
OR you can also pass multiple arguments using performSelectorOnMainThread.
you can search on google many tutorials are present about this.
You can use GCD to avoid writing a separate method:
dispatch_async(dispatch_get_main_queue(), ^{
lbl.text = newLabel;
});
To address the issue described in the comments on this answer (__NSAutoreleaseNoPool(): Object 0x55f3ec0 of class NSCFString autoreleased with no pool in place):
You are most likely executing this snippet in a background thread without an autorelease pool. You can either create one or avoid using autorelease.

Delegate callback from other NSThread

I have object with .delegate property which i manipulate in method 'doJob'. I assign this property with 'self' and my function is being called when this object finishes his job. Till now everything is fine.
Now i want to manipulate this object in a separate thread.
I'm using [NSThread detachNewThreadSelector...] to run the 'doJob' function.
In this case my delegate method not being called. I guess this is because 'self' points to new thread instead of main one. Ok. I'm passing self as argument to function while creating the thread and it still not working. What do i miss?
my current code is as follows:
- (void)mainFunction
{
[NSThread detachNewThreadSelector:#selector(doJob:) toTarget:self witObject:self];
}
- (void)doJob:(MyObject*)parentThread
{
ManipulatedObject *obj = [[ManipulatedObject alloc] init];
obj.delegate = parentThread;
[object startJob];
}
GCD will make most of your multi-threading troubles trivial. You can do something like this:
- (void)mainFunction
{
// Runs your task on a background thread with default priority.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ManipulatedObject * obj = [[ManipulatedObject alloc] init];
[obj startJob]; // I'm assuming this is sychronous.
// The callback is explicitly run on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
// Your callback here.
[obj release];
});
});
}
That's all you have to do, it's that simple. All the relevant code is inline and together.
If you want the ManipulatedObject to explicitly invoke the block, then you could add that ability to ManipulatedObject. To do so, you should:
Define the block type for convenience typedef void(^MyCallback)();
Add #property (nonatomic, copy) MyCallback block; and #synthesize block. Don't forget the copy.
Invoke the block when you need to dispatch_async(dispatch_get_main_queue(), [self block]);.
If your delegate needs to make more than one kind of callback, then you will need a block for each callback. It's a minor inconvenience, but it's worth it for all the conveniences you gain.
For a more thorough explanation of blocks and GCD, check out WWDC 2011 session 308.
Well firstly you do not need to pass self as the witObject: parameter, (which is spelt wrong) because - (void)doJob:(MyObject*)parentThread is still in the same object (self is the same in both threads), self has nothing to do with your main thread its MyObject presumable, you also have a problem were you are not creating a new autorelease pool for your doJob:, doJob: should look like
- (void)doJob:(MyObject*)parentThread
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
ManipulatedObject *obj = [[ManipulatedObject alloc] init];
obj.delegate = parentThread;
[object startJob];
[pool release];
}
you have to give us some information about how you're delegate method is being called, if it is tying to use timers or something like that then you are going to have problems because there is no runloop to add your timer to.