简体   繁体   中英

performSelector may cause a leak because its selector is unknown IN Singleton Class/ FUNCTION Pointer -Passing Function as parameter

@interface URLClass : NSObject
{
    id target;
    SEL funObj;
}
+ (URLClass *)sharedInstance;
-(void)theBigFunction:(SEL)func :(id)target;
@property (nonatomic,retain) SEL funObj;

#import "URLClass.h"

static URLClass *instance = NULL;


@implementation URLClass
{
    NSMutableData *webData;
}
- (id)init
{
    if ( self = [super init] )
    {

    }
    return self;    
}

+ (URLClass *)sharedInstance
{
    @synchronized([URLClass class])
    {
        if (!instance)
            instance = [[super alloc] init];        
        return instance;
    }    
    return nil;
}
-(void)theBigFunction:(SEL)func :(id)targetObj{

    funObj =func;
    target=targetObj;  
    NSURL *URL = [NSURL URLWithString:@"urlString"];    
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];

    if( connection )
    {
        webData = [NSMutableData data] ;
    }
    else
    {
        NSLog(@"theConnection is NULL");
    }
}



-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{

    return YES;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{    
    NSLog(@"ERROR with theConenction");
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{    

    NSError *error;
    id jsonObj = [NSJSONSerialization JSONObjectWithData:webData options:0 error:&error];    
    if (jsonObj!=nil && error==nil) {        
        if ([jsonObj isKindOfClass:[NSDictionary class]]) {            
            NSDictionary *dic=(NSDictionary*)jsonObj;
            NSLog(@"DIC jsonObj %@ ",dic);
            NSArray *array=[dic objectForKey:@"items"];
            NSLog(@"array jsonObj %@  %d",array,[array count]);
        }else if ([jsonObj isKindOfClass:[NSArray class]]) {

            NSArray *arr=(NSArray*)jsonObj;
            NSLog(@"arr jsonObj %@ ",arr);
        }
    }
    [target performSelector:funObj];
// Not gEtting called the aboue line

//performSelector may cause a leak because its selector is unknown warning in above line }

When am planing to execute below lines form code from any class . its not get called.

-(void)loginPress:(id)sender{
    URLClass *rlOBJ=[URLClass sharedInstance];   
    [rlOBJ theBigFunction:@selector(upd_Handler:) :self];
}
- (void) upd_Handler: (id) value{

    NSLog( @"Seccess");    
}

The modern approach would be for your class to accept a completion block instead of a target / selector. Then you don't have to insert a bunch of ugly compiler-inhibiting lines, and you get more flexibility.

Here are complete replacements of [target performSelector:selector withObject:object]; to avoid the warning. Use either of the replacements like this:

[NSObject target:target performSelector:selector withObject:object];

@interface NSObject (NSPerformSelector)

+ (id)target:(id)target performSelector:(SEL)selector;
+ (id)target:(id)target performSelector:(SEL)selector withObject:(id)object;
+ (id)target:(id)target performSelector:(SEL)selector withObject:(id)object1 withObject2:(id)object2;

@end

@implementation NSObject (NSPerformSelector)

+ (id)target:(id)target performSelector:(SEL)selector {

    IMP imp = [target methodForSelector:selector];
    id (*func)(id, SEL) = (void *)imp;
    return func(target, selector);
}

+ (id)target:(id)target performSelector:(SEL)selector withObject:(id)object {

    IMP imp = [target methodForSelector:selector];
    id (*func)(id, SEL, id) = (void *)imp;
    return func(target, selector, object);
}

+ (id)target:(id)target performSelector:(SEL)selector withObject:(id)object1 withObject2:(id)object2 {

    IMP imp = [target methodForSelector:selector];
    id (*func)(id, SEL, id, id) = (void *)imp;
    return func(target, selector, object1, object2);
}

@end

Simply use [sender performSelector:selector withObject:object afterDelay:0]; This will remove the warning and the code will run fine.

That is a warning, not an error. Your code should still work.

When you invoke a selector and the compiler can't tell what the selector is, it can't tell if the called method is going to take ownership of the passed object or not, or release it, or whatever. Thus ARC can't be sure that memory management of the parameter will be handled correctly.

You should be able to enclosed the performSelector call in a compiler directive to disable that warning. Then the burden will be on you to make sure the called method does not keep any strong references to the object passed to it, or release the object.

You don't need to pass your success method in your bigFunction. Instead you should be using a completion block, as suggested by [Wil Shipley][1]

Here is an example of a NSURLConnection making a Asynchronous request and returning; a response, data and an error; if one occurs. In the future you should refer to the documentation.

NSURLConnection Class Reference https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/index.html

+ (void)sendAsynchronousRequest:(NSURLRequest *)request
                      queue:(NSOperationQueue *)queue
          completionHandler:(void (^)(NSURLResponse *response,
                                      NSData *data,
                                      NSError *connectionError))handler

You could re-write your bigFunction method like this --

- (void)bigFunctionWithYourInputData:(NSDictionary*)userInfo withCompletionHandler:(void(^)(NSData* data, NSError* error))completionHandler {
    NSURL *theUrl = [NSURL URLWithString:[userInfo objectForKey:@"url"];
    NSURLRequest *req = [NSURLRequest requestWithURL:theUrl];
   [NSURLConnection sendAsynchronousRequest:req queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

        if (!connectionError) {
            //do what you want with your data
            NSLog(@"response :%@", response);
            NSLog(@"data     :%@", data);

            //pass to your completion handler
            if(completionHandler) {
                completionHandler(data, nil);
            }
        } else {
            //something went wrong
            NSLog(@"Got the error with code :%ld", (long)connectionError.code);

            //pass to your completion handler
            if(completionHandler) {
                completionHandler(nil, error);
            }
        }
    }];
}

Then you would implement it somewhere else, via your singleton like so --

[URLClass sharedInstance] bigFunctionWithYourInputData:_someDictionaryData withCompletionHandler:^(NSData* data, NSError* error) {

     if (!error) {
         //success
         NSLog(@"returned the data:%@", data);

     } else {

         //handler the error somehow
     }
 }];  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM