I would like to modify various variables which exist outside an Objective-C block within it's body.
I know I can directly access and modify a variable using the __block attribute while declaring the variable. So this works:
__block NSMutableString *alertMessage;
void(^appendMessage)(NSMutableString*, NSString*)= ^(NSString *append){
if (!alertMessage)
{
alertMessage = [NSMutableString new];
}
if ([append length] > 0)
{
[alertMessage appendString:#"\n"];
}
[alertMessage appendString:append];
};
appendMessage(#"Alert part 1"); //This works fine
However I want to create a block which can perform an operation on a passed variable, enabling me to use the operation on multiple variables outside the block without directly accessing the same. Something like the following:
__block NSMutableString *alertMessage;
__block NSMutableString *otherString;
void(^appendMessage)(NSMutableString*, NSString*)= ^(NSMutableString *string, NSString *append){
if (!string)
{
string = [NSMutableString new];
}
if ([append length] > 0)
{
[string appendString:#"\n"];
}
[string appendString:append];
};
//The following do not work as intended
appendMessage(alertMessage, #"Alert Part 1");
appendMessage(otherString, #"Bleh bleh");
I want to be able to use the above block to modify the variables declared before it.
How can I achieve such an operation? Is this even possible?
Your question shows some confusion over values and variables, maybe the following will help.
Modify parameters in Objective-C blocks
In (Objective-)C all parameters to methods/functions/blocks are passed by value, e.g. when in the call f(x) the value of the variable x is passed to f, not the variable itself. This is known as call-by-value.
There are languages which do allow variables to be passed, known as call-by-reference. When used the argument must be a variable and the parameter name within the function is effectively an alias to the supplied variable. This is not supported directly in (Objective-)C.
However you can emulate it in (Objective-)C. It is not commonly used, with one notable exception: many methods use it to return an NSError * value.
You later comment:
What I want to achieve includes object creation, which is essentially what the question now boils down to. "Can I create an object declared outside within a block?". The answer which I have gathered with the help of all the activity here is NO.
You can, it is just a question of whether you should (i.e. is the design right?) and the best way to do it.
The straightforward way to solve your particular issue is to write a function:
NSMutableString *alertMessage;
NSMutableString *otherString;
NSMutableString *(^appendMessage)(NSMutableString *, NSString *) =
^(NSMutableString *string, NSString *append)
{
if (!string)
string = [NSMutableString new];
if (append.length > 0)
{
[string appendString:#"\n"];
[string appendString:append];
}
return string;
};
alertMessage = appendMessage(alertMessage, #"Alert Part 1");
otherString = appendMessage(otherString, #"Bleh bleh");
If you really (really, really) want to you can instead "pass the variable" by passing its address (using the & operator) and indirection (using the * operator) inside the block to get/set the value:
void (^appendMessage)(NSMutableString **, NSString *) =
^(NSMutableString **stringPtr, NSString *append)
{
if (!stringPtr) return; // no "variable" passed
NSMutableString *string = *stringPtr; // use indirection to get the value in the passed variable
if (!string)
string = [NSMutableString new];
if (append.length > 0)
{
[string appendString:#"\n"];
[string appendString:append];
}
*stringPtr = string; // use indirection to set the passed variable
};
appendMessage(&alertMessage, #"Alert Part 1"); // pass "variable" by passing its address
appendMessage(&otherString, #"Bleh bleh");
While the above is valid code it is generally not recommended coding practice in Objective-C for simple cases such as yours.
Once you take the address of a variable you need to be concerned over the lifetime of that variable - if you attempt to use the address to access the variable after the variable has been destroyed your program will fail (the dangling pointer problem)
What about __block?
Neither of the above examples use __block anywhere.
When a block references a variable by default it captures the variables value at the time the block is created. The __block attribute changes this to capturing the variable (so its value can be changed by the block) and alters the lifetime of the capture variable if required (so the variable lives at least as long as the capturing block, avoiding the dangling pointer problem).
The __block attribute is not applicable in your situation as you wish to capture different variables based on the call.
HTH
The code, as written, seems to confuse operation on object with object creation.
For clarity's sake, you should either pass in a mutable object to be manipulated or you should define a single __block variable whose value will be set by the block (and you do the logic after to figure out where that value should be stuffed).
Passing in something by reference is inherently dangerous to the point of being an anti-pattern (what happens as soon as you try to refactor the code to be asynchronous? At least in the __block case, the code after the block will see nil).
i.e.:
__block NSMutableString *foo = [sourceString mutableCopy];
doIt(#"new stuff"); // appends to `foo`
whereItShouldReallyGo = foo;
Related
new to objective c here... I am trying to initialize 2 strings and set them later, but I got this error for r1 and r2:
the variable is not assignable (missing _block type specifier)
my code looks like:
NSMutableString *r1 = [NSMutableString stringWithFormat:#"None"];
NSMutableString *r2 = [NSMutableString stringWithFormat:#"None"];
NSURLRequest *request = [client URLRequestWithMethod:#"GET" URL:statusesShowEndpoint parameters:params error:&clientError];
if (request) {
[client sendTwitterRequest:request completion:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (data) {
// handle the response data e.g.
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
NSLog(#"ok!");
for(NSString *key in [json allKeys]) {
NSLog(#"%#",key);
}
r1 = json[#"statuses"][0][#"text"];
r2 = json[#"statuses"][1][#"text"];
I am confused about if this is the correct way to set strings....
like all i want to do is to initialize a string and set it later.
Your assignments to your two string variables are inside a block you pass to sendTwitterRequest:completion:. By default a block captures the value of any local variables it uses declared outside the block, in your case r1 and r2, it does not capture the variables themselves, which means it cannot alter the values stored in those variables. Note that if the value captured is a reference to a mutable object, in your case the values are references to mutable strings, then you can mutate the object - doing that does not alter the reference itself.
You can fix your code in two ways:
1) I think the values you are trying to assign are references to immutable strings, NSString *, so I suspect you have only declared the two variables as NSMutableString * in an attempt to fix your problem.
To allow assignment to captured local variables you must annotate the local variable declaration with __block, this changes the behaviour capture so that the variable, rather than its value, is captured. To go this route you just need to change your declarations to:
__block NSString *r1;
__block NSString *r2;
There is no need to give the variables an initial value, they will automatically have the value nil. Your block can now assign directly to r1 and r2, however see the BIG BUT below.
2) If you do require mutable strings, NSMutableString *, then your two declarations are fine and the issue is that you use assignment to the variables (r1 & r2) instead of mutating the strings they reference. You can change the value of a mutable string using setString: (Apple documentation. To do this replace your two assignments with:
[r1 setString:json[#"statuses"][0][#"text"]];
[r2 setString:json[#"statuses"][1][#"text"]];
These two will mutate your strings referenced by variables and those changes will be visible outside your block via the references stored in r1 and r2. However as with the first method above see the following BIG BUT...
BIG BUT
The code fragment you supply suggests you might also be making a common error: assuming values assigned within a block used as an asynchronous handler will be visible after the call taking the block, in your case sendTwitterRequest:completion:. This will normally not be true, the call to sendTwitterRequest:completion: returns before the completion block has run - which is the whole point of asynchronous methods such as sendTwitterRequest:completion:; they schedule the work (accessingTwitter in your case) to run concurrently in the background and then return, later when the asynchronous work is completed the completion block is called.
If you have made this error you need to read up on asynchronous design, you can use Apple's documentation if you wish, you can also search on SO - there are plenty of Q & A on the topic. After that is done you can redesign your code, if you have problems at that point ask a new question.
HTH
I'm pretty new to using blocks. I'm wondering if there is a way to add code dynamically to a block? A mutable block if you will.
This is not quite what it sounds like you want, but it achieves a similar result if not quite the same one: Having a __block NSMutableArray of blocks to be called sequentially from within an outer block.
A silly demo:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
__block NSMutableArray *subblocks = [NSMutableArray array];
void (^blockWithBlocks)(void) = ^{
NSLog(#"%s: in blockWithBlocks()", __PRETTY_FUNCTION__);
for (void (^subblock)(void) in subblocks) {
subblock();
}
};
for (int i = 0; i < 3; i++) {
void (^subblock)(void) = ^{
NSLog(#"%s: in subblock %d", __PRETTY_FUNCTION__, i);
};
[subblocks addObject:subblock];
}
blockWithBlocks();
}
return 0;
}
Note that the requirements for copying blocks under ARC have been in flux. Previously it would have been necessary to write [subblocks addObject:[subblock copy]]; rather than simply [subblocks addObject:subblock]; Under the current semantics described in the clang documentation
With the exception of retains done as part of initializing a __strong parameter variable or reading a __weak variable, whenever these semantics call for retaining a value of block-pointer type, it has the effect of a Block_copy. The optimizer may remove such copies when it sees that the result is used only as an argument to a call.
the only times that it is necessary to copy a block to be sure that it is no longer on the stack is when the block is being passed as an argument to a function/method that has a __strong parameter variable and when the block is being read from a __weak variable.
What do you mean "add code dynamically to a block"? How is that different from simply making a new block from the "code" and the original block?
If the difference is that you want to have a reference to a block and have its behavior change without having to assign a new block to that reference, then you can have the block capture mutable state, where the mutable state could contain the block(s) to call, which you can then change, like what #NateChandler suggests.
If the difference is that is that you can choose between several different pieces of "code", so you cannot hard-code it at the place you are creating the block, then you can just make the "pieces of code" into blocks and select the right block to put into the new block.
What exactly does the __block keyword in Objective-C mean? I know it allows you to modify variables within blocks, but I'd like to know...
What exactly does it tell the compiler?
Does it do anything else?
If that's all it does then why is it needed in the first place?
Is it in the docs anywhere? (I can't find it).
It tells the compiler that any variable marked by it must be treated in a special way when it is used inside a block. Normally, variables and their contents that are also used in blocks are copied, thus any modification done to these variables don't show outside the block. When they are marked with __block, the modifications done inside the block are also visible outside of it.
For an example and more info, see The __block Storage Type in Apple's Blocks Programming Topics.
The important example is this one:
extern NSInteger CounterGlobal;
static NSInteger CounterStatic;
{
NSInteger localCounter = 42;
__block char localCharacter;
void (^aBlock)(void) = ^(void) {
++CounterGlobal;
++CounterStatic;
CounterGlobal = localCounter; // localCounter fixed at block creation
localCharacter = 'a'; // sets localCharacter in enclosing scope
};
++localCounter; // unseen by the block
localCharacter = 'b';
aBlock(); // execute the block
// localCharacter now 'a'
}
In this example, both localCounter and localCharacter are modified before the block is called. However, inside the block, only the modification to localCharacter would be visible, thanks to the __block keyword. Conversely, the block can modify localCharacter and this modification is visible outside of the block.
#bbum covers blocks in depth in a blog post and touches on the __block storage type.
__block is a distinct storage type
Just like static, auto, and volatile, __block is a storage type. It
tells the compiler that the variable’s storage is to be managed
differently....
However, for __block variables, the block does not retain. It is up to you to retain and release, as needed.
...
As for use cases you will find __block is sometimes used to avoid retain cycles since it does not retain the argument. A common example is using self.
//Now using myself inside a block will not
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;
When you don't use __block, the block copies the variable (call-by-value), so even if you modify the variable elsewhere, the block doesn't see the changes.
__block makes the blocks keep a reference to the variable (call-by-reference).
NSString* str = #"hello";
void (^theBlock)() = ^void() {
NSLog(#"%#", str);
};
str = #"how are you";
theBlock(); //prints #"hello"
In these 2 cases you need __block:
If you want to modify the variable inside the block and expect it to be visible outside:
__block NSString* str = #"hello";
void (^theBlock)() = ^void() {
str = #"how are you";
};
theBlock();
NSLog(#"%#", str); //prints "how are you"
If you want to modify the variable after you have declared the block and you expect the block to see the change:
__block NSString* str = #"hello";
void (^theBlock)() = ^void() {
NSLog(#"%#", str);
};
str = #"how are you";
theBlock(); //prints "how are you"
__block is a storage qualifier that can be used in two ways:
Marks that a variable lives in a storage that is shared between the lexical scope of the original variable and any blocks declared within that scope. And clang will generate a struct to represent this variable, and use this struct by reference(not by value).
In MRC, __block can be used to avoid retain object variables a block captures. Careful that this doesn't work for ARC. In ARC, you should use __weak instead.
You can refer to apple doc for detailed information.
__block is a storage type that is use to make in scope variables mutable, more frankly if you declare a variable with this specifier, its reference will be passed to blocks not a read-only copy for more details see Blocks Programming in iOS
hope this will help you
let suppose we have a code like:
{
int stackVariable = 1;
blockName = ^()
{
stackVariable++;
}
}
it will give an error like "variable is not assignable" because the stack variable inside the block are by default immutable.
adding __block(storage modifier) ahead of it declaration make it mutable inside the block i.e __block int stackVariable=1;
From the Block Language Spec:
In addition to the new Block type we also introduce a new storage qualifier, __block, for local variables. [testme: a __block declaration within a block literal] The __block storage qualifier is mutually exclusive to the existing local storage qualifiers auto, register, and static.[testme] Variables qualified by __block act as if they were in allocated storage and this storage is automatically recovered after last use of said variable. An implementation may choose an optimization where the storage is initially automatic and only "moved" to allocated (heap) storage upon a Block_copy of a referencing Block. Such variables may be mutated as normal variables are.
In the case where a __block variable is a Block one must assume that the __block variable resides in allocated storage and as such is assumed to reference a Block that is also in allocated storage (that it is the result of a Block_copy operation). Despite this there is no provision to do a Block_copy or a Block_release if an implementation provides initial automatic storage for Blocks. This is due to the inherent race condition of potentially several threads trying to update the shared variable and the need for synchronization around disposing of older values and copying new ones. Such synchronization is beyond the scope of this language specification.
For details on what a __block variable should compile to, see the Block Implementation Spec, section 2.3.
It means that the variable it is a prefix to is available to be used within a block.
About a year ago, when I first got into programming, I learned the hard way that variables don't escape the scope of the condition they're created in. For example:
-(void)someMethod {
if (x == y) {
NSString *string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
NSLog(string); // Doesn't work, we're out of the scope of the "if" statement.
}
My question is, is there any way to dynamically create variables within a conditional statement and be able to access them at other times, kind of like if I declared it in my #interface?
EDIT I guess I didn't explain it well, I meant if I wanted to use it later in other methods.
You just need to declare (and optionally initialize) the variable outside of the if. So something like:
-(void)someMethod {
NSString *string = nil;
if (x == y) {
string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
NSLog(string);
}
EDIT
To respond to your clarification, 'string' here is a local variable, local to this method. You have other options like instance variables for example. Instance methods like this one (ones that start with '-') will have access to this instance's (this object's) instance variables.
So you could do:
#interface MyClass : NSObject {
NSString *string; // <<-- This is an instance variable (aka "ivar")
}
- (void)someMethod;
- (void)someOtherMethod;
#end
#implementation MyClass
- (void)someMethod {
string = #"Foo";
}
- (void)someOtherMethod {
NSLog (string);
// will print "Foo" provided someMethod was called before this method
}
#end
Obviously there's more to this than you can get in a short answer. You can have global variables. If you're new to the language, you should read properties as a very useful tool for encapsulating instance variables (useful when you want to get the memory mgmt right). But hopefully that gets you pointed in the right direction at least.
-(void)someMethod {
NSString *string = nil;
if (x == y) {
string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
NSLog(string); // Doesn't work, we're out of the scope of the "if" statement.
}
Consider what you're asking. You start with:
-(void)someMethod
{ if (x == y)
{ NSString *string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
NSLog(string); // Doesn't work, we're out of the scope of the "if" statement.
}
What would you like the second NSLog to do? You seem to be requesting that it either work, if the body of the if statement has been executed, or what? Produce a dynamic "undeclared variable" error?
Now you seem to be wanting:
-(void)someMethod
{ if (x == y)
{ NSString *string = [[NSString alloc] initWithString:#"Foo"];
NSLog(string); // outputs "Foo" to console successfully
}
...
}
-(void)someOtherMethod
{
NSLog(string); // Doesn't work, we're out of the scope of the "if" statement.
}
What do you wish to happen in someOtherMethod if the body of the if statement in someMethod has not been executed?
As #Daniel's solution points out, you can reference a variable provided it is in scope. In the single method case you move the point of declaration out of the if and into the enclosing method body. In the two method case you move it to the class - as an instance variable.
Now scope isn't the same as lifetime - a variable can exist (be alive), but no be accessible (it is out of scope); the common example is when one method calls another, the calling method's variables stay alive but are inaccessible to the called method.
In a similar way that a variable is in scope does not mean that it has to contain a valid value. That is why #Daniel puts nil in the variable - you know if a value has been created inside the body of the if by the value of string not being nil.
So maybe this is the "dynamic" behaviour you seem to be seeking - you must declare some way to reference the value (the variable), but you indicate whether it has been created by storing some sentinel (nil in this case) in the variable.
- (void)createAString:(NSString **)str
{
*str = [NSString stringWithString:#"Hi all!"];
[*str autorelease]; // ???? is this right ?
}
How should I use release or autorelease ? I don't want to release outside of the function of course :)
...
NSString *createStr;
[self createAString:&createStr];
NSLog(#"%#", createStr);
You're correct that you'd generally want to return autoreleased (or the like) objects from out params when you use this form. Your assignment statement in the function that sets *str to a string:
*str = [NSString stringWithString:#"foo"];
is already doing the right thing, because that method returns an instance of NSString that the caller doesn't own. Just like you could return this string object from your function without any further memory management, you can set it as the outparam as you've done. Your second snippet showing the call site is fine.
This said, I'm worried about a few things in your code that you should be sure you understand:
The value of str inside the method is still a **, and sending that a message (as you've done for the speculative autorelease) is nonsense. Be sure you fully understand doubly indirected pointers before using them too liberally. :) If you need to send str a message after creating it, send it to *str, which is what contains the NSString *.
Setting an outparam like this when the function returns void is not idiomatic Cocoa. You would normally just return the NSString * directly. Outparams are rare in Cocoa. (Usually just NSErrors get this treatment from framework calls. Otherwise they conventionally use name like getString to differentiate them from normal get accessors which don't use the word "get".)
I hope -stringWithString was just an example. That method is almost never used in practice, since it's equivalent (in this case) to just using a #"string literal" (although that would muddy your example).
Instead of using a double pointer, would it not be more elegant to use an NSMutableString instead?
- (void)createAString:(NSMutableString *)str
{
[str setString:#"Hi all!"];
}
....
NSMutableString *createStr = [[NSMutableString alloc] init];
[self createAString: createStr];
NSLog(#"%#", createStr);
[createStr release];
Or, even better, just have the createAString method return an NSString.
- (NSString *)createAString
{
return #"Hi all!"; // this is autoreleased automatically
}
I wouldn't want to presume that your needs are this simple, though. =)