-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EXC_BAD_ACCESS when nesting [Promise new:...] calls #77
Comments
Can you show the code? Nesting news is fine, PromiseKit itself does this all over the place. Usually something is being deallocated because you need to keep a strong pointer to that thing around, and if I could see the code I would be able to advise. |
Here is the project I am working on, specifically this file: https://github.com/Streamlyne/Cocoa-SDK/blob/master/Common/SLStore.m |
Thanks, I'll check it out. |
As an FYI, this: - (PMKPromise *) find:(Class)modelClass withQuery:(NSDictionary *)query;
{
return [PMKPromise new:^(PMKPromiseFulfiller fulfiller, PMKPromiseRejecter rejecter) {
[self.adapter findQuery:modelClass withQuery:query withStore:self]
.then(^(NSDictionary *adapterPayload) {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
// FIXME: Should used a shared Serializer, etc.
SLSerializer *serializer = [[SLSerializer alloc] init];
// Extract from Payload
NSArray *extractedPayload = [serializer extractArray:modelClass withPayload:adapterPayload withStore:self];
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
[self pushMany:modelClass withData:extractedPayload]
.then(^(NSArray *records) {
fulfiller(records);
})
.catch(rejecter);
});
});
})
.catch(rejecter);
}];
} Could be simpler: - (PMKPromise *) find:(Class)modelClass withQuery:(NSDictionary *)query;
{
id q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
return [self.adapter findQuery:modelClass withQuery:query withStore:self].thenOn(q, ^(NSDictionary *adapterPayload) {
SLSerializer *serializer = [[SLSerializer alloc] init];
NSArray *extractedPayload = [serializer extractArray:modelClass withPayload:adapterPayload withStore:self];
return [self pushMany:modelClass withData:extractedPayload];
});
} You don't have to do any GCD with promises, just specify what queue you want in the continuation handlers. |
Even more specifically, around this: https://github.com/Streamlyne/Cocoa-SDK/blob/master/Common/SLStore.m#L226-L252 I hope that helps! Looking at my commits, I had about a lull of 4 hours last night/this morning where I was receiving this error and could not figure it out. And something as strange as commenting out the contents of the |
Also there's an AFNetworking+PromiseKit pod now: https://github.com/csotiriou/AFNetworking-PromiseKit in case that is useful. |
Oh wow, that's awesome!! Thank you for that optimization, @mxcl! I am still learning all of the exciting PromiseKit tricks and you've been a great help so far. |
This: - (PMKPromise *) push:(Class)modelClass withData:(NSDictionary *)datum
{
return [PMKPromise new:^(PMKPromiseFulfiller fulfiller, PMKPromiseRejecter rejecter) {
NSLog(@"push: %@",datum);
SLNid nid = (NSString *) datum[@"nid"];
[PMKPromise when:@[
[self record:modelClass forId:nid],
[self normalizeRelationships:modelClass withData:datum withStore:self]
]
]
.then(^(NSArray *results) {
SLModel *record = results[0];
NSDictionary *newDatum = results[1];
NSLog(@"record: %@", record);
NSLog(@"post normalizeRelationships datum: %@", newDatum);
//
[record setupData:newDatum];
NSLog(@"Pushed record: %@", record);
fulfiller(record);
})
.catch(rejecter);
}];
} Could be: - (PMKPromise *) push:(Class)modelClass withData:(NSDictionary *)datum
{
NSLog(@"push: %@",datum);
SLNid nid = (NSString *) datum[@"nid"];
return [PMKPromise when:@[
[self record:modelClass forId:nid],
[self normalizeRelationships:modelClass withData:datum withStore:self]
]
]
.then(^(NSArray *results) {
SLModel *record = results[0];
NSDictionary *newDatum = results[1];
NSLog(@"record: %@", record);
NSLog(@"post normalizeRelationships datum: %@", newDatum);
//
[record setupData:newDatum];
NSLog(@"Pushed record: %@", record);
return record;
});
} There's no reason to nest things that return promises in a Obviously it shouldn’t crash either way. |
I had been trying to go through and clean up when I read that if you return a Promise it will resolve it, so I could omit the |
A pleasure to help as it enables me to reconsider the documentation so people are guided towards the more condensed versions in future. |
I think I may have committed a typo in my morning rush to my office. I'll let you know once I've fixed that error. Working only now. |
Hey @mxcl. I apologize for not getting back to you sooner: I ended up having other high priorities to do at work and was unavailable for the weekend. Would you be able to try running the tests again and let me know how it goes and potentially what it shows in the console logs? I will be pushing to |
k np,, I'll look into it later today. |
I have stepped through the code and found for case where it breaks the // sig = nil
const char rtype = sig.methodReturnType[0]; See https://github.com/mxcl/PromiseKit/blob/master/objc/PMKPromise.m#L42-L44 ImportantI may have forgot to mention: I am seeing the error on the front-end application for iOS I am developing, that uses the Streamlyne Cocoa SDK that is public. However, the iOS app I am developing is closed source, so I am unable to show you the complete source code. |
On my iOS app I have debugged it down to the following snippet of code: NSLog(@"Attempt login");
[client authenticateWithUserEmail:email
withPassword:password
withOrganization:organization]
.then(^(SLClient *client, SLUser *me) {
NSLog(@"Logged in");
// Removed for brevity
}).catch(^(NSError *error) {
// Removed for brevity
}); It successfully logs Here is a sample of my console log (I redacted the unimportant information, just wanted to show you the starting message of those logs so you know where it is flowing):
I hope that helps! I believe I have used PromiseKit inappropriately in the Streamlyne Cocoa SDK and hopefully it is a simple fix / typo. |
I believe I have found the issue! I am calling if (count == 0)
return [PMKPromise promiseWithValue:@[]]; That leads me to https://github.com/mxcl/PromiseKit/blob/master/objc/PMKPromise.m#L324-L329 + (PMKPromise *)promiseWithValue:(id)value {
PMKPromise *p = [PMKPromise alloc];
p->_promiseQueue = PMKCreatePromiseQueue();
p->_result = value ?: PMKNull;
return p;
} And that is as far as I have gotten so far to finding a solution. Manually adding checks in my SDK code to prevent from using I am going to try and look into how the |
Thanks for the assistance! I will really look at this soon. I've had a bunch of urgent client work that cannot wait sadly. |
Not a problem. Let me know when you're working on it so I can help out :). |
Thanks @dbachrach! I did find that commit and even the passing test; I was writing a comment here about my findings, but must have forgot to hit send. |
I still cannot reproduce with your project:
Is there some setup step I have missed? |
Shoot. Sorry about that. Looks like I could be accessing a Core Data context from the wrong thread. I went through the other day and optimized most of the methods with the PromiseKit optimizations you suggested. I may have wrote another typo.. I'll take another look. Thanks |
How am I meant to reproduce this issue? I thought running the test cases would do it, but you just showed that they all pass. |
I am not entirely sure. The tests pass for the SDK, however when used in the closed source iOS application that uses this SDK they fail with something as simple as
that there may be something I should wrap my usage in to prevent from deallocating and causing the EXC_BAD_ACCESS errors. I was also wondering if it could be an issue between the Mac and iOS version of PromiseKit. Since I am using the Mac version for the SDK tests and the iOS for the breaking app. I have narrowed it down to a basic call to I'm not sure how else I can help. I wish there was some way I could distribute the reproducing App to you so you can experience what I am, however it's company IP. I think I will try to build a basic iOS app as an open source project, just to demo a reproduction of the error I am experiencing. I hope that will help. |
@mxcl here is a demo app that successfully reproduces the same error I am having: https://github.com/Streamlyne/cocoa-sdk-iso-demo Let me know if there is anything else I can do! Thank you! :) |
K, if I force an anti-ARC retain on the block the crash doesn't happen. So I'm actually thinking we are exposing some bug in ARC here. |
This is worrying, but this copy fixes the crash. I think we probably need to do more copies. Fixes #77.
0.9.16.3 pushed to CocoaPods. |
It worked! Thank you! |
np. I should report this bug to Apple really. I'm pretty sure ARC is prematurely releasing the block based on my reading the spec. |
Result:
![image](https://cloud.githubusercontent.com/assets/1885333/4010381/3c34480c-29f4-11e4-984e-c7501c754b3d.png)
I am wrapping the internals of many asynchronous method's with
and am now finding that
EXC_BAD_ACCSS
issue, as mentioned on the website:I have been "debugging" this for hours, however I can find little helpful information or correlation except that it stops if I flatten the Promise chain a little and then occurs later on.
I am hoping that you may know what I am talking about / seen this before / have a solution. I have been up all night working on this and implemented PromiseKit everywhere and not am a little discouraged to see I have nothing to show for my nights work because of a strange error that I cannot debug.
Please let me know if there is anything I can do to help you diagnose. Thank you!
The text was updated successfully, but these errors were encountered: