Any danger using performselector in objective-c? - ios

I've been stuck for a couple of days with a crash on Crashlytics that says :
libobjc.A.dylib
objc_msgSend
I managed to point out what seems to be the reason of this crash : __NSThreadPerformPerform
With a little search on stack it seems to be linked with performSelector:withObject:afterDelay:
The code I have mentions a delay of 0 all the time. I use this method to call out what we could call "functors" in iOS, by recreating selectors dynamically from an array of strings that I access from a enum state.
And basically it's all the informations I have from this so far, I learned that the afterDelay: makes your call delayed to the next RunLoop. Does it applies as well on performSelector:withObject: or the afterDelay: adds something to the method that causes this?
And, finally, the real question :
Is performSelector:withObject:afterDelay: a good way to make functors, or is it dangerous to use it like this, and should I be doing any other way?
Thanks in advance,

Don't just assume that your object still exists and can perform selector. Test it before!
if ([someObject respondsToSelector:#selector(someSelector)]) {
[someObject performSelector:#selector(someSelector) withObject:nil];
}

Related

cancelPreviousPerformRequests with arbitrary object

I am scheduling a method to be called with an object in the near future and the object is just a random NSString that is gone as soon as I schedule the selector.
So I may say something like:
[self performSelector:#selector(runMethod:) withObject:#"randomString" afterDelay:1.0f];
If I need to cancel this BEFORE it fires documentation says to use:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(runMethod:) object:***];
• *The only issue is I don't know what the "object" is, it was just a random string that doesn't exist anymore and has been released by ARC by now.
How can I cancel any scheduled methods with a specific selector (in my case runMethod:) but without knowing the "object"?
Is there any way to get a list of all scheduled functions in the NSRunLoop and just iterate through them with a for loop looking for ones with specific selector names?
If you need to cancel things you should organise a better way to schedule them such that you can actually check what it scheduled and the details associated with it. A potential solution would be a custom class with a set of parameters. Internally this class runs a timer which executes the action at the specified fire time. An array of instances of this class would be trivial to search and cancel arbitrary items from.
If, as I understand, the randomString is useless for you, then pass nil to both performSelector... and cancelPreviousPerform...
Like this:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(runMethod) object:nil];
[self performSelector:#selector(runMethod) withObject:nil afterDelay:1.0f];

Method mysteriously exits execution in the middle to resume the rest of the program?

So i've got this code that tries to find an unused upload name, using the user's email and a number at its end. It does this with a list of uploaded objects we've already collected, the user's email.(upload_name), and the
current number that might be open (it is incremented when a match is found).
The list is not sorted, and it's pretty tricky to sort for a few reasons, so I'm having the method read through the list again if it reaches the end and the upload_number has changed.
- (NSString*)findUnusedUploadNameWithPreviousUploads:(NSMutableArray*)objects withBaseUploadName:(NSString*)upload_name {
previous_upload_number = upload_number;
for (NSString *key in objects) {
// the component of the object name before the first / is the upload name.
NSLog([key componentsSeparatedByString:#"/"][1]);
if ([[key componentsSeparatedByString:#"/"][1]
isEqualToString:([NSString stringWithFormat:#"%#_%ld", S3KeyUploadName1, upload_number])]) {
upload_number++;
NSLog([NSString stringWithFormat:#"upload name: %#_%ld", S3KeyUploadName1, upload_number]);
}
NSLog(#"pang");
}
NSLog(#"ping");
if (previous_upload_number == upload_number) {
return [NSString stringWithFormat:#"%#%ld", upload_name, upload_number];
}
return [self findUnusedUploadNameWithPreviousUploads:objects withBaseUploadName:upload_name];
}
The problem is, the program never reads the "ping". it just leaves the method after the first for loop is done.
Edit: No the NSlogs are fine, you can do simple string OR StringWithFormat.
Edit: Don't mind the unnecessary use of recursion, I did this because the simple way was having the same problem and i wanted to see if a different (albeit unnecessarily recursive) way would share that problem. It does.
Edit: I set a breakpoint in the for loop, and I set a break point at the "ping". It does not reach the ping. It completes the for loop and the ditches the whole thing.
Edit: Please try to help me figure out why it's exiting the the method immediately after the for loop. I'm aware this is stylistically meh, and I promise I'll make it shiny and spotless when it works. =]
Edit: to be clear, the method DOES exit. it does so early I know this because the rest of the program following this method (which is not threaded such that it wouldn't have to wait for it) runs AFTER this for loop, consistently.
There are a couple of possible explanations for the described behavior:
The method never exits. For some reason it blocks or performs infinitely somewhere in the loop. Make sure this is not the case by setting a breakpoint after the place where the message is called (i.e. the place to where it should return).
The method, or some method it calls, throws an exception. While seldom and unsupported in productive Cocoa code it could be some misbehaving 3rd party library or just a simple programmer error, where Cocoa actually does throw. Make sure this does not happen by setting an exception breakpoint in Xcode.
Undefined behavior. This is, sadly, part of official C and heavily exploited by modern compilers. It basically means: Anything can happen, if there's something in your code, where the standard says that the behavior is not defined. One example would be accessing a deallocated object. Another fine reason of undefined behavior can be threads accessing common data in an unsynchronized way.
Other than exceptions there's no explanation for a method to "exit early". If you still can't find the reason I suggest you invest some time to learn the debugger. Single stepping, like Aris suggested, might be a way to find out what's going on.

How to tell what code is calling a method?

I have a method that's getting called in my iPhone app at an unexpected time, and I can't figure out what code is calling it. Is there any way to find this out from an Objective C method or an Xcode feature? Basically...
- (void)myMethod {
NSLog(#"who just called me?");
}
This method is called from lots of places in my code, so just searching for the method name doesn't narrow it down enough. I could temporarily add an argument to the method and then edit every call to the method to add a unique identifier as the argument, but that would require a lot of edits and then undoing all the edits when I'm done.
Ideally I would find the class and line number of the code that called this method. I don't need to use this info in the method, I just need it for debugging.
You can log the call stack to see from where the method got invoked
- (void)myMethod {
NSLog(#"who just called me? %#", [NSThread callStackSymbols]);
}
If you are running the app through Xcode then simply set a breakpoint in your myMethod and run the app. When the breakpoint is reached, the app will pause and the debugger will show you the stack trace leading to the method call. Then you can see exactly what it going on.
No need to log a stack trace. Look at in real time with the debugger.
Use NSThread +callStackSymbols — you can just log that directly.
Xcode give us a great feature "Break Point" you just need to put the break point in our method and you can see where the control reach.
This is the best I've come up with. Add some code to the method that crashes the app:
- (void)myMethod {
NSLog(#"who just called me?");
[self nonexistentMethod];
}
Then after the app crashes, type bt into the Xcode console to get a backtrace. From there I can see the last few methods that were called before this one. It's ugly but it works. Is there a better way?

NSMutableArray mutation crash

I'm facing "Collection was mutated while being enumerated" crash in dispatch_async method. This is the code:
- (void) addCutObjectFromObject: (Object *) object {
dispatch_async(self.objectsQueue, ^{
ObjectCut *objectCut = [[[ObjectCut alloc] init] autorelease];
objectCut.objectId = object.uid;
objectCut.objectName = object.internal.name;
#synchronized(self.shownChannelsArray) {
if (![self.objectsArray containsObject: objectCut])
{
[self.objectsArray addObject: objectCut];
}
}
});
}
Dispatch_queue is created in the init method like this:
_objectsQueue = dispatch_queue_create("objectsQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_retain(_objectsQueue);
The code crashes without synchronized block. As far as I know, it should not crash, because there is no loop, and blocks should be added one-by-one waiting for their queue. Can someone explain me, why that is happening?
dispatch_queue_create("objectsQueue", DISPATCH_QUEUE_CONCURRENT) creates a concurrent queue. Arbitrarily many blocks dispatched to that queue may execute simultaneously. It's likely that containsObject: uses enumeration internally to do a linear search. Therefore one block is in the middle of containsObject: while another performs addObject:. NSMutableArrays are, like the other Foundation mutable objects, not guaranteed to be thread safe.
At the very least you want to use DISPATCH_QUEUE_SERIAL. Unless the order is really important to you or your objects don't implement hash, isEqual:, etc, you should probably also just use a set rather than sort of manually recreating one with an array.
Aren't you calling addCutObjectFromObject: from a loop or an enumeration ?
If It is, you may want to check this part.
Ah a tricky one.. from my knowledge (and I may be a bit off, so others, feel free to follow up, but this poor chap doesnt have any responses yet..
so when this executes, the async block will not run right away. It will first be scheduled, I would imagine you are calling this addCutObjectFromObject method more than once. Let's say for example sake that you are calling this method 5 times. Now think about threads. The first call gets scheduled, eventually begins running, while the second call gets scheduled, eventually starts running, etc.. and this is where the problem starts and I believe your culprit is in containsObject and addObject. Under the hood, containsObject is iterating through the array. Since there are more than 1 thread executing, while one thread is iterating the array, another could be calling addObject. Thus generating your crash of 'collection was mutated while being iterating'
Two options for solutions
1) in containsObject, check against a copy of the array rather than the array itself (this is a bad approach because it requires duplicating of the content of the array, BUT it is easy to put in, and for small array sizes, could be a quick fix)
if (![self.objectsArray containsObject: objectCut]) =>
if (![[NSArray arrayWithArray:self.objectsArray] containsObject: objectCut])
Again, this should stop your sporadic crashes, but this is a BAD SOLUTION (because of the memory duplication)
2) create a temporary array to add the objects to, and once all threads are finished, add the contents of the temporary array to self.objectsArray
The error says the reason of crash. It is not allowed to change an array while enumerating.
if (![self.objectsArray containsObject: objectCut])
{
[self.objectsArray addObject: objectCut];
}
In the above code you were enumerating the array an inserting and object. This is where the error will be given. Hope this helps .. :)

Proper memory management when invoking a delegate callback that may cause object to be released

I'm trying to figure out the best practice for memory management around invoking a delegate callback.
One issue I had seen in the past is that invoking a delegate callback may cause the object to be deallocated before returning, which may cause it to crash if the object tries to access its own properties after invoking the callback.
For example, an object (e.g. A) may do something like this:
- (void)doStuff
{
[_delegate done];
NSLog(#"msg = %#", _msg);
}
If invoking done leads to A getting deallocated, the subsequent attempt to access _msg will result in a BAD_ACCESS crash.
It is possible to get around this by, say, delaying the invocation of done till the next run loop (e.g. by doing a dispatch_async), but that would force us to have to make it asynchronous. Alternatively, we can retain self prior to calling done and releasing right after, but that just seems like a hacky workaround as well. Does any one have a recommended style for dealing with this issue?
I'm not sure this question really has anything to do with 'delegates' to be honest but more just memory management in general.
If you're not finished with an object make sure you are still 'retaining' it. When you're finished with it 'release' it and don't access it any further.
Also try and move to ARC if possible and life becomes much easier! :)
It's crashing because the delegate you want to call refers to a deallocated object.To fix this crash you need to set Delegate = nil; in your dealloc method.
You can not set property of delegate as retain as it will cause issue in memory management.
It shouldn't be possible, that the delegate method releases the sender. What let you run in this situation?
It is always possible to pair +1 with -1 methods in one (C) block.
If you work with MRC:
Anyway, I would prefer a retain + autorelease on sender in the delegate method before causing the deallocation over a retain + release in the delegate. Therefore the sender should be added as a parameter to the delegate method as usual:
- (void)senderIsDone:(Sender*)sender
{
[[sender retain] autorelease];
…
[sender release]; // Something like this in your code
}
But at all: This should not happen.
Another strategy is to delay that code that causes the deallocation. In the example above
- (void)senderIsDone:(Sender*)sender
{
[sender performSelectorOnMainThread:#selector( release ) withObject:nil waitUntilDone:NO]; // Whatever make the sender disappear
}

Resources