using performSelector in a c function to call delegate - objective-c

Scenario
if (delegate && [delegate respondsToSelector:#selector(aboutTextUpdated:)]) {
[delegate aboutTextUpdated:aboutText];
}
to simplify this kind of checks in my project I created a couple of c functions as below and used them.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
id callSelectorOnDelegateWithObject(SEL selector,NSObject *delegate,id object)
{
if (delegate != nil && [delegate respondsToSelector:selector] == YES)
{
if (object != nil)
{
return [delegate performSelector:selector withObject:object];
}
else
{
return [delegate performSelector:selector];
}
}
return nil;
}
id callSelectorOnDelegate(SEL selector,NSObject *delegate)
{
if (delegate != nil && [delegate respondsToSelector:selector] == YES)
{
return [delegate performSelector:selector];
}
return nil;
}
#pragma clang diagnostic pop
they were called as below
callSelectorOnDelegateWithObject(#selector(aboutTextUpdated:), delegate, aboutText);
Problem
I get EXC_BAD_ACCESS error some times, but not in a reproducible manner. The exception occurred when the c function was called but before executing the first statement. When I replace the function call with actual if statement, then the error never occurs. To meet project deadline I went ahead with that change. But couldn't get this error out of my head.
What is causing the error?
More Background
The project uses ARC, minimum target is iOS 5.0
The project uses AFNetworking 1.* to get data from server, core data to save it in the app and NSNotification for inter app communication
The methods given to perform selector always return void

We would need to see the details of the crash to know more. My best guess is that there are occasional calls to callSelectorOnDelegateWithObject() where object is nil but the method identified by selector actually does take an argument. In that case, you're using just -performSelector:, not -performSelector:withObject:. So, the method receives garbage for its argument.
If some code called callSelectorOnDelegateWithObject() rather than callSelectorOnDelegate() then you should pass the object argument through unconditionally. The caller passing nil does not mean you can drop the argument. The nil probably (or, at least, possibly) is important.
That said, this whole approach doesn't seem like a good idea. I would not find this scheme simpler than the code snippet at the top of your question.
You can simplify that code snippet by not explicitly checking if delegate is non-nil. The -respondsToSelector: check will already return false if delegate is nil, because messaging nil always results in false.
Finally, you should never compare against YES (or TRUE, etc.). You're taking an expression which is already a boolean expression and then making it a compound boolean expression. Let me ask if you would ever write code like the following:
if (([delegate respondsToSelector:selector] == YES) == YES)
...
If you would not write that second == YES (and an infinite sequence of further comparisons to YES), then you should understand why you shouldn't have written the first.
Beyond that, any non-zero value is a true value. YES is only one such value. For any given method which returns a boolean value, you can't be sure that the true value it returns is actually YES as opposed to any other true value.

Related

Obj C - Is there a way to make a method return the method in which it is called?

I want to know whether there is the way to achieve the following requirement:
First, there is a methodA, which takes an input objA and check whether objA is valid for the method. If it's valid, then it returns an object objB. However, if objA is not valid, it just returns objA itself. Note that even if objA is valid, the returned objB may still be the same with objB.
Then, there is a methodB and in the method, methodA is called. If the input of methodA is valid, the program go on without error. However, if methodA is called with invalid input, then methodB should terminate (a.k.a return with a certain string signaling an error) to prevent future crash.
The current solution that I could think of is to create a (BOOL)methodAInputIsValid:(obj)input and in methodB there is:
if(methodAInputIsValid:input) {
obj objReturn = methodA:input;
}else{
//show error warning
return;
}
//continue doing something with objReturn and finally return some valid thing
A problem of the code is that (BOOL)methodAInputIsValid:(obj)input and methodA:(obj)input share a lot of code in common because there is a validity-test in methodA. So I want to use the validity-test more efficiently so that it will work for methodB and drop the (BOOL)methodAInputIsValid.
Here is one possible application that I could think of:
In a program the user is asked to enter a string that will direct the action of the program. However, the input string may need to be standardized such as converting all letters to lower-case and converting all single quotation to double quotation and auto-complete any missing right parenthesis or quotation marks. However, if the user is just entering nonsense that cannot be standardized, then the program should terminate and warn the user of the problem. The two methods are (BOOL)isStandardizeable:(NSString *)input and (NSString *)standardize:(NSString *)input. Because (NSString *)standardize already returns an NSString *, I can't make it to return another BOOL, right? So the following code is not achievable:
if(standardize:input){
NSString *result = standardize:input;
} else {
NSLog(#"unrecognizable input!");
return;
}
//continue...
Can anyone think of a way to do this?
Realistically, I'd just have methodA return nil if the input is not valid. This is a fairly common method to signal failure to a caller. If you really need the "identity" part of the method, just have callers do
id res = [... methodA:input];
if(res == nil)
res = input;

Check for completion within NSMutableArray of custom objects populated from NSURLConnection

First off, a big thank you to the SO community. I have learned a great deal. However, I am still an extreme novice w/re to Objective C and thus have a question. Apologies in advance if this is an ignorant question.
I have subclassed NSURLConnection to fetch my custom objects (myObject) from my web API. Each object requires 2 calls to the API for completion. The first call is to grab an id property from a list of my Objects. The second call is the to use that id to construct a different URL and populate the rest of the myObject properties. All is working well but I have a question as to the correctness of my approach for reloading a tableViewsection based on a completion of all of themyObjectobjects within anNSMutableArray`.
Here is the method I call after successfully instantiating and fetching all of the incomplete myObjects and adding them to an NSMutableArray. messageStringis a property of myObject that is only available/set on the second network call for each of the instances of myObject. Thus, I thought I would use it to check for completeness. arrayOfMyObjects is mutable and contains all of the incomplete myObjects. MyStoreClass is just that. A store that handles the creation of the subclassed NSURLConnections.
- (void)fetchDetails {
void (^completionBlock)(myObject *obj, NSError *err, int statusCode) = ^(myObject *obj, NSError *err, int statusCode) {
if (!err && statusCode == 200) {
NSArray *completionCheckArray = [arrayOfMyObjects filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"messageString = %#", [NSNull null]]];
if ([completionCheckArray count] == 0) {
[[self tableView] reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
}
} else if (!err && statusCode != 200) {
[self statusCodeError:statusCode];
} else {
[self generalError:err];
}
};
for (myObject *mobj in arrayOfMyObjects) {
[[MyStoreClass sharedStore] fetchDetails:mobj withCompletion:completionBlock];
}
}
While this works, it seems inefficient to me to have to create an array through the completion block for every single one of myObjects. If so, what would be an alternative approach to checking completion of all of myObjects?
A possible and feasible approach would be to go wit KVO (Key Value Observing). Check out the appropriate Key-Value Observing Programming Guide.
For example: If you know how many objects (i.e. object count) you're expecting from a given service call,
you could hook up an observer on the array object holding your objects and be notified whenever an element is added etc.
See Observing an NSMutableArray for insertion/removal.

How can I check whether parameter of function is nil or not?

I made Sender class and sender object:
Sender *sender = [[Sender alloc] init];
Then, I wrote test:
// should success
[sender upload:[UIImage imageNamed:#"test2.jpg"] withName:#"test2.jpg"];
// should fail
[sender upload:nil withName:#"test2.jpg"];
Then, I wrote nil check code:
- (void)upload:(UIImage *)image withName:(NSString *)name
{
if ([image isEqual:nil]) {
...
}
...
}
But nil check is ignored.
How can I check whether the image parameter is nil or not?
Thank you for your kindness.
Try
if(!image)
or
if(image == nil)
Short answer, replace [image isEqual:nil] with image == nil.
Long answer, when you write:
[image isEqual:nil]
you are asking for the message isEqual: to be sent to the object whose reference is stored in the variable image.
However you don't wish to send a message to an object, you wish to determine whether the variable image contains a reference to an object or not. To do this you need to compare the value of your reference with the "no reference" value - which is written nil. The value comparison operator in Objective-C (and C et al) is == so you need to test:
image == nil
Now you might ask why did the code you have execute without error, just not producing the result you expected, given that there is no object to send a message to? Objective-C supports sending messages to "no object", aka nil, and returns the "zero" value for the result type, i.e. 0, nil etc. When converted to a boolean value, as in your if statement, a "zero" value produces NO.
HTH
#yeesterbunny's answer is on point as to what you should do in this situation, but it's also good to know why.
Remember that every objc method call is a message. When you call
[objA isEqual:objB]
you're sending a message to objA asking whether it considers itself "equal" to objB.
If the "image" parameter to your method is nil, there's nothing there to send a message to. By definition, any message to nil returns nil. (And "nil" is the same as zero in an if statement.)
Moral of the story: test pointer equality with == (or !=, or implicitly as in "if (!image)") if you're looking for nil. Use isEqual: only when you want to test whether two objects (known to exist and of similar type) are semantically equal (e.g. when you want to know if two instances of NSString contain the same text).

Objective-C self variable check

I am reading the Cocoa Design Patterns books. In the books, after assigning the self variable, it is checked whether self is nil like below
if( nil != self){
// code
}
I have also seen another way of checking the self like
if( self ){
// code
}
Is there any benefit for using either of these techniques or is it just a matter of preference?
Thanks.
They're the same
The contents of an if block
if (expression) {
// contents...
}
are executed just in case the expression evaluates to a non-zero value.
This means that the code in the following if block
//the first if block
if (self) {
// contents....
}
will execute whenever self is non-zero.
Meanwhile, the contents of this if block
// the second if block
if (self != nil) {
// contents...
}
are executed whenever self != nil is non-zero.
The operator != returns 0 in the case that the two comparands (in this case self and nil) are identical and returns 1 in the case that the two comparands are not identical. So the code in the second if block will execute whenever self is not identical to nil.
But nil is #defined to be 0 in <objc/objc.h>. Thus the code in the second if block will execute whenever self is not identical to 0. This is exactly the same time that the code in the first if block executes: whenever self is non-zero.
The upshot
Since the two approaches result in exactly the same code being executed, there are no technical reasons to prefer one to the other. The most important stylistic consideration in this case is consistency in your initializers. Decide which style you prefer and use it everywhere.

BOOL being set to 'NO' but succeeds for a check off == YES

.h
#property (nonatomic,assign) BOOL dontSendDelegate;
.m
#synthesize dontSendDelegate;
- (id) initWithSession:(AVCaptureSession *)aSession outputFileURL:(NSURL
*)anOutputFileURL
{
self = [super init];
if (self != nil)
{
self.dontSendDelegate = NO;
}
return self;
}
if (self.dontSendDelegate == YES)
{
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
Thats all my code in that class relating to the BOOL.
It always prints YES.
What is it that I'm not understanding? I expect it to always print NO.
EDIT
Used Xcode to search 'dontSendDelegate'
It only appears in the code I've shown. Which is copy and paste.
Changed it to an int and assigned 0 instead of the 'NO' and did the comparison == 1 instead of == YES and it works as you would expect. But I'm still lost as to why BOOL was not working.
Thanks for all the help and discussion about the problem.
When you put the mutable part of an expression (your bool instance variable) before a comparison operator such as ==, sometimes you have a typo where you write = instead, so you set the variable instead of compare it. Look for errors of this type.
Or always put the immutable value first, so in the code you have provided so far you would instead write
if (YES == self.dontSendDelegate)
That way, if you ever type one equal sign instead of two, the compiler will complain.
(from comments) When testing boolean variables, you don't need to use == at all. Just use if (self.dontSendDelegate) or if (!self.dontSendDelegate).
#GeorgFritzsche is correct. When this happens I override the setter and create a breakpoint. Then take a look at the stack on the left side of your screen and trace it down to the culprit. Most likely you are changing the value somewhere else in your program.