iOS what does performSelector:withObject:withObject: really do? - ios

What's different between selectors with same arguments type but different arguments sort?
I got these two selectors with same arguments type.
- (void)methodWithCallBack:(void(^)(void)) cb double:(double)value {
NSLog(#"%s %f", __PRETTY_FUNCTION__, value);
if (cb) {
cb();
}
}
- (void)methodWithDouble:(double)value callBack:(void(^)(void)) cb {
NSLog(#"%s %f", __PRETTY_FUNCTION__, value);
if (cb) {
cb();
}
}
But when performSelector:withObject:withObject: called with these selectors, I got different result.
[self performSelector:#selector(methodWithDouble:callBack:) withObject:#(2.5) withObject:[^(void){
NSLog(#"Test Call Back Double");
} copy]];
[self performSelector:#selector(methodWithCallBack:double:) withObject:[^(void){
NSLog(#"Test Call Back Double");
} copy] withObject:#(2.5)];
How does this happend? What does performSelector:withObject:withObject: really do?

I have no idea what #(2.5) is. NSNumber literal is #2.5. a double is non object and NSNumber is object. You should pass an NSNumber.
On a second note: I feel weird that Apple created performSelector:withObject:withObject:. A single "withObject" is suffice actually, you just pass an NSArray with objects you want to pass to that.
Eg.
NSNumber *var1 = #2.5;
NSString *yourMom = #"Sally";
NSArray *params = [NSArray arrayWithObjects:var,yourMom,nil];
[self performSelector:#selector(goMethod:) withObject:params];

Related

Recursive method Objective-C

I'm checking is first letter of string is 0, if it is remove it and call again method to check is there is still 0. I've debugged this and it seems like when it accomplish number without 0, it goes backwards. Code:
-(NSString *)deleteZerosOnFirst:(NSString *)card
{
NSString *firstLetter = [card substringToIndex:1];
if ([firstLetter isEqualToString:#"0"]) {
card = [card substringFromIndex:1];
[self deleteZerosOnFirst:card];
NSLog(#"CARD: %#", card);
return card;
}
else {
NSLog(#"CARD: %#", card);
return card;
}
}
The main problem is that you're not using the result of the recursion. The line of code where you call yourself should say this:
card = [self deleteZerosOnFirst:card];
Also, you're calling deleteZerosOnFirst before you do the NSLog. Reverse the order of these two lines. That will at least give you your debug output in the right sequence.
Here's your recursive call:
[self deleteZerosOnFirst:card];
That doesn't modify the string that card references. It creates and returns a new string. You're ignoring the returned string. You want this:
card = [self deleteZerosOnFirst:card];
But this is really a lot simpler:
#implementation NSString (withoutLeadingZeroes)
- (NSString *)withoutLeadingZeroes {
NSString *s = self;
while ([s hasPrefix:#"0"]) {
s = [s substringFromIndex:1];
}
return s;
}
#end

Mantle issue - MTLValueTransformer won't transform an NSNumber to an int

I'm using Mantle to successfully transform values in my model - but this one comes back with the error: Incompatible block pointer types sending 'int (^)(NSNumber *__strong)' to parameter of type 'MTLValueTransformerBlock' (aka 'id (^)(__strong id)'):
+ (NSValueTransformer *)numDownvotesJSONTransformer
{
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSNumber *number) {
return [number intValue];
} reverseBlock:^(int value) {
return [NSNumber numberWithInt:value];
}];
}
This similar transform works fine:
+ (NSValueTransformer *)longitudeJSONTransformer
{
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSNumber *number) {
return [NSDecimalNumber decimalNumberWithDecimal:[number decimalValue]];
} reverseBlock:^(NSDecimalNumber *decimalNumber) {
return [NSNumber numberWithDouble:[decimalNumber doubleValue]];
}];
}
Not understanding the difference here.
Answer from the project's maintainer: "Transformers must operate upon objects. You don't need to do anything for an NSNumber going to a primitive property, because Key-Value Coding takes care of that automatically."

how to pass the uncertain parameters form one method to another on Objective-C? [duplicate]

i have googled and came to know that how to use the variable arguments. but i want to pass my variable arguments to another method. i m getting errors. how to do that ?
-(void) aMethod:(NSString *) a, ... {
[self anotherMethod:a];
// i m doing this but getting error. how to pass complete vararg to anotherMethod
}
AFAIK ObjectiveC (just like C and C++) do not provide you with a syntax that allows what you directly have in mind.
The usual workaround is to create two versions of a function. One that may be called directly using ... and another one called by others functions passing the parameters in form of a va_list.
..
[obj aMethod:#"test this %d parameter", 1337);
[obj anotherMethod:#"test that %d parameter", 666);
..
-(void) aMethod:(NSString *)a, ...
{
va_list ap;
va_start(ap, a);
[self anotherMethod:a withParameters:ap];
va_end(ap);
}
-(void) anotherMethod:(NSString *)a, ...
{
va_list ap;
va_start(ap, a);
[self anotherMethod:a withParameters:ap];
va_end(ap);
}
-(void) anotherMethod:(NSString *)a withParameters:(va_list)valist
{
NSLog([[[NSString alloc] initWithFormat:a arguments:valist] autorelease]);
}
You cannot pass variadic arguments directly. But some of these methods provide an alternative that you can pass a va_list argument e.g.
#include <stdarg.h>
-(void)printFormat:(NSString*)format, ... {
// Won't work:
// NSString* str = [NSString stringWithFormat:format];
va_list vl;
va_start(vl, format);
NSString* str = [[[NSString alloc] initWithFormat:format arguments:vl] autorelease];
va_end(vl);
printf("%s", [str UTF8String]);
}
Have you considered setting up your arguments in either an array or dictionary, and coding conditionally?
-(void) aMethodWithArguments:(NSArray *)arguments {
for (id *object in arguments) {
if ([object isKindOfClass:fooClass]) {
//handler for objects that are foo
[self anotherMethod:object];
}
if ([object isKindOfClass:barClass]) {
//and so on...
[self yetAnotherMethod:object];
}
}
}
I think you could use macros to achieve same thing.
Let's say you wanna pass aMethod's variable arguments to another
-(void) aMethod:(NSString *) a, ... {
}
You could define your another 'method' using macro though it is not a real method:
#define anotherMethod(_a_,...) [self aMethod:_a_,##__VA_ARGS__]
This is my solution.

NSDictionary Enumeration Cause "malloc: double free" Error

I get a double free error when using [nsdictionary enumerateKeysAndObjectsUsingBlock:]
CnFExhibition_0821(74624,0x114853000) malloc: * error for object 0x7fe972814fa0: double free
This happen mostly when using appendFormat: in enumeration block, not always happen but quite often.
I finally prevent it by not using enumerateKeysAndObjectsUsingBlock:, but still wondering why?
This happen when calling this line, which "newData.list[0]" is a NSDictionary,
[database updateRow:#"01" inTable:#"Exhibition" setting:newData.list[0] error:nil];
SQLiteHelper.m
-(BOOL)updateRow:(id)rowID inTable:(NSString*)tablename setting:(NSDictionary*)setting error:(NSError *__autoreleasing *)err{
if ([self checkTableName:tablename error:err]){
if ([self checkUpdateSetting:setting error:err]) {
NSMutableString * updateCmd = [sqlCmdUpdateFromTable(tablename) mutableCopy];
[updateCmd appendString:sqlCmdSetRow(setting)];
if (rowID) {
[updateCmd appendString:sqlCmdWhereCondition(#{[self pkOfTable:tablename]:rowID})];
}
return [self execCmdStr:updateCmd error:err];
}else{
return NO;
}
}else{
return NO;
}
}
NSString * sqlCmdSetRow(NSDictionary*setting){
if (setting && setting.count){
NSMutableString * setCmd = [NSMutableString stringWithString: #" SET "];
[[setting copy] enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id key, id obj, BOOL *stop){
if ([obj isKindOfClass:[NSString class]]){
//*Mostly crush at this line*
[setCmd appendFormat:#"%#='%#', ",key,obj]];
}
else{
[setCmd appendFormat:#"%#=%#, ",key,obj];
}
}];
[setCmd deleteCharactersInRange:NSMakeRange(setCmd.length-2, 2)];
return setCmd;
}
else{
return nil;
}
}
Replacing enumeration in "sqlCmdSetRow" with code below and never happen again
NSArray * a = [setting allKeys];
for (NSString * s in a) {
if ([setting[s] isKindOfClass:[NSString class]]){
[setCmd appendString:[NSString stringWithFormat:#"%#='%#', ",s,setting[s]]];
}else{
[setCmd appendFormat:#"%#=%#, ",s,setting[s]];
}
}
By using -[NSDictionary enumerateKeysAndObjectsWithOptions:usingBlock: with the NSEnumerationConcurrent option, you are effectively calling appendString: and appendFormat: on the same NSMutableString object (setCmd) on multiple threads simultaneously. The documentation of those methods doesn't say anything about thread safety, so they probably aren't thread-safe. Your random crashes back that up.
You did the right thing by changing to a for-in loop. Now that you are only touching setCmd on a single thread, there is no thread safety issue and the crashes went away.

iOS - Stanford CS193P Fall 2011 course, Assignment 2 descriptionOfProgram issue

Part of this assignment includes printing out on the display the current equation that is present to be solved, for that I use the following methods:
+ (NSString *)descriptionOfTopOfStack:(NSMutableArray *)stack {
NSMutableString *programFragment = [NSMutableString stringWithString:#""];
id topOfStack = [stack lastObject];
if (topOfStack) [stack removeLastObject];
if ([topOfStack isKindOfClass:[NSNumber class]]) {
[programFragment appendFormat:#"%g", [topOfStack doubleValue]];
} else if ([topOfStack isKindOfClass:[NSString class]]) {
NSString *operation = topOfStack;
if ([self isDoubleOperandOperation:operation]) {
[programFragment appendFormat:#"(%# %# %#)", [self descriptionOfTopOfStack:stack], operation, [self descriptionOfTopOfStack:stack]];
} else if ([self isSingleOperandOperation:operation]) {
[programFragment appendFormat:#"%#( %# )", operation, [self descriptionOfTopOfStack:stack]];
} else if ([ self isNoOperandOperation:operation]) {
[programFragment appendFormat:#"%#", operation];
} else if ([self isVariable:operation]) {
[programFragment appendFormat:#"%#", operation];
}
}
return programFragment;
}
+ (NSString *)descriptionOfProgram:(id)program {
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
return [self descriptionOfTopOfStack:stack];
}
My program computes the results and everything just fine, the only problem is that when I enter a variable, digit or single operand operation the display only shows said last entry, because it doesn't continue to iterate over the rest of the values present in the array, because no other recursive calls are made, any idea how I can make the program execute throughout the entire stack and not have it break the output?
I am not quite sure what you mean. The recursion should stop at a variable, digit or single operand operation. Although for a sin(operand) operation it should continue with the operand.
Did you take into account that your stack might be not completely defined?
Say you enter: 3 Enter 5 + 6 Enter 7 * 9 sqrt
this should translate to: 3+5, 6, sqrt(7*9)
So you have three elements still on your stack, but your approach stopped at sqrt(7*9).
You need to add a check at the to see if there is anything left on the stack, and continue if necessary (and add the comma's).
OK, another hint then (to be added at the end):
if ([stack count]) { // did I finish the entire stack?
[programFragment appendFormat:#"%#, %#", [self describeStack:stack], programFragment];
}
Interestingly you have used a NSMutableString, I did it with a NSString and used the class method stringWithFormat. So each time my result is a new string. I do not know if either approach is better.
aleene already answered, but just to clarify. I added the [stack count] check in the method that calls the recursive function.
+ (NSString *)descriptionOfProgram:(id)program {
NSMutableArray *stack;
NSString *strDesc = #"";
if ([program isKindOfClass:[NSArray class]]) {
// Make a consumable, mutable copy:
stack = [program mutableCopy];
}
while (stack.count) {
strDesc = [strDesc stringByAppendingString:[self descriptionOfTopOfStack:stack]];
if (stack.count) {
// More statements still on stack. We will loop again, but first, append comma separator:
strDesc = [strDesc stringByAppendingString:#", "];
}
}
return strDesc;
}

Resources