iOS instance variables not initialised from within a block - iphone

i am using parse.com as backend for my app.
i need to get information from my backend and init an instance with this information.
i use this code in order to do so:
- (id) initWithTeamId:(NSString *)teamId
{
__block NSString *str;
__block FrFTeam *blockSelf = self;
PFQuery *query = [PFQuery queryWithClassName:#"teams"];
[query getObjectInBackgroundWithId:teamId block:^(PFObject *object, NSError *error) {
str = [object objectForKey:#"teamName"];
(void)[blockSelf initWithName:str players:nil thumb:nil];
}];
return self;
}
when this code is done self.name is set to null,
what am i doing wrong?
thank you!

Try this code:
// call init on the object, then setup the team id
- (id)initWithTeamId:(NSString *)teamId
{
self = [super init];
if (self) {
[self setupWithTeamId:teamId];
}
return self;
}
- (void) setupWithTeamId:(NSString *)teamId
{
__weak FrFTeam *blockSelf = self;
PFQuery *query = [PFQuery queryWithClassName:#"teams"];
[query getObjectInBackgroundWithId:teamId block:^(PFObject *object, NSError *error) {
NSString *name = [object objectForKey:#"teamName"];
NSLog(#"Received name: %# from object: %#", name, object);
[blockSelf setName:name];
}];
}
Then, change the name of the method from initWithName:... because this isn't really an init method because you have already done the init before calling setupWithTeamId:.
If you need the parse bit to be done before the init method returns, you should:
Call parse to get the details before calling init on the object
Use getObjectWithId: --- not recommended as this blocks the thread in init, bad idea

Pretty sure the reason is in the method name you are calling -getObjectInBackgroundWithId:block: (it specifies InBackground, which suggests the block is called at some later stage and not immediately)
This would suggest that you end up with self == nil (as you are not calling any other initialiser in the method.
Initialisation of an object has to be synchronous.

Related

How to update a property of a ViewController from data retrieved asynchronously?

I would like to update a property of my ViewController self.matchedUsers, which takes data from a query that I run asynchronously through a block.
Then somewhere later When I retrieve the count via [self.matchedUsers count], I still get 0, despite knowing that multiple objects was added to my property. My question is, how do I ensure that my property gets updated even when I am retrieving data asynchronously through a block? Thanks!
Update:
For context, here is the block:
//Way earlier in an initializer:
self.matchedUsers = [[NSMutableArray alloc] init];
//In a method much later
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error){
//Code that updates self.matchedUsers with the NSArray objects
dispatch_async(dispatch_get_main_queue(), ^{
[self.matchedUsers addObjectsFromArray: objects];
});
//Question relates to ensure how property could be updated
}
}];
This should work provided you didn't forget to initialize matchedUsers, you check for its value after it's been changed and array does not lose its elements between the time you schedule and execute the block.
However, I would prefer to write a method that can be called from any thread, say
- (void)addUser ...
#synchronized(self.usersToAdd) {
[self.usersToAdd addObjectsFromArray: array];
Enqueue on main thread {
NSArray *addingNow;
#synchronized(self.usersToAdd) {
addingNow = [self.usersToAdd copy];
[self.usersToAdd removeObjects...
}
if (addingNow.count) {
[self.users addObjectsFromArray: addingNow;
[self.tableView insertRowsWithIndexPaths...
}
}
}
}
As others have written the problem could be missing initialization of matchedUsers but...
...the problem could also be due to your main thread being blocked. You write that you "somewhere later retrieve the count". If that is within the same method as the one that made the first dispatch you will be in trouble. Consider this code
NSMutableArray *collection = [[NSMutableArray alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSArray *array = [[NSArray alloc] initWithObjects:#"1", nil];
dispatch_async(dispatch_get_main_queue(), ^{
for (NSString *item in array){
[collection addObject:item];
}
NSLog(#"A");
});
});
[NSThread sleepForTimeInterval:5];
NSLog(#"B");
If this is running on the main thread it will output first B on then A (no matter the sleep time), because the block is not run until the method finishes executing. If you on the other hand dispatch to another global queue instead of the main queue it will be A and then B.

ARC capturing self... block inside a block and reference being released before execution

I have this problem: a block inside a block.
self.createStuff = ^ (NSString *text) {
self.post.onCompletion = ^(NSURLResponse *response, NSData *data, NSError *error){
[self doStuff]; // error here
};
[self doMoreStuff]; // error here
};
I will have errors in [self doStuff] and on [self doMoreStuff]. The error is capturing 'self' strongly in this block is likely to lead to a retain cycle
Easy you say, just add
id mySelf = self;
before the first block and use mySelf instead.
Nope. This will not save my problem, simply because mySelf being of kind id will not give me a post property, needed by the second line. So I need to declare it like
MyClass *mySelf = self;
Making it like:
MyClass *mySelf = self;
self.createStuff = ^ (NSString *text) {
mySelf.post.onCompletion = ^(NSURLResponse *response, NSData *data, NSError *error){
[self doStuff]; // error here
};
[mySelf doMoreStuff];
};
OK, you say, now the self.post.onCompletion line and doMoreStuff are not complaining anymore, but we have another self inside onCompletion... because this is a block inside a block. I can repeat the process creating another weak reference like and this will have to be a weak reference to a weak reference
MyClass *internalMyself = mySelf;
and use
[internalMyself doStuff];
this seems to me to be a pretty pathetic way to do this and more, the app hangs when this method runs. Something like the reference is being deallocated before the method executes...
How do I solve this charade?
thanks.
note: this is being compiled to iOS 6+
You're pretty close. Just replace your solution
MyClass *mySelf = self;
self.createStuff = ^ (NSString *text) {
mySelf.post.onCompletion = ^(NSURLResponse *response, NSData *data, NSError *error) {
[self doStuff]; // error here
};
[mySelf doMoreStuff];
};
with
__weak MyClass *mySelf = self;
self.createStuff = ^ (NSString *text) {
mySelf.post.onCompletion = ^(NSURLResponse *response, NSData *data, NSError *error) {
[self doStuff]; // error here
};
[mySelf doMoreStuff];
};
The problem with the first solution is that mySelf isn't designated weak, so it's ownership qualifier is implicitly __strong (see LLVM's documentation). I'm not sure why this quiets the warning in the first block, but designating the reference __weak will fully remove the retain cycle.

Pass argument to a block?

I have a singleton that I'm using to parse XML and then cache it. The parsing/caching is done with a block. Is there any way for me to pass an argument to this block from another class so that I can change the URL from outside the singleton?
Here's the code I have now:
// The singleton
+ (FeedStore *)sharedStore
{
static FeedStore *feedStore = nil;
if(!feedStore)
feedStore = [[FeedStore alloc] init];
return feedStore;
}
- (RSSChannel *)fetchRSSFeedWithCompletion:(void (^)(RSSChannel *obj, NSError *err))block
{
NSURL *url = [NSURL URLWithString:#"http://www.test.com/test.xml"];
...
return cachedChannel;
}
And here's the class where I need to modify the NSURL from:
- (void)fetchEntries
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// Initiate the request...
channel = [[BNRFeedStore sharedStore] fetchRSSFeedWithCompletion:
^(RSSChannel *obj, NSError *err) {
...
}
}
How do I pass an argument from fetchEntries to fetchRSSFeedWithCompletion?
You would want to add a parameter in the method, not the block.
Also, when using a completion block, there really is no reason to return anything in the method.
I'd change it to look like this:
-(void)fetchRSSFeed:(NSURL *)rssURL completion:(void (^)(RSSChannel *obj, NSError *error))block{
RSSChannel *cachedChannel = nil;
NSError *error = nil;
// Do the xml work that either gets you a RSSChannel or an error
// run the completion block at the end rather than returning anything
completion(cachedChannel, error);
}

why is "error:&error" used here (objective-c)

why is "error:&error" used here (objective-c)
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
wouldn't an object in objective-c be effectively pass-by-reference anyway?
The argument type for error: is NSError** (i.e. a pointer to a pointer to an object). This permits the moc object to allocate and initialize a new NSError object as required. It is a common pattern, especially in Cocoa.
The NSError documentation gives some indication of the motivation for this approach:
Applications may choose to create subclasses of NSError to provide better localized error strings by overriding localizedDescription.
Passing in an NSError** argument allows that method to return any subclass of NSError that makes sense. If you passed in NSError*, you would have to supply an existing NSError object, and there would be no way for the method to return a different object from the one you passed in.
To be clear, the method could look something like this:
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError**)error {
...
if ((error != NULL) && (some_error_condition)) {
*error = [[[SomeNSErrorSubclass alloc] init...] autorelease];
return nil;
}
}
Note that this also allows the calling code to ignore errors by simply passing in NULL for the error: parameter, as follows:
NSArray *array = [moc executeFetchRequest:request error:NULL];
Update: (in response to questions):
There are two reasons why the argument type has to be NSError** instead of NSError*: 1. variable scoping rules, and 2. NSError instances are imutable.
Reason #1: variable scoping rules
Let's assume that the function declaration were to look like this:
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error;
And we were to call the function like this:
NSError * error = nil;
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }
When you pass in a variable this way, the function body will not be able to modify the value of that variable (i.e. the function body will not be able to create a new variable to replace the existing one). For example, the following variable assignments will exist only in the local scope of the function. The calling code will still see error == nil.
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
...
error = [[[NSError alloc] init...] autorelease]; // local only
error = [[[SomeNSErrorSubclass alloc] init...] autorelease]; // local only
}
Reason #2: instances of NSError are immutable
Let's keep the same function declaration, but call the function like this:
NSError * error = [[[NSError alloc] init...] autorelease];
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }
First of all, the variable scoping rules guarantee that error can not be nil, so the if (error != nil) { ... condition will always be true, but even if you wanted to check for specific error information inside the if block, you would be out of luck because instances of NSError are immutable. This means that once they are created, you cannot modify their properties, so the function would not be able to change the domain or userInfo of that NSError instance that you created in the calling code.
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
...
error.domain = ... // not allowed!
error.userInfo = ... // not allowed!
}
It's effectively another return value. The error is not dominant by convention in Cocoa when there is a return value for the operation. When an error is encountered, it may be returned to you by this out parameter.
In the case of NSError, it works this way because NSError is not a mutable type - its fields are set at initialization and never mutated. Therefore, you cannot pass an NSError as usual and set the error code.

iphone - performSelectorOnMainThread with return value

I have the following method:
- (NSMutableArray *)getElements:(NSString *)theURL;
And I wanted to know if there is a way to call that method using performSelectorOnMainThread so that I can get the return value. So far, I've tried with:
myArray = [object performSelectorOnMainThread:#selector(getElements:)
withObject:url waitUntilDone:YES];
but it doesn't work, since performSelectorOnMainThread returns void. How could I solve this?
Welcome to a multi-threaded environment my friend.
You'll need to store the return value in an instance variable or pass in an object by reference through the withObject parameter:
NSMutableDictionary *myDict;
[object performSelectorOnMainThread:#selector(getElements:)
withObject:&myDict waitUntilDone:YES];
Your method prototype should now look like this:
- (void)getElements:(NSMutableDictionary **)objects;
You can't do it directly, because, as you say, that method returns void.
So, you'd have to arrange another way to get a value back, for example by passing an NSDictionary instead of an NSString, and having the method store the result in the dictionary for you.
Had to implement this recently. Great candidate for adding a category to NSObject so that all your objects can do this:
#implementation NSObject (CallSelectorWithObjectOnMainThread)
- (id)resultFromSelectorOnMainThread:(SEL)selector object:(id)object {
NSMutableDictionary *resultDictionary = [NSMutableDictionary dictionaryWithCapacity:1];
NSMutableDictionary *callDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:resultDictionary, #"ResultDictionary", NSStringFromSelector(selector), #"Selector", nil];
if(object) [callDict setValue:object forKey:#"Object"];
[self performSelectorOnMainThread:#selector(callObject:) withObject:callDict waitUntilDone:YES];
return [resultDictionary objectForKey:#"Result"];
}
- (void)callObject:(NSMutableDictionary *)info {
id result;
SEL selector = NSSelectorFromString([info objectForKey:#"Selector"]);
id object = [info objectForKey:#"Object"];
NSMutableDictionary *resultDictionary = [info objectForKey:#"Dictionary"];
if(object)
result = [self performSelector:selector withObject:object];
else
result = [self performSelector:selector];
if(result)
[resultDictionary setValue:result forKey:#"Result"];
}
#end
I have universal solution for async blocking call with return value for any thread, not only main. This example for main thread like performSelectorOnMainThread:withObject:waitUntilDone:
__block id result = nil;
NSOperationQueue* targetQueue = [NSOperationQueue* mainQueue];
// targetQueue is main thread, but it may be queue on any thread.
[targetQueue addBlockOperation:^{
// performs on target thread.
result = [someObject someSelector];
}];
// wait until operations on targetQueue will finished.
[targetQueue waitUntilAllOperationsAreFinished];
// result is ready here.
I used this technique only in ARC environment.