Objective-C检测是否可以保留参数?

Objective-C检测是否可以保留参数?,objective-c,arguments,retain,Objective C,Arguments,Retain,我正在浏览一个参数列表,尽管在这些参数中,值可以是从NSInteger、Selector到NSObjects的任何内容。但是,如果它是NSObject,则需要正确地保留它。我不能简单地检查类是否与NSObject相同,或者它是否响应retain方法,因为如果在选择器或整数上这样做,它将崩溃。那你怎么还能这么做呢?我不知道 我甚至试着在里面放一个@try@catch,如果没有的话试着保留它可能是一个不需要保留的对象。但它也立即崩溃:这里没有错误例外 如果我可以测试某个参数是否有类,如果找到一个类,

我正在浏览一个参数列表,尽管在这些参数中,值可以是从NSInteger、Selector到NSObjects的任何内容。但是,如果它是NSObject,则需要正确地保留它。我不能简单地检查类是否与NSObject相同,或者它是否响应retain方法,因为如果在选择器或整数上这样做,它将崩溃。那你怎么还能这么做呢?我不知道

我甚至试着在里面放一个@try@catch,如果没有的话试着保留它可能是一个不需要保留的对象。但它也立即崩溃:这里没有错误例外

如果我可以测试某个参数是否有类,如果找到一个类,我可以检查它是否是NSObject类,如果没有找到任何类,它也不应该被保留。我发现:

object_getClass();
但当你超过一个NSInteger时它就会崩溃

看看NSInvocation类,您可以调用retainArguments方法,不幸的是,这也会使应用程序崩溃。但是在setArgument的描述中有一些奇怪的地方:

当参数值是一个对象时,传递一个指向应该从中复制对象的变量或内存的指针

这意味着,如果一个参数是一个对象,就有“东西”可以检测出来,但如何检测呢

编码到现在

- (void)addObserver:(NSObject *)observer selector:(SEL)selector arguments:(id)firstObj, ... {
    // Define signature
    NSMethodSignature *signature  = [[observer class] instanceMethodSignatureForSelector:selector];
    NSInvocation      *invocation = [NSInvocation invocationWithMethodSignature:signature];

    // Prepare invocation
    [invocation setTarget:observer];
    [invocation setSelector:selector];

    id        currentObject;
    va_list   argumentsList;
    NSInteger currentIndex = 2;

    if (firstObj) {
        va_start (argumentsList, firstObj);
        while (currentObject = va_arg(argumentsList, id)) {
            [invocation setArgument:&currentObject atIndex:currentIndex];
            currentIndex++;
        }
        va_end(argumentsList);
    }

    // The observer can easily be retained by doing [observer retain];
    // However the arguments may consist of NSIntegers etc. which really don't like
    // to be retained (logically). So I want to skip the argument that don't need
    // retaining.
}
目标

我正在努力实现以下目标:

我有一个随机方法,比如:

- (void)fetchFruitApples:(NSInteger)amount inRange:(NSRange)range withString:(NSString *)aString {
    //Can I fetch fruit? 
    //If so, execute method. 
    //If not wait for a certain event to occur (without blocking the main thread) 
    //Then retry this method with the arguments passed.
    //Thats why here I want to do [MyObject addObserver:self selector:@selector(fetchFruitApples:inRange:withString:) arguments:amount, range, aString, nil];
}

您可以将数字作为NSNumber对象传递,这样您就可以确保您得到的所有东西都是一个对象,并且会响应[arg retain]和[arg iskindof class:[NSNumber class]]


我不确定这一点,但我要做一个有根据的猜测,没有办法做到这一点。您可能能够区分int和objects,但是区分指向选择器的指针和指向对象的指针?值得怀疑。

您所述的目标使我认为您可能应该探索NSOperationQueue

这就引出了我的下一点:操作依赖性。到目前为止,NSThread没有用于添加依赖项的内置机制。但是,NSOperation具有-voidaddDependency:NSOperation*操作方法,当与NSOperationQueue一起用于依赖项管理时,该方法允许使用一种简单的机制。因此,让我们进入NSOperationQueue


中,您的最佳选择可能只是使用格式字符串,就像您使用[NSString stringWithFormat:]一样。调用者总是知道正确的类型,所以为什么不让它传递这些信息呢

例如,如果将方法签名更改为:

- (void)addObserver:(NSObject *)observer selector:(SEL)selector argumentFormat:(NSString*)format arguments:(id)firstObj, ... {

  // parse format

}

格式类似于:isS,表示第一个参数是整数,第二个是字符串,第三个是选择器,即保留第二个参数,但不保留其他两个参数。

可能是最简单的解决方案:

[myInvocation setRetainArguments:YES];
这将告诉调用实例保留您设置的任何对象参数。所以从你的角度看根本没有工作

如果需要手动执行,则应查看方法-[NSMethodSignature getArgumentTypeAtIndex:]。它将返回一个char*,其编码类型与@encode use的格式相同。可以这样使用:

char* type = [myMethodSignature getArgumentTypeAtIndex:3];
if (strcmp(type, @encode(id)) == 0) {
  // It is an object!
}
最后,我已经完成了您可能需要轻松创建NSInvocation实例的工作,以便用一条语句调用具有任何类型参数的方法。在后台/主线程上(有延迟)和操作队列上

我已经在博客上写过这个主题,完整的源代码可以从这里获得:

还有更多信息:

最后是解决方案

经过一天的努力,我终于找到了解决办法,特别感谢佩洛为我指明了正确的方向。阅读他的博客:

关键是:

const char *type   = [signature getArgumentTypeAtIndex:index];
NSString *dataType = [[[NSString alloc] initWithCString:type] autorelease];
if ([dataType isEqualToString:@"@"]) // The argument is an object!

您接收这些值的类型是什么?无效*?通常,允许这样做的API需要指示类型。我不是C大师,但据我所知,当你收到一个空*它只是一个指向任意内存的指针,你需要通过其他方式知道它是什么。也许发布一些代码?而且,这将是一个使用块的好地方。。。如果你的代码在leopard之前不需要支持iOS 3或任何东西。没错,不过我还是想支持iPad3.2,因为4.2刚刚发布。嘿,肯尼,谢谢你花时间回复。问题是,我不能简单地对它进行编号,因为如果我传递带有参数的NSRange,我将遇到问题。也许我应该以我想要完成的目标来编辑我的问题。NSOperationQueue呢?问题是在线程完成观察并检测到它可以继续之后。它应该回忆起这种方法。但是,当调用performMethodOnMainThread时,它只能传递一些参数,最多两个。那么如果我的方法有更多的参数呢?在我的fetchFruit示例中,它有3个。而我只能在withObject中传递两个:withObject.An NSInvocation是Objective-C消息格式
是静态的,也就是说,它是一个转化为对象的操作。如果在提交交叉变量NSInteger和NSString*时打开retainArguments,它仍然会崩溃,因此,在这一点上,我仍然需要手动保留我传递的变量,而我希望该方法能够检测自身,而不是它应该保留还是不保留。你是在告诉我,你有时会将NSInteger和NSString*传递到同一个参数中吗?如果是这样的话,那完全是错误的。我已经尝试过了,但是如果你使用[NSString stringWithFormat:@%@,someInteger],它会崩溃,导致你不得不使用%d,因为我不知道它是整数还是对象,所以我无法对它做出反应。虽然我注意到,如果一个属性没有类,它就不是一个对象,你怎么能检测出一个属性没有类呢?不,我不是说使用NSString stringWithFormat:,我是指类似的东西。我已经澄清了我的答案。