0

BOUNTY NOTICE

Hello! I started bounty on this question because people don't read whole question and don't try to understand root of the problem. Or, don't know what is going on here. More detailed approach needed.. Thank you!

END

When I call NSURLSession with my custom task - I store those tasks in a dictionary. When I get delegate calls back I need to find which task is it about. Here is how I do it:

- (NSInteger)getOperationIdentifierForTask:(NSURLSessionTask*)task
{
    for (NSString *key in self.operations)
    {
        HttpTaskOperation *value = [self.operations objectForKey:key];
        if (value.task == task) return [key integerValue]; //**********
    }

    return 0;
}

It DID work for me previously (with iOS7 for sure) but now this comparison never succeeds. I also pulled screenshot from locals, see object id's highlighted. Yes, I'm not C developer so it might be related to issue :) I assume it's due to pointer issue..

When line of code with //***** executes - I don't get match. This function always returns 0.

enter image description here

enter image description here

enter image description here

EDIT:

Seems like issue related to PROXY. iOS when I create and store NSURLSession actually returns NSProxy. Same for NSUURLSessionTask. Is this all something new and how can I compare those objects given what you see in locals? I need to get task._taskDelegate but it's not exposed..

katit
  • 17,375
  • 35
  • 128
  • 256
  • Aren't you comparing _task (0x787346a0) with task (0x78753180)? _task.taskDelegate is different from _task – Dmitry Kurilo Sep 18 '15 at 20:39
  • You're not comparing the `taskDelegate`; you are comparing the `task` itself. Try `if (value.task.taskDelegate == task) {...}`. – jakenberg Sep 18 '15 at 20:40
  • There's not enough context to answer the question. You have a pointer `task` being passed to the method, and an array that's expected to contain that same pointer. However, without seeing where the pointer came from, or how the array was populated, there's no way to know whether that expectation is reasonable. – user3386109 Sep 18 '15 at 20:46
  • See my edit where I show locals and snippets of comparison line while running - they both (left and right sides) show same id – katit Sep 18 '15 at 20:47
  • 2
    There's a contradiction between the screen shot of the local variables, and the other two screenshots. Specifically, the last screen shot shows `value.task` as 0x78690480. But in the local variables, you've highlighted `value.task.taskDelegate`. – user3386109 Sep 18 '15 at 20:56
  • I agree with you, but this is what I see in Xcode, this is what they show me on compared values. – katit Sep 18 '15 at 20:58
  • One possibility is that you have a multi-threading problem. Another thread is modifying `self.operations` while this thread is looping through `self.operations`. That could confuse the debugger. OTOH, you can just ditch the debugger, and debug with NSLog's, e.g. `NSLog( @"%p",task)` and then inside the loop `NSLog(@"%p", value.task)`. – user3386109 Sep 18 '15 at 21:07
  • Yes, now I see it. Actually I see it from locals as well, but not sure what changed. How do I get this _taskDelegate out of CRNSURLSessionTaskProxy. And what is this CRNSURLSessionTaskProxy anyway, no documentation found about it.. – katit Sep 18 '15 at 21:19
  • `[value.task isEqual:task]` i think this should do the trick. From my experience it is not good to compare objects with `==`. Let me know if it works – rob180 Sep 23 '15 at 08:36
  • Try to compare address of both it's same – Varun Naharia Sep 24 '15 at 09:22

6 Answers6

3

It is unclear how you are adding items to operations.

This line is misleading:

for (NSString *key in self.operations)

since in your debug trace, it seems your keys are actually NSNumbers in operations.

Why don't you try using the taskIdentifier. It is supposed to uniquely identify the task within the session.

- (NSInteger)getOperationIdentifierForTask:(NSURLSessionTask*)task
{
    NSNumber * value = self.operations[@(task.taskIdentifier)];

    if( value == nil )
         return 0;

    return value.integerValue;
}
Timothy Davison
  • 429
  • 2
  • 7
  • String is because it's a NSMutableDictionary. I'm wrapping all NSURL tasks into NSOperations. But problem is - when I get call back from NSURLSession - they pass only task back. Essentialy what I'm doing in this method - trying to match which NSOperation carries NSURLSessionTask.. But seems like all of the NSURLSessionTasks I create being wrapped into NSProxy and this, in turn causes issue I have. I don't know why it worked before. Essentially, I need to somehow pull "real" object out of NSProxy – katit Sep 18 '15 at 22:27
  • To whoever upvoted it: NSURLSessionTask.taskIdentifier is read-only property set by OS. I can't use it myself. – katit Sep 21 '15 at 00:42
  • Your answer did point me to the right direction, I didn't get it at first, I don't really need to SET taksIdentifier. So, it works to fix and work around issue. I will award bounty to you if nobody replies with full explanation on why I started to have this issue as it was working before just fine.. – katit Sep 21 '15 at 01:39
2

You can't use the primitive equality check == on non primitive objects. You should be using isEqual:. Override it in your subclass and compare whichever properties of the item that are required to be the same to ensure equality.

Do something like this in your subclass:

-(BOOL)isEqual:(id)object{
    if([object isKindOfClass:[YourSubClass class]]){
        YourSubClass *otherObject = (YourSubClass*)object;

        NSInteger currentDistinctiveNumber = [self.someDistinctiveProperty integerValue];
        NSInteger otherDistinctiveNumber = [otherObject.someDistinctiveProperty integerValue];
        if(currentDistinctiveNumber == otherDistinctiveNumber){
            return YES;
        }
    }

    return NO;
}
Tommy Devoy
  • 13,441
  • 3
  • 48
  • 75
1

If you're certain it worked in iOS 7 and, I'm making a guess here, you didn't have any issues until the transition into iOS 9, I would say that the issue is probably being caused by Apple's new App Transport Security.

Reason:

In iOS 9 and OS X 10.11, Apple introduced App Transport Security (ATS), a low-level set of restrictions on apps’ network connections. One of the most visible of these restrictions is the requirement that apps no longer make connections over plain HTTP; instead, the OS enforces the use of HTTPS unless explicitly told otherwise.

Judging from your provided screenshots, it does seem that the URL you are pointing to is a http:// based URL, not a https://. I say this because your problem seems to begin in the HTTPTaskOperation.

The Fix:

Add the following Dictionary to your app's .plist file:

enter image description here

This completely voids Apple's new Security (ATS)

If that is a concern, or if it is still not working, you'll need to adjust the Exceptions to fit your project's needs. Look over the earlier link, or read Apple's App Transport Security documentation.

.

If NOT an iOS 9 ATS Problem

~ Added ~

This stack overflow Question and Answer gives the following explanation:

If you are using the delegate based implementation, things start to get pretty hairy pretty quickly, though. This is because of an understandable (but incredibly annoying) feature of NSURLSession whereby the task-level delegates are implemented at the session-level. (Think about that: Two different requests that require different handling are calling the same delegate method on the shared session object. Egad!)

Wrapping a delegate-based NSURLSessionTask in an operation can be done(...) but it involves an unwieldy process of having the session object maintain a dictionary cross referencing task identifiers with task operation objects, have it pass these task delegate methods passed to the task object, and then have the task objects conform to the various NSURLSessionTask delegate protocols. It's a pretty significant amount of work required because NSURLSession doesn't provide a maxConcurrentOperationCount-style feature on the session (to say nothing of other NSOperationQueue goodness, like dependencies, completion blocks, etc.)

Community
  • 1
  • 1
ChrisHaze
  • 2,800
  • 16
  • 20
  • I've edited my post with a picture of the .plist additions – ChrisHaze Sep 24 '15 at 23:20
  • Chris, this is not ATS Problem (I did have that because I use SSL but it's fixed and not related to Q. What you explain in second part is exactly what I have done, I wrapped into NSOperationQueue, that function on top is exactly what it is - cross-referencing NSOperation with NSURLSessionTask – katit Sep 25 '15 at 14:07
0

The "==" operator in Objective-C checks to see if both variables point to exactly the same object. If you have 2 different objects that contain the same data, the test will fail.

As others have said, you should either implement the isEqual method for your custom subclass, or if you don't have an easy way to subclass/add a category to your class, you can create a global function that takes 2 of your objects as parameters and returns a BOOL that tells you if they are equivalent.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • This is exact same object and this is what I'm trying to figure out. See I added screenshots where I compare. IT IS same object but doesn't return.. – katit Sep 18 '15 at 20:52
0

'==' in objective-c tests for equality of reference on objects. This means that it checks if two objects are the same object not if their values are the same. Try using the objects isEqualTo: function. If it's your own object, write an isEqualTo: function that tests for equality of a certain value in that object.

Sidd Menon
  • 856
  • 6
  • 18
0

Perhaps you can store a unique identifier for each task in the taskDescription property of the NSURLSessionTask before you store it your operations and then compare later using that value. This value should remain constant, allowing you to avoid a pointer comparison between the objects being passed around.

Mean Dinosaur
  • 376
  • 1
  • 12