Confusion over how I should use weakSelf in blocks - ios

I have a lot of blocks in my code. I have a process for initialising a user upon login, I am using Parse.com as my backend:
PFQuery *messageBankQuery = [PFQuery queryWithClassName:#"messageBank"];
[messageBankQuery whereKey:#"username" equalTo:[PFUser currentUser].username];
[messageBankQuery getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if(!error){
[self setupUserWithMessageBank:object];
}//end no error if
else{
NSLog:(#"error");
}
}];
The messageBank is a parse object that holds references to all the messages the user has. If that object is found setupUserWithMessageBank is called in the block. setupUserWithMessageBank also does more block work:
-(void)setupUserWithMessageBank: (PFObject *)object{
__weak FriendsViewController *weakSelf = self;
//2.)Init the user
weakSelf.currentUser = [[appUser alloc] initWithParseUser:[PFUser currentUser] andMessageBank:object];
//3.) Setup that message array
[weakSelf.currentUser setupMessagedTodayWithHandler:^(BOOL successful) {
if(successful){
//4.)Add friends to the array
[weakSelf.currentUser populateFriendsArrayWithCompletionHandler:^(BOOL successful, NSError *error, BOOL addSelf, BOOL alreadyFriends) {
if(successful){
[self.indicator stopAnimating];
[self.indicator removeFromSuperview];
[self.tableView reloadData];
__weak FriendsViewController *weakSelf = self;
[weakSelf.currentUser refreshMessagesArrayWithCompletionHandler:^(BOOL successful, BOOL newMessages) {
if(successful) {
//set the button
[self.navigationItem.rightBarButtonItem setAction:#selector(showMessages)];
}
else{
[weakSelf displayGeneralError];
}
}];//end fill messages
}
else{
[weakSelf displayGeneralError];
}
}];//end populate method call
}
else{
[weakSelf displayGeneralError];
}
}];
}
I am a little confused over the use of weakSelf. Is it okay to declare weakSelf inside the start of the setupUserWithMessageBank method? Because his method is called inside another block so technically it's being created inside a block. Do I need to pass weakSelf inside the method instead?
I'm also not completely sure where I should be using weakSelf. Do I need to use it to turn off activity indicators ? Any pointers about my usage of this would be really appreciated. Thanks!

you will probably only need to use a weakSelf if you actually keep a reference to the block within self (or maybe transitively, a block being kept in an object that is kept within self), which in this case, doesnt look like you are doing. the only reason really to use a weakSelf within a block is to avoid retain cycles.
if both blocks have short life cycles then its probably safe to just use self within the block. (if you have any control over the life cycle of the blocks, make sure they are set to nil after executing or cleaned up if they dont get executed because of some failure so they dont hang around)

Related

How to make method wait for a response in order to return a value?

I am sure this has been asked before, but I don't know the keywords to search for it.
Suppose that I have a function validateConnection that returns a bool. This function makes a call to the server (using anetworking) and returns true if the request was successful (this is just a example).
I want my method to wait until the response is back in order to return the bool: how to achieve that?
Any help would be much appreciated!
Its Called Block you can learn more about block by Introduction to Objective-C Blocks
Following is the sample method to create block and its call back:
-(void)callmethodwith:(NSString*)string withCompletion:(void(^)(BOOL success, NSError* error, id responce))completion
{
NSString *str =[NSString stringWithFormat:#"MY FUNTn CALLBACK %#",string];
if (completion){
dispatch_async(dispatch_get_main_queue(), ^{
completion(YES,nil,str); // here that call when method complete
});
}
}
And you can call it like following:
[self callmethodwith:#"My new" withCompletion:^(BOOL success, NSError *error, id responce) {
if(success)
{
NSLog(#"==%#",responce); // here you get response once method camplet
}
}];
I think, once the method is executing then there is no way of stopping it.
But you can cancel if it is not fired.
Like following way;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(yourMethod) object:nil];
[self performSelector:#selector(yourMethod) withObject:nil afterDelay:5.0];
You can use AFNetworking success and failure methods to wait for response. Alternatively, you can use performSelectorOnMainThread method
[yourViewController performSelectorOnMainThread:#selector(yourFunction:) withObject:nil
waitUntilDone:YES];
While working with blocks or Completion Handler, make sure you are not using a strong reference of your class or any view, as it affects retain count.
While working with blocks or completion handler use weak reference inside block, something like this,
__weak Class *weakSelf = self;
[manager communicateUsingGETMethod:#"www.google.com" parameterDictionary:#{} success:^(id successDicitoanary) {
// Call using weak reference.
[weakSelf handleSuccess];
} failure:^(NSError *error) {
[weakSelf handleFailure];
}];
and write whole code separately.This will make code more readable and you will get rid of retain cycle too as you are calling your completionCode with weak reference,
For your reference, read apple doc for Avoid Strong Reference Cycles when Capturing self

Query PFUser not working

I am using this query to find users, it works but it just shows me the first user. I want it to show me the user with the text of an UITextField.
How can I do that ?
(I have a textfield and there I type in a name and then it should show the parsed users with the name)
PFQuery *query = [PFUser query];
NSArray *users = [query findObjects];
userQuerys.text = users[0][#"username"];
Thanks very much
This code will fetch you all the PFUsers in which username is equal to the name parameter:
- (void)searchUsersNamed:(NSString *)name withCompletion:(void (^)(NSArray *users))completionBlock {
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:name];
[query findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
// we found users with that username
// run the completion block with the users.
// making sure the completion block exists
if (completionBlock) {
completionBlock(users);
}
} else {
// log details of the failure
NSLog(#"Error: %# %#", error, [error description]);
}
}];
}
An example, if you need to update the UI with the result, for example, a table:
- (void)someMethod {
// we will grab a weak reference of self to perform
// work inside the completion block
__weak ThisViewController *weakSelf = self;
//replace ThisViewController with the correct self class
[self searchUsersNamed:#"Phillipp" withCompletion:^(NSArray *users) {
//perform non-UI related logic here.
//set the found users inside the array used by the
//tableView datasource. again, just an example.
weakSelf.users = users;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//pefrorm any UI updates only
//for example, update a table
[weakSelf.tableView reloadData];
}];
}];
}
A small note: the completionBlock here wont run if there is an error, but it will run even if no users were found, so you gotta treat that (if needed. in this example, it was not needed).
Avoid running non-UI related logic on that mainQueue method, you might lock the Main thread, and that`s bad user experience.

BAD ACCESS after block within a block is called (iOS)

I have a block where I am checking a user's status property from firebase. If the status property is 'free' I want to return from the block, otherwise I want to search for another user and check their status and do so until a 'free' user has been found:
void( ^ myResponseBlock)(BOOL finished) = ^ void(BOOL finished) {
if (finished) {
if ([self.freedom isEqualToString: #"free"]) {
NSLog(#"free!");
return;
} else if ([self.freedom isEqualToString: #"matched"]) {
NSLog(#"get another user");
//get another user
do {
//picking another random user from array
rando = arc4random_uniform(arraycount);
}
while (rando == randomIndex && rando == [self.randString intValue]);
self.randString = [NSString stringWithFormat: #"%u", rando];
[users removeAllObjects];
[users addObject:usersArray[rando]];
self.freeUser = users.firstObject;
NSLog(#"set up another check");
//error is called after this block is called here, again
[self checkIfFree: myResponseBlock];
} else {
NSLog(#"error!");
}
} else {
NSLog(#"not finished the checking yet");
}
};
[self checkIfFree: myResponseBlock];
As shown, I'm getting an error of 'BAD ACCESS' when the block is called for a second time on the 'compblock(YES)' line below:
-(void)checkIfFree:(myCompletion) compblock{
self.freeUserFB = [[Firebase alloc] initWithUrl:[NSString stringWithFormat: #"https://skipchat.firebaseio.com/users/%#", self.freeUser.objectId]];
[self.freeUserFB observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot)
{
self.otherStatus = snapshot.value[#"status"];
NSLog(#"snapshot info %#", snapshot.value);
if ([self.otherStatus isEqualToString:#"free"]) {
self.userIsFree = YES;
self.freedom = #"free";
NSLog(#"user is free in the check method %#", self.freedom);
}
else{
self.userIsFree = NO;
self.freedom = #"matched";
NSLog(#"user is matched in the check method %#", self.freedom);
}
compblock(YES);
}];
}
Everything is fine if the block does not have to be recalled and the first user that's checked is already 'free'. I'm stuck as to why I'm getting this error/crash and wondering how I can solve it!
Thanks!
A block captures all variables passed in including itself, however the variable myResponseBlock has not been initialized yet inside the block. Because of this, you are calling checkIfFree method with a nil value which in turn causing app to crash.
One thing you can do to overcome this would be declaring your block as a __block variable.
__block __weak void(^weakResponseBlock)(BOOL);
void(^myResponseBlock)(BOOL);
weakResponseBlock = myResponseBlock = ^void(BOOL finished) {
...
if (weakResponseBlock) {
[self checkIfFree:weakResponseBlock];
}
}
Additionally, please note that blocks retain all variables passed into them. In this case, you are retaining self inside the block, so it will never get deallocated as long as block executes. So unless required otherwise, always pass a weak reference to blocks.
__weak UIViewController *weakSelf = self;
weakResponseBlock = myResponseBlock = ^void(BOOL finished) {
...
if (weakResponseBlock) {
[weakSelf checkIfFree:weakResponseBlock];
}
}
I think still you might have an error because all your blocks are being created on the stack. So if anything async should happen the myResponseBlock will go away.
What I'd recommend is your copy (using the a property with copy attribute) your myResponse block into a property and reuse it from there. That way your block lives on the heap and goes away when self is set to nil.

Weak and strong self usage, blocks memory management

There are a bunch of questions about all these weak and strong selves but I want that you guys took a look at my particular example:
- (void)getItemsWithCompletionHandler:(void (^)(NSArray*items))completionHandler {
__weak __typeof__(self) weakSelf = self;
[self doWorkWithCompletionHandler:^(Response *response) {
// this completion is not on main thread
dispatch_async(dispatch_get_main_queue(), ^{
...
[weakSelf doAnotherWorkWithCompletionHandler:^(Response *response) {
// this completions is not on main thread either
dispatch_async(dispatch_get_main_queue(), ^{
__typeof__(self) strongSelf = weakSelf;
NSArray *itemsIds = [strongSelf doWorkOnMainThread1];
NSArray *items = [strongSelf doWorkOnMainThread2];
completionHandler(items);
});
}];
});
}];
}
Is everything correct here or not? Also you are welcome to suggest a refactoring
If you turn all warnings on, then you will get a warning for
[weakSelf doAnotherWorkWithCompletionHandler... ];
You shouldn't send messages to weak objects. A weak object could disappear while the method that is called is running. Store the weak object into a strong one, the result is either nil or not. If you then call
[strongSelf doAnotherWorkWithCompletionHandler... ];
you know that either strongSelf == nil and nothing happens, or strongSelf is and stays not nil while the method is executing.
You should check if completionHandler is not NULL and then call it.
if (completionHandler) {
completionHandler(items);
}
Otherwise you'll crash if completionHandler is NULL
You may also reconsider if you want to call completionHandler(items) at all if self == nil at any point. I say that, because there is a slight inconsistency.
In line
[weakSelf doAnotherWorkWithCompletionHandler:^(Response *response) {
if weakSelf is nil already, then it's completionHandler will not be called and in result completionHandler(items) will also not be called.
But in here:
__typeof__(self) strongSelf = weakSelf;
NSArray *itemsIds = [strongSelf doWorkOnMainThread1];
NSArray *items = [strongSelf doWorkOnMainThread2];
completionHandler(items);
if self == nil then completionHandler will in fact be called.
Of course I don't see the whole picture and maybe it's completely irrelevant, but something you might want to take under consideration.
Going further, I assume you'd rather have completionHandler called in each and every scenario, even if self == nil at any point. Or add an error parameter to it in case of anything going wrong.
And if you want to be super pedantic, you might want to put __weak after the type, as such:
__typeof__(self) __weak weakSelf = self;
It's the preferred way: https://developer.apple.com/library/ios/releasenotes/objectivec/rn-transitioningtoarc/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW4 (look for "You should decorate variables correctly.")

NSArray Leak inside setCompletionBlock

Edit to Question: 21/10/2013 20:10 GMT
Added how the method is called below and also the object that is leaking is "allDBObjects" If i remove this and change the dictionary below to "NSMutableDictionary *objectsById" there is no leak.
Edit to Question: 21/10/2013
After a few days not on this issue I have come back to it. I believe this is all down to "The Deallocation Problem" (https://developer.apple.com/library/ios/technotes/tn2109/_index.html#//apple_ref/doc/uid/DTS40010274-CH1-SUBSECTION11). I have tested with both MKNetworkKit and AFNetworking 1.3.3 (Changing 1 method to use AFNetworking instead of MKNetwork Kit) and am still getting these objects leaking in my completion block. I have no references to self within my block and using AFNetworking I can see the completionBlock is set to nil and I have tried to manually break the retain cycle by setting the [weakOp setCompletionBlock:nil].
EDIT: The code sample below I tried to use properties and reference them as weakSelf. I have now changed these to local variables and they still leak.
Any ideas?
Original Question
I have taken over a project using MKNetworkKit and Core Data, after running the project through Leaks in instruments I can see a lot of leaked objects in various places in the app.
After debugging the code I can see the objects that are leaking are 2 fetch requests that are happening in the callback of a MKNetworkKit request (setCompletionBlock:). The fetch requests need to be done to check whether the data needs to be inserted or updated.
Some further information. Inside the completion block I am getting an instance of the ManagedObjectContext and creating it with concurrency type of "NSPrivateQueueConcurrencyType" and to perform the insert I am correctly calling "performBlock:" on the moc.
Please advise.
James
Sample Code of Block:
Please note: I have commented out the 2 fetch requests are there are no leaks and putting them back in causes the leaks of hundreds of objects, also the weakSelf properties I am setting the NSDictionary and NSArray are (nonatomic, strong).
- (void) updateDbObjects: (int) page withCallback: (CompletionResultsNumberBlock) callback {
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
dispatch_queue_t callerQueue = dispatch_get_current_queue();
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
__weak typeof(self) weakSelf = self;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[self createFullPath:urlStr]]];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSManagedObjectContext *moc = [weakSelf managedObjectContextForCurrentThread];
DataRoot *dataRoot = [DataRoot sharedInstanceInMoc:moc];
NSArray *returnJSON = JSON[#"object"];
__block int count = returnJSON.count;
if (!count)
{
dispatch_async(callerQueue, ^{
callback(0);
});
return;
}
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"DBObjects"];
NSError *error;
NSArray *allDBObjects = [moc executeFetchRequest:fetchRequest error:&error];
NSMutableDictionary *objectsById = [NSMutableDictionary dictionaryWithObjects:allTeamsArray forKeys:[allTeamsArray valueForKey: GoalTeamObjectAttributes.teamId]];
for (NSDictionary *rootDict in returnJSON)
{
GoalTeamObject *dbObject = objectsById[rootDict[#"id"]];
if (dbObject == nil)
{
dbObject = [DBObjects insertInManagedObjectContext:dataRoot.managedObjectContext];
}
[weakSelf importStandardParametersFrom:rootDict into:dbObject withPrefix:#""];
}
returnJSON = nil;
objectsById = nil;
[dataRoot saveContext];
NSError *childError = nil;
if ([moc save:&childError]) {
NSError *parentError = nil;
if (![moc.parentContext save:&parentError]) {
NSLog(#"Error saving parent");
}
dispatch_async(callerQueue, ^{
callback(count);
});
} else {
NSLog(#"Error saving child");
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
}];
[operation start];
}
This is how this code is called: It is called recursively in a loop as there are many pages of data.
__block int page = 1;
__weak typeof(self) weakSelf = self;
CompletionResultsNumberBlock loadData;
__block CompletionResultsNumberBlock block_loadData = loadData = ^(int results)
{
if (results < 100)
{
dispatch_async(callerQueue, callback);
} else {
[weakSelf updateDbObjects:++page withCallback:block_loadData];
}
};
[self updateDbObjects:page withCallback: loadData];
This doesn't look right:
__block CompletionResultsNumberBlock block_loadData = loadData = ^...
Under ARC, the block would hold a strong reference to itself. Under ARC, you should do:
__block __weak CompletionResultsNumberBlock block_loadData = loadData = ^...
You use weakSelf in some places within the block, and self in others. That means that the block will still capture self in a strong fashion.
I'd try replacing all references to self with weakSelf within your block first.

Resources