Objective c — Update parameter in block - objective-c

I was doing some tinkering with tree traversals (which I have solved in a much more straightforward way) but I have come across an issue in the following piece of Objective C logic:
- (NSString *)someWrapperFunction
{
NSString *result = #"";
NSString *(^appendBlock)(int, NSString **) = ^NSString *(int a, NSString **adder){
if (a == 0)
{
// base case
return #"";
}
NSLog(#"%d", a);
*adder = [*adder stringByAppendingFormat:#"-%d-", a];
NSLog(#"adder: %#", *adder);
return [*adder stringByAppendingString:appendBlock(a-1, adder)];
};
appendBlock(5, &result);
return result;
}
Basically, I want to create a block of code that concatenates numbers into the given string (adder). The result should be: "-5--4--3--2--1-".
I get a segmentation fault with the above code but with some other code that I wrote for the tree traversal, the adder string was essentially not getting updated. Any pointers to what I am doing wrong here? (Is it possible that the variable that is being updated by the inner block (inside recursion) is disallowed as it is already being occupied by the outer block OR is it just that NSString is non-mutable data type?)
In any case, I want to keep the design of the function the same; how would I solve this problem (using c/objective)?

After some searching and experimenting I found a way to fix this.
There is no reason to be using a double-pointer for your adder parameter in the block. Just use a regular pointer and update your code accordingly.
The error is coming from the fact that inside of the block, appendBlock is NULL and you end up dereferencing the NULL pointer trying to call it.
Here's an updated version that works:
- (NSString *)someWrapperFunction
{
NSString *result = #"";
NSString *(^appendBlock)(int, NSString *);
__block __weak NSString *(^weakBlock)(int, NSString *);
weakBlock = appendBlock = ^NSString *(int a, NSString *adder){
NSString *(^innerBlock)(int, NSString *) = weakBlock;
if (a == 0)
{
// base case
return #"";
}
NSLog(#"%d", a);
adder = [adder stringByAppendingFormat:#"-%d-", a];
NSLog(#"adder: %#", adder);
// Split this update to make it easier to debug.
NSString *update = innerBlock(a-1, adder);
return [adder stringByAppendingString:update];
};
appendBlock(5, result);
return result;
}
Output: "-5--4--3--2--1-"
This update is rewritten for point #1 (which really has nothing to do with your original issue.
To solve point #2 this update creates the original appendBlock variable as well as a new __block __weak weakBlock reference to the same block. And then inside the block, a new (strong) block pointer is created to reference the weak block pointer. Without the use of the weak pointer, the code works but causes a warning.

Related

What is the best way to initialise a variable in objective c, before a conditional statement?

I used to initialise variables before a conditional statement in the following way:
NSString *string = [[NSString alloc] init];
if (conditional statement) {
string = #"foo";
}
else{
string = #"bar";
}
But the Xcode Analyser complains:
"Value stored to 'string' during its initialization is never read"
So, I then tried a couple of different options:
A:
NSString *string = nil;
if (conditional statement) {
string = #"foo";
}
else{
string = #"bar";
}
B:
NSString *string = #"bar";
if (conditional statement) {
string = #"foo";
}
So my question is, what is the best way to initialise a variable in objective c, before a conditional statement?
UPDATE:
The variable itself is not used [read] in the conditional. Here is an example below...
NSString *string = [[NSString alloc] init];
if (x == 0) {
string = #"foo";
}
else{
string = #"bar";
}
UPDATE:
Based on Sven's answer, it seems like a good compromise is:
NSString *string;
if (x == 0) {
string = #"foo";
}
else{
string = #"bar";
}
A and B are both valid options. In the end it won't really matter, if you just assign string literals. The compiler might even generate the same code for both cases.
Of course if you assign something other than compile-time constants you need to be more careful. Depending on the side effects that happen in your computation only one or the other version will be correct.
In your case A you won't even have to nil-initialise the variable at first, the compiler is smart enough to see that it is initialised in any case. For patterns like this where you want to initialise an variable depending on some conditions it's actually a good idea to skip the initialisation where the variable is defined. Then the compiler can produce a warning if there is a code path where you forgot to initialise the variable.
Another option for this is to use the ternary operator ?::
NSString *string = condition ? #"foo" : #"bar";
This is not just shorter to write, but also makes it immediately clear that the variable is initialised no matter what the condition is.

Why dereferencing a NSString pointer is not necessary?

In the example
NSString *message = #"Hello";
message = #"World";
If message is just a pointer why don't I need to explicitly say whatever is in message is now equal to string or *message = #"World"; like in C?
DISCLAIMER
The discussion below gives a general idea on why you never dereferenciate a pointer to an object in Objective-C.
However, concerning the specific case of NSString literals, this is not what's happening in reality. While the structure described below is still sound and it may work that way, what's actually happening is that the space for a string literal is allocated at compile time, and you get its address back. This is true for string literals, since they are immutable and constant. For the sake of efficiency therefore each literal is allocated only once.
As a matter of fact
NSString * a = #"Hello";
NSString * b = #"Hello";
NSLog(#"%# %p", a, a); // Hello 0x1f4958
NSLog(#"%# %p", b, b); // Hello 0x1f4958
ORIGINAL ANSWER
Because it will be translated to
message = [[NSString alloc] initWithUTF8String:"Hello"]];
which will boil down to
message = objc_msgSend(objc_msgSend(objc_getClass("NSString"), #selector(alloc)), #selector(initWithUTF8String:), "Hello");
Now if we take a look to the signature of objc_msgSend
id objc_msgSend(id theReceiver, SEL theSelector, ...)
we see that the method returns an id type, which in Objective-C is the object type. But how is id actually defined?
typedef struct objc_object {
Class isa;
} *id;
id is defined as a pointer to an objc_object struct.
So in the end #"string" will translate in a function call that will produce a pointer to an object (i.e. an objc_object struct, if you prefer), which is exactly what you need to assign to message.
Bottom line, you assign pointers, not objects.
To better clarify the last concept consider this
NSMutableString * a = [NSMutableString stringWithString:#"hello"];
NSMutableString * b = a;
[a setString:#"hola"];
NSLog(#"%#", a); // "hola"
NSLog(#"%#", b); // "hola"
If you were assigning objects, b would have been a copy of a and any further modification of a wouldn't have affected b.
Instead what you get is a and b being two pointers to the same object in the heap.

Recursive Blocks in Objective-C leaking in ARC

So I'm using recursive blocks. I understand that for a block to be recursive it needs to be preceded by the __block keyword, and it must be copied so it can be put on the heap. However, when I do this, it is showing up as a leak in Instruments. Does anybody know why or how I can get around it?
Please note in the code below I've got references to a lot of other blocks, but none of them are recursive.
__block NSDecimalNumber *(^ProcessElementStack)(LinkedList *, NSString *) = [^NSDecimalNumber *(LinkedList *cformula, NSString *function){
LinkedList *list = [[LinkedList alloc] init];
NSDictionary *dict;
FormulaType type;
while (cformula.count > 0) {
dict = cformula.pop;
type = [[dict objectForKey:#"type"] intValue];
if (type == formulaOperandOpenParen || type == formulaListOperand || type == formulaOpenParen) [list add:ProcessElementStack(cformula, [dict objectForKey:#"name"])];
else if (type == formulaField || type == formulaConstant) [list add:NumberForDict(dict)];
else if (type == formulaOperand) [list add:[dict objectForKey:#"name"]];
else if (type == formulaCloseParen) {
if (function){
if ([function isEqualToString:#"AVG("]) return Average(list);
if ([function isEqualToString:#"MIN("]) return Minimum(list);
if ([function isEqualToString:#"MAX("]) return Maximum(list);
if ([function isEqualToString:#"SQRT("]) return SquareRoot(list);
if ([function isEqualToString:#"ABS("]) return EvaluateStack(list).absoluteValue;
return EvaluateStack(list);
} else break;
}
}
return EvaluateStack(list);
} copy];
NSDecimalNumber *number = ProcessElementStack([formula copy], nil);
UPDATE
So in my own research I've discovered that the problem apparently does have to do with the references to the other blocks this block uses. If I do something simple like this, it doesn't leak:
__block void (^LeakingBlock)(int) = [^(int i){
i++;
if (i < 100) LeakingBlock(i);
} copy];
LeakingBlock(1);
However, if I add a another block in this, it does leak:
void (^Log)(int) = ^(int i){
NSLog(#"log sub %i", i);
};
__block void (^LeakingBlock)(int) = [^(int i){
Log(i);
i++;
if (i < 100) LeakingBlock(i);
} copy];
LeakingBlock(1);
I've tried using the __block keyword for Log() and also tried copying it, but it still leaks. Any ideas?
UPDATE 2
I found a way to prevent the leak, but it's a bit onerous. If I convert the passed in block to a weak id, and then cast the weak id back into a the block type, I can prevent the leak.
void (^Log)(int) = ^(int i){
NSLog(#"log sub %i", i);
};
__weak id WeakLogID = Log;
__block void (^LeakingBlock)(int) = [^(int i){
void (^WeakLog)(int) = WeakLogID;
WeakLog(i);
if (i < 100) LeakingBlock(++i);
} copy];
LeakingBlock(1);
Surely there's a better way?
Ok, I found the answer on my own...but thanks to those who tried to help.
If you're referencing/using other blocks in a recursive block, you must pass them in as weak variables. Of course, __weak only applies to block pointer types, so you must typedef them first. Here's the final solution:
typedef void (^IntBlock)(int);
IntBlock __weak Log = ^(int i){
NSLog(#"log sub %i", i);
};
__block void (^LeakingBlock)(int) = ^(int i){
Log(i);
if (i < 100) LeakingBlock(++i);
};
LeakingBlock(1);
The above code doesn't leak.
Aaron,
As your code appears to be single threaded, why are you copying the block? If you don't copy the block, you don't have a leak.
Andrew
Without further context information, I can say this:
You are leaking that block because you are copying it and not releasing it elsewhere. You need to copy it to move it to the heap, that's ok. But the way you've chosen is not entirely ok.
A correct way to do it is to store it as some object instance variable, copy it, and then release it inside dealloc. At least, that's a way to do it without leaking.

Objective C: Why am I getting "EXC_BAD_ACCESS"?

I'm really new to Objective C and am trying to write a program to go through the collatz conjecture. When I run the program, it stops after the first scanf and comes up with "EXC_BAD_ACCESS". Here's my code:
int original,i;
NSString *PrintFull;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"Collatz Conjecture:");
NSLog(#"Print full results?");
scanf("%s",PrintFull);
NSLog(#"What number should we go up to?");
scanf("%d", &original);
while (original <= 100) {
NSLog(#"\n\n%d", original);
i = original;
while (i != 1) {
if (i % 2) {
i = (i*3)+1;
} else {
i = (i/2);
}
if ([PrintFull isEqualToString:#"yes"]) {
NSLog(#"%d",i);
}
}
original++;
}
}
What am I doing wrong here?
scanf does not work with with object types such as NSString. Please see SO post - Using scanf with NSStrings.
scanf's arguments after the format string should point to already allocated objects. In this case you've just declared a pointer and passed it in without setting it. scanf will try to write to this location, but since the pointer contains a garbage value, the application crashes.
scanf is from the C library 'stdio.h', meaning it doesn't know about NSStrings, which are from the Objective-C 'Foundation' framework.
The following should solve these problems
int original,i;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"Collatz Conjecture:");
NSLog(#"Print full results?");
char inputBuffer[80];
scanf("%s", inputBuffer);
NSString *printFull = [NSString stringWithCString:inputBuffer encoding:NSUTF8Encoding];
First, you have to initialize and alloc the NSString. Second, scanf can't handle NSString.
Also notice, that class names begin with a capital letter and class instances with a small one.

Anyway to get string from variable name?

Say I have my class
#interface Person : NSObject { NSString *name; }
I need to get the name of NSString's within my class
Person *person = [[Person alloc] init];
NSLog(#"Name of variable %s\n", _NameofVariable_(person->name));
Thanks for the answers, here's the solution I came up from the replies
//returns nil if property is not found
-(NSString *)propertyName:(id)property {
unsigned int numIvars = 0;
NSString *key=nil;
Ivar * ivars = class_copyIvarList([self class], &numIvars);
for(int i = 0; i < numIvars; i++) {
Ivar thisIvar = ivars[i];
if ((object_getIvar(self, thisIvar) == property)) {
key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
break;
}
}
free(ivars);
return key;
}
As easy as
#define VariableName(arg) (#""#arg)
Then you do:
NSObject *obj;
NSString *str = VariableName(obj);
NSLog(#"STR %#", str);//obj
You can get the names of a class's instance variables with the Objective-C runtime API function class_copyIvarList. However, this is rather involved, rarely done and almost never the best way to accomplish something. If you have a more specific goal in mind than mere curiosity, it might be a good idea to ask about how to accomplish it in Objective-C.
Also, incidentally, person.name doesn't specify an instance variable in Objective-C — it's a property call. The instance variable would be person->name.
You might use preprocessor stringification and a bit of string twiddling:
NSUInteger lastIndexAfter(NSUInteger start, NSString *sub, NSString *str) {
NSRange found = [str rangeOfString:sub options:NSBackwardsSearch];
if(found.location != NSNotFound) {
NSUInteger newStart = NSMaxRange(found);
if(newStart > start)
return newStart;
}
return start;
}
NSString *lastMember(NSString *fullName) {
if(!fullName) return nil;
NSUInteger start = 0;
start = lastIndexAfter(start, #".", fullName);
start = lastIndexAfter(start, #"->", fullName);
return [fullName substringFromIndex: start];
}
#define NSStringify(v) (##v)
#define _NameofVariable_(v) lastMember(NSStringify(v))
If the person object is exposed as a property of the class, you can use objc_msgSend to get the value.
So, if you could access person using
[object person]
You could also do
objc_msgSend(object, "person")
For more details on message sending, including how to pass arguments to methods, see the Objective-C Runtime Programming Guide section on Messaging
The following works as a macro:
#define STRINGIZE(x) #x