【问题标题】:performSelector may cause a leak because its selector is unknown IN Singleton Class/ FUNCTION Pointer -Passing Function as parameterperformSelector 可能会导致泄漏,因为它的选择器未知 IN Singleton Class/ FUNCTION Pointer -Passing Function as parameter
【发布时间】:2014-02-21 10:06:00
【问题描述】:
@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 可能会导致泄漏,因为它的选择器在上面的行中是未知警告 }

当我计划从任何类执行下面的代码时。它不会被调用。

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

    NSLog( @"Seccess");    
}

【问题讨论】:

  • 你试过用断点调试它吗?在函数的开头放一些NSLog,记录对象的内存,看是否真的被调用/
  • - (void) upd_Handler: (id) value 函数未回调,我已调试,[target performSelector:funObj];没有被执行。
  • 调用了BigFunction?
  • 是的,它被调用了。我也得到了 JSON 数据。得到 Json 数据后,我想调用 - (void) upd_Handler: (id) value function back

标签: ios objective-c delegates


【解决方案1】:

现代方法是让您的类接受完成块而不是目标/选择器。这样您就不必插入一堆难看的编译器抑制行,并且您获得了更大的灵活性。

【讨论】:

  • 你能举个例子吗,完成 NSURL 委托功能后,我想返回 json 数据/webData。
  • 威尔说实话。 (已投票)。选择器在运行时解析,因此编译器无法检查您的代码在做什么。完成块是一种更清洁的方式。
  • 我有一个用例,我需要存储一些代码,以便稍后通过序列化到 plist 文件来执行。在这种情况下,我无法序列化整个块,但我可以将@selector 序列化为稍后恢复的 NSString,并稍后调用它来完成任务。我想知道是否有更好的方法来做到这一点。
【解决方案2】:

这里是 [target performSelector:selector withObject:object]; 的完整替换,以避免警告。使用以下任一替换:

[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

【讨论】:

    【解决方案3】:

    只需使用[sender performSelector:selector withObject:object afterDelay:0]; 这将删除警告,代码将正常运行。

    【讨论】:

    • 这会给不应异步运行的代码带来问题,因为使用 afterDelay 仍会安排调用而不是执行它。
    【解决方案4】:

    这是一个警告,而不是错误。您的代码应该仍然可以工作。

    当你调用一个选择器并且编译器无法判断选择器是什么时,它无法判断被调用的方法是否会获得传递对象的所有权,或者释放它,或者其他什么。因此 ARC 不能确定参数的内存管理是否会被正确处理。

    您应该能够将 performSelector 调用包含在编译器指令中以禁用该警告。然后,您将有责任确保被调用的方法不会保留对传递给它的对象的任何强引用,或者释放该对象。

    【讨论】:

      【解决方案5】:

      你不需要在你的 bigFunction 中传递你的成功方法。相反,您应该按照 [Wil Shipley][1] 的建议使用完成块

      这是一个 NSURLConnection 发出异步请求并返回的示例;响应、数据和错误;如果发生。将来您应该参考文档。

      NSURLConnection 类参考 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
      

      你可以像这样重写你的 bigFunction 方法 --

      - (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);
                  }
              }
          }];
      }
      

      然后你可以通过你的单例在其他地方实现它——

      [URLClass sharedInstance] bigFunctionWithYourInputData:_someDictionaryData withCompletionHandler:^(NSData* data, NSError* error) {
      
           if (!error) {
               //success
               NSLog(@"returned the data:%@", data);
      
           } else {
      
               //handler the error somehow
           }
       }];  
      

      【讨论】:

        猜你喜欢
        • 2011-10-24
        • 2014-01-07
        • 2013-08-04
        • 2022-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多