pass parameter in objective-c block - objective-c

My program:
- (void)getPostPraiseListWithSessionId:(NSString *)sid withPostId:(NSString *)postId withPageNo:(int)pageNo completeBlock:(void (^)(NSError *))complete{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{
#"sid":sid,
#"post_id":postId,
#"pageNo":[[NSNumber alloc] initWithInt:pageNo],
};
[manager POST:[NSString stringWithFormat:#"%#%#",K_BASE_URL,K_GET_POST_PRAISE_LIST_URL] parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
if([[responseObject objectForKey:#"state"] intValue] == 0){
NSError *error = [NSError new];
NSArray *postPraiseList = [MTLJSONAdapter modelOfClass:MSMPostPraiseInfo.class fromJSONDictionary:[responseObject objectForKey:#"data"] error:&error];
complete(error);
}else{
NSError *error = [[NSError alloc] initWithDomain:MSMNETWORK_ERROR_DOMAIN code:[[responseObject objectForKey:#"state"] intValue] userInfo:#{#"message":[responseObject objectForKey:#"message"]}];
complete(error);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
complete(error);
}];
}
I have some questions.I wanna pass one parameter to the block named completeBlock,but I don't know what type of the parameter should I use.Should I use the weak type or the strong type?Whatever the type is weak or strong,please tell me the reason.

Rob explained it a lot better, but long story short you use weak when you don't care much about the object being valid, allowing it to be dealloced before your block use it. strong should be used when the object must be valid (and in memory) to be used inside your block.
Also, here are some minor improvements you could do (which not really related to your question):
[[NSNumber alloc] initWithInt:pageNo] can be replaced by a simple #(pageNo)
If K_BASE_URL and K_GET_POST_PRAISE_LIST_URL are declared in #define as NSString* macros, you don't need to use stringWithFormat:, you can simply use [manager POST: K_BASE_URL K_GET_POST_PRAISE_LIST_URL parameters:(...)]
In this case, your NSError *error = [NSError new]; would probably better off as NSError *error = nil;, but it depends on what that method of MTLJSONAdapter does.

Frequently you will use a weak reference because you often do not want the request to maintain a strong reference to the object in question. For example, if you're updating a view object in your user interface, if the view is dismissed before the request finishes, there's no benefit in having the network request keeping a strong reference to a user interface control that is no longer visible.
Sometimes, when dismissing a view controller, it is useful to cancel any pending network requests that are only being used for the benefit of that scene. In that situation, you would want to save the reference to the AFHTTPRequestOperation object returned by POST method and then cancel that request in dealloc of the view controller. But that only works if you explicitly avoid having the blocks maintain a strong reference back to the view controller, i.e. you used weak references.
In your case, you would appear to be posting data, so maybe canceling the request isn't necessary, so this latter point might not be relevant. But, nonetheless, you may well still use weak references if you're only referring to UI controls that may be dismissed while the request is running. But if you're calling some additional instance methods that are updating a persistent store with the result of the request upon completion of the request, then you might need strong reference.
Bottom line, the basic question is whether it is critical for the object in question to be retained for the duration of the network request (i.e. the completion block would have a strong reference) or not. Generally you don't need that, and would thereby favor weak reference. But we can't speak to your specific case without more information about what you plan on doing within the closure you pass to getPostPraiseListWithSessionId method. The choice of strong or weak is not a function of the getPostPraiseListWithSessionId method, but rather a question of what you plan on doing inside the completion block you supply to this method.

Related

Understanding 'potential null dereference' when dealing with NSError

I understand that this may seems a very basic question, but I always thought that explaining (and understanding) basic question is the key to build clean and working code.
I have the following fragment of code in a CoreData entity:
-(BOOL)validateForInsert:(NSError *__autoreleasing *)error {
[super validateForInsert:error];
return [self validateInternal:error];
}
-(BOOL)validateInternal:(NSError *__autoreleasing *)error {
// checking attributes and build userInfo error
if(anyErrors) {
*error = [NSError errorWithDomain:appDomain code:200 userInfo:details];
return NO;
}
return YES;
}
by running 'analyze' in Xcode I get the famous 'potential null dereference' when creating error. While the compiler is not giving any alert, and the code has always worked.
This could be a question too, why the compiler doesn't warn me about that ?
But coming to the code, I perfectly know the solution, which is to check if (error != nil), but I am quite lost at this.
The question start at the beginning, at the meaning of NSError *_autoreleasing* , why the surrounding star ? Which should be a pointer to a pointer ?
Supposing I would like to call the validateForInsert: by myself, how can I build an *_autoreleasing* object ?
The following question, I suppose is related to the question above: if I am building the *error from scratch, why am I suppose to check if nil first ?
Last but not least, the code works fine, the errors get intercepted, could you please spot a case that can have it fail or crash ? As I said I am using CoreData, a generic example would be fine, but another related to CoreData is appreciated.
thanks
*error = [NSError errorWithDomain:appDomain code:200 userInfo:details];
is syntactically correct, therefore the compiler does not warn. The Analyzer detects that this will crash at runtime if error = NULL, i.e. if you call
[myObj validateForInsert:NULL];
If you call
NSError *error;
[myObj validateForInsert:&error];
then you pass the address of error, therefore you pass a variable of type NSError * *.
The __autoreleasing modifier is a hint to the ARC compiler that the object assigned to error is an autoreleased object.
You can declare a variable to be autoreleasing with
NSError * __autoreleasing error;
[myObj validateForInsert:&error];
but normally you do not care. ARC just generates the correct code.
For details, read the Transitioning to ARC Release Notes. There are also lots of answers on this topic on stackoverflow.

blocks and async callback, dealloc object - need to nil the block

There is a similar question here, which doesn't explain exactly what I want: Objective C Blocks as Async-callbacks & BAD ACCESS
I have a view controller, which calls a service with an async callback. The callback is done using a block, which references variables on the view controller to populate them.
It looks like so:
- (void) loadData {
__block MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
me.data = result;
}
}];
}
However, if I dealloc the view controller, 'me' is then badly accessed by the callback.
What is the simplest way of making 'me' NULL? If i put it as an iVar, it then brings back the circular reference... i think?
I think I'm missing something obvious....
Thanks
Are you targeting iOS 5.0 or later (or Mac OS X 10.7 or later)? If so, you can use ARC and a __weak variable (instead of a __block one). This will automatically zero out when the referenced object is deallocated. Your code would look like
- (void)loadData {
__weak MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
MyViewController *strongMe = me; // load __weak var into strong
if (strongMe) {
strongMe.data = result;
}
}
}];
}
If you need support for an older OS then you need to find a different solution. One solution is to just go ahead and let the block retain self. If the service is guaranteed to execute the completion block (and then release it), this will only produce a temporary cycle that will break automatically when the completion block is run. Alternatively if you have some way to cancel the service (in a way that guarantees the block cannot be called after the cancellation), you can stick with the __block and just be sure to cancel the service in your -dealloc. There's other alternatives too but they're more complicated.
I did a combination of things above from the suggestions. Including nilling the blocks. Although, my objects are still not getting released immediately. i.e. I'd put a breakpoint on dealloc of MyViewController, and without the __block variable it would get called at a much later point in time (probably due to the async connection) and sometimes not at all.
The code is fairly complex - so I imagine there are other things going on for it to not work as suggested above.
What I have also done, is used Mike Ash's MAZeroingWeakRef, which i guess is the same as using __weak - which #KevinBallard suggested.
Below is how I've implemented it, and it appears to be working. Dealloc is called immediately on disposal of the view controller, which i want. And I can't get it to crash... and with the log comment that i've put in, I can already see that I'm dodging bullets.
- (void) loadData {
__block MAZeroingWeakRef *zeroWeakRef = [[MAZeroingWeakRef alloc] initWithTarget:self];
[zeroWeakRef setCleanupBlock: ^(id target) {
[zeroWeakRef autorelease];
}];
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
MyViewController *me = [zeroWeakRef target];
if (!me) {
DULog(#"dodged a bullet");
}
if (!error) {
me.data = result;
}
}];
}
Is there a real retain cycle problem that you're trying to avoid? Is there a reason that self should not simply be retained until -executeWithCompletion: completes? Is there any real chance that it won't complete?
So long as it really will eventually complete (even with failure) and so long as it releases the block after invoking it (perhaps by setting a property to nil), then the retain cycle will eventually be broken and all will be well.

Objective-C: Using a block as a completion handler

I'm using CLGeocoder, and I'm using a block as a completion handler. I'm unsure of the retain/release cycle for the instance of CLGeocoder that I create.
Here's the basic code:
CLGeocoder* geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:newLocation completionHandler:
^(NSArray* placemarks, NSError* error)
{
// process the placemarks...
[geocoder autorelease];
}
];
Is autoreleasing the geocoder as the last line of the block the recommended way to handle this? Any suggestions are appreciated!
You can just release it (no need for auto release) Auto release is for when you are unsure when you will need to release an object (such as returning an object at the end of a method or for convenience methods)
In this case you are certain that you are done using the object so it may be released. Of course, autorelease works too, but lingers around in memory longer.

Concept behind NSError

I have been using NSError in a few places but I don't really grasp the concept behind it. Especially why it is used using a double pointer like this:
NSError *err = nil;
NSString *s = nil;
s = [NSString stringWithContentsOfURL:url error:&err];
So why can't we just pass the variable errto the method, which is passed as "by-reference" anyway as far as I can tell.
Thanks for some clarification
This is one of the best descriptions around about NSError.
Actually your are passing not an object but a pointer to a pointer. With this, you can set your NSError before you send a message to an object to nil and only if an error occurs, your NSError will "suddenly" hold an value so that you can do stuff like this:
NSError *err = nil;
[SomeClass doSomethingGreatWhichCanFailWithError:&err];
if(err) {
NSLog("Something failed :-(");
//TODO: Add better logging!
}
Otherwise you would have to create an instance of NSError before you send the message and than check for a property or something else if an error occured.
Passing an object by reference would require you to allocate and initialize an NSError object that can then be manipulated by the called method to suit the needs. This also requires to have all funcitonality either directly in NSError or require the caller to know of and use specific subclasses of NSError.
Using this method the caller does not have to create an NSError object at all. In most cases no error should occur and there is no creation of useless error objects. Additionally any class could feature a subclass of NSError to give further information. If the caller does not care about this, it interprets the returned object as simple NSError and everything is fine. If the caller is interested in details, it can cast to the subclass and get further information.

Retain/Release through intermediary method

I think I understand retain/release in objective-C for the most part. However, I have a specific case I am unsure about. Here is an example:
+ (NSString *)getPlayerNameByIndex:(NSInteger)globalIndex:(ABAddressBookRef)addressBook
{
...
Player *player = [PlayerHelper loadPlayer:globalIndex];
NSString *name = [PlayerHelper getPlayerName:player :addressBook];
[player release];
// 'retain' here?
return name;
}
+ (NSString *)getPlayerName:(Player *)player:(ABAddressBookRef)addressBook
{
...
NSString *name = [[[NSString alloc] initWithString:player.nickname] autorelease];
return name;
}
So then I call...
NSString *name = [PlayerHelper getPlayerNameByIndex:index:addressBook];
// name is 'autorelease'?
What I saw on random occasions is that the view sometimes shows the 'name' field as empty when it populates the table after coming back from another view. This could be another issue but I want to be sure of my use of 'autorelease'.
The core of my question is the use of 'autorelease' in getPlayerName. Does the 'autorelease' state of being get passed through method getPlayerNameByIndex to the caller?
Or, do I have to call 'retain' in the intermediary method? I am thinking 'autorelease' may be releasing in method getPlayerNameByIndex.
Hopefully my question is clear. Any help is appreciated.
Update: Some more info for clarification...
NSError *error = nil;
Player *player = nil;
NSArray *array = [appDelegate.managedObjectContext executeFetchRequest:request error:&error];
if ([array count] == 1)
{
player = [array objectAtIndex:0];
[player retain];
}
This is essentially the "loadPlayer" method which loads info from core data. From the answers it sounds like I do not need to call [player retain], since it is an autorelated object, and I can simply return "player" and use it? Thanks for the responses!
The core of my question is the use of 'autorelease' in getPlayerName. Does the 'autorelease' state of being get passed through method getPlayerNameByIndex to the caller?
The answer is yes.
Or, do I have to call 'retain' in the intermediary method?
whether you want to call retain depends on the semantics of your method.
In Obj-C/Cocoa, the following convention applies: a method whose name begins with “alloc” or “new” or contains “copy” will return a retained object; otherwise you can expect to get an autoreleased object, then it is the caller responsibility to retain it according to its needs.
I am thinking 'autorelease' may be releasing in method getPlayerNameByIndex.
autoreleased objects are released at the next point in time when the autorelease pool is drained; this is usually associated to going back to the main loop (though, no details are available about this); so you can be pretty sure that auto-releasing does not kick in in getPlayerNameByIndex...
Hope this helps clarifying the issue...
In getPlayerNameByIndex The line:
[player release];
is wong, remove it. You did not obtain ownership. Ownership is ob gained by calling a method with alloc or the method names starts with new, copy or an explicit retain. (NARC).
You do not need to release player because you did not obtain ownership, see above rule.
In getPlayerName:
can be simplified to:
return player.nickname;
The method name can be simplifies to:
+ (NSString *)getPlayerName:(Player *)player