So I'm trying to pull in data from Parse.com and then add it to a global array to update a table view with. Right now I have:
- (void)loadData {
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
for (PFObject *object in objects) {
EventObject *thisEvent = [[EventObject alloc] initWithPFObj:object];
[self.events addObject:thisEvent];
}
[self.tableView reloadData];
}];
}
When the tableview tries to reload the data, it finds an object in self.events, but the object's properties are all nil. (I think this has something to do with weak/strong self in an asynchronous block, but I can't figure it out.) How do I get the data to be preserved between this block and the reload?
Since you are using block to fill data on your array by accessing Self, you must use a weakSelf variable to dont loose the reference.
So you must use this weakSelf instead of just Self, on your loadData method:
__weak typeof(self) weakSelf = self;
Also, remember to put your NSMutableArray "events" as Strong, not Weak.
Edit: A brief explanation of the difference between Strong and Weak references: https://stackoverflow.com/a/18344946/2788711
Related
I have a parse class with a NSArray column, it is an array of dictionaries, and inside that dictionaryI have a key with a pointer to another class. I'm trying to use includeKey but it is not working. I'm hoping to get the objects inside pointers without calling fetch, since there are many, the requests could go up easily.
I'm using: [query includeKey:#"ingredients.item"]; Ingredients is an NSArray of dictionaries, it is a column in the main object. item is a key in the dictionary that stores pointers.
[{"item":{"__type":"Pointer","className":"NutricaoCatalogo","objectId":"aTnHD5ttUH"},"ingredientTitle":"Clara De Ovo Natural","portionIndex":"1","portionQuantity":200},
Try this it may help you
PFQuery *query = [PFQuery queryWithClassName:#"ingredients"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error && objects) {
for(int i = 0; i < objects.count; i++) {
PFObject *object = [objects objectAtIndex:0];
PFObject *pointer = [[object valueForKey:#"item"] valueForKey:#"__type"];
PFObject *obj = [PFQuery getObjectOfClass:#"<classname of that pointer points to>" objectId:(NSString *)pointer];
//obj is an object of that pointer so you can manupulate accordingly
//Note: getObjectOfClass: objectId: method run on main thread
}
}
}];
include only works on pointers. It doesn't even work on an array of pointers.
One point I want to make first is that unless you have a ton of active users, you're not going to hit the mark for free requests any time soon. If you are, your app should be profitable enough that it doesn't matter.
My best solution would be to call a background job that performs a query.each() and pulls all of the object ids that you'd like to get, then returns the array of objectIds. Then, you can query for all the objects with ids that are containedIn that array.
I have some block that get data from server. I could see that I get error when trying to return my array from that block. Question is, what is right :
use delegates from the block to the other class that use it
don't use block,but selector method that being called when done, and I give it argument that is being filled and return.
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
for(PFObject *object in objects)
{
PFObject *user = [object objectForKey:#"userId"];
//can't return value to other class here ..
}
}];
Should I use delegate here? (is it possible?)
The typical Cocoa way to do this would be to use a delegate with a callback. If you use selectors instead, you will still need to pass the block an instance of the object that the selector belongs to, and you lose some compile-time safety.
You can simply declare a variable or a property assignable in block with __block prefix and then you can assign a value to it in the block and use everywhere you need.
Example:
__block NSString *string;
I finally found my memory bug is caused by referring self strongly in a block. But I don't know why in a similar case, the weak is not needed:
I have a CameraCaptureManager class doing image capture tasks, and a CameraViewController has a strong property of this manager. The manager has weak delegate property pointing back to the controller.
This is where I must use weakSelf in the manager, otherwise -(void)dealloc won't be called:
// in CameraCaptureManager
__weak CameraCaptureManager *weakSelf = self;
void (^deviceOrientationDidChangeBlock)(NSNotification *) = ^(NSNotification *notification) {
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
[weakSelf updateVideoOrientation:deviceOrientation];
};
self.deviceOrientationDidChangeObserver = [notificationCenter addObserverForName:UIDeviceOrientationDidChangeNotification
object:nil
queue:nil
usingBlock:deviceOrientationDidChangeBlock];
The manager holds the deviceOrientationDidChangeObserver strongly, so weakSelf is needed to break the memory retain cycle. That's fine, I got that... but I find I don't have use weakSelf in a similar case in the same class:
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error){
UIImage *image = nil;
if (imageDataSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
image = [[UIImage alloc] initWithData:imageData];
}
if ([self.delegate respondsToSelector:#selector(captureManager:capturedStillImage:)]) {
[self.delegate captureManager:weakSelf capturedStillImage:image];
}
}];
The manager also holds the stillImageOutput strongly, but why I can use the strong "self" in the completion block? The manager object gets dealloc with this strong self inside the block. I'm confused, please shed some light.
Also do I need to use weakSelf in the 2nd case even when it won't cause any retain cycle?
In your second code example you have a temporary retain cycle. When the completionHandler block has been called, the block is released and with it the captured self, so
that the release cycle is broken.
I have read Apple's Blocks Programming Topics and my due diligence searching online, but I am still unclear if I am implementing my blocks correctly. I have an array of clients as a property that is populated when an NSNotification is sent. Clients is used as a tableview data source. The code below works, but I am curious if it is putting self in a retaining cycle. Should I do something like __block id theClients = self.clients; and then reference theClients inside the block?
#property (strong, nonatomic) NSMutableArray *clients;
NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
__block id observer = [notifyCenter addObserverForName:queryHash
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification* notification){
// Explore notification
if ([[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0]) {
NSArray *rows = [[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0];
if (self.clients)
{
self.clients = nil;
}
self.clients = [[NSMutableArray alloc] initWithCapacity:rows.count];
for (NSDictionary *row in rows) {
[self.clients addObject:row];
}
} else {
NSLog(#"CLIENTS ERROR Returned: %#",[notification.userInfo objectForKey:kerrorReturnKey]);
}
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
There is no problem in accessing the clients property because it is a strong (i.e. retained) property. So you don't need the __block here.
One problem can be that self might not exist anymore when the notification is sent. Then you would access the deallocated object and the app can crash! To avoid that you should remove the observer in the dealloc method.
The __block before id observer is definitely required !
EDIT:
In iOS 5 you can safely capture self using a weak reference:
__weak id weakSelf = self;
Then inside the block you can safely use weakSelf.clients. The variable weakSelf will turn into nil automatically when the object is deallocated.
Yes, you have a retain cycle, at least until the notification occurs. When you access the clients ivar in the block, the block will retain self. It will be retained by the block in notification center until the notification occurs (since you remove the observer at the end of the block). If that's not desirable in your case, you can use a weak reference to self.
NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
__weak id weakSelf = self;
id observer = [notifyCenter addObserverForName:queryHash
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification* notification) {
if (weakSelf) {
if ([[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0]) {
NSArray *rows = [[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0];
if (weakSelf.clients)
{
weakSelf.clients = nil;
}
weakSelf.clients = [[NSMutableArray alloc] initWithCapacity:rows.count];
for (NSDictionary *row in rows) {
[weakSelf.clients addObject:row];
}
} else {
NSLog(#"CLIENTS ERROR Returned: %#",[notification.userInfo objectForKey:kerrorReturnKey]);
}
}
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
I don't see any reason you need to __block qualify observer.
It's also not clear you're getting anything out of using the block-based API here. If you don't want to worry about the potential retain cycle, you could just use addObserver:selector:name:object: and put the body of your notification callback in an instance method.
I have two additional columns on my PFUser "firstName" and "lastName". They are saving properly; I can see the data in the data browser.
I have another PFObject "class" that has a property with a NSArray of PFUsers. I use the class method +fetchAllIfNeededInBackground:block: on PFObject to fetch the array of PFUsers. In the callback block, I call objectForKey: on each of the PFUsers in the array, but I access them through the owning PFObject.
// invited is the NSArray of PFUsers
self.whoCell.mainLabel.text = [[self.plan.invited objectAtIndex:0]
objectForKey:#"firstName"];
Debugger outputs this at a breakpoint right before the objectForKey call:
(lldb) po self.plan.invited
(NSArray *) $4 = 0x06e62a60 <__NSArrayM 0x6e62a60>(
<PFUser:GCCdPjCU2J> {
firstName = Fake;
lastName = Account;
username = yyajnbafv53qw4yhjm9sfoiis;
}
)
Edit: adding implementation of self.plan.invited because the above is misleading.
- (NSArray*)invited
{
// self.dataSource is a PFObject*
return [self.dataSource objectForKey:INVITED_PROP];
}
Yet when the above call it made to objectForKey: this exception is thrown:
'NSInternalInconsistencyException', reason: 'Key "firstName" has no data. Call fetchIfNeeded before getting its value.'
Edit: Accessing the array of fetchedObjects that is passed to the block callback for +fetchAllIfNeededInBackground doesn't throw, but accessing the actual array that was originally passed to +fetchAllIfNeededInBackground throws.
Calling fetchIfNeeded before the call solves the problem, but why? The data is already there. Do I miss understand +fetchAllIfNeededInBackground in that it DOES NOT updated the PFObject that owns to collection of PFUsers?
I figured out what was happening. Let me explain with code:
PFQuery *query = [PFQuery queryWithClassName:#"TestClass"];
PFObject *testObj = [query getObjectWithId:#"xWMyZ4YEGZ"];
// an array of PFUser "pointers" (pointers in Parse parlance)
NSLog(#"%#", [testObj objectForKey:#"userArrayProp"]);
[PFObject fetchAll:[testObj objectForKey:#"userArrayProp"]];
// now userArrayProp contains fully fetched PFObjects
NSLog(#"%#", [testObj objectForKey:#"userArrayProp"]);
For me, after a certain amount of time userArrayProp would revert to an array of "pointers" and this mystified me. My problem was that calling refresh on a PFObject will revert a fetched array BACK to an array of pointers. Like this:
PFQuery *query = [PFQuery queryWithClassName:#"TestClass"];
PFObject *testObj = [query getObjectWithId:#"xWMyZ4YEGZ"];
// an array of PFUser "pointers" (pointers in Parse parlance)
NSLog(#"%#", [testObj objectForKey:#"userArrayProp"]);
[PFObject fetchAll:[testObj objectForKey:#"userArrayProp"]];
// now userArrayProp contains fully fetched PFObjects
NSLog(#"%#", [testObj objectForKey:#"userArrayProp"]);
[testObj refresh];
// now userArrayProp contains pointers again :(
NSLog(#"%#", [testObj objectForKey:#"userArrayProp"]);
Wish it said that [PFObject refresh] did that in the documentation....
What you are describing should work fine. fetchAllIfNeeded updates the objects in the array themselves, so it shouldn't matter how you access them. You say you are accessing them through the parent PFObject, but your debugger output is showing access through the array directly. Is it possible the array isn't pointing to the current PFObject member?
One thing you could try while debugging is calling isDataAvailable on the instances of PFUser, both after the fetchAllIfNeeded, and right before you access their first and last names. After the call to fetchAllIfNeeded, isDataAvailable should return YES for every element of the array. If it still returns YES when you access the names, they should not give this error.
Otherwise, if you can provide a minimal code sample that reproduces the problem, I'd be glad to debug it further.
Thanks,