当我尝试将当前代码迁移到ARC时,每当我将NSString作为NSInvocation参数传递时,我都会收到错误.
例:
NSInvocation inv = ...;
NSString *one = @"Hello World!";
[inv setArgument:&one atIndex:2];
Run Code Online (Sandbox Code Playgroud)
当我使用编辑菜单中的重构 - >转换为Objective-C ARC选项时,会发生错误.文本是"NSInvocation的setArgument与不具有__unsafe_retained的所有权的对象一起使用是不安全的."
我怎么能绕过这个?
所以我来自Java世界,我们对内存管理问题一无所知.在大多数情况下,ARC已经救了我的屁股,但这里有些让我难过的东西.基本上我使用NSInvocations来做一些事情,在进行下面的代码修改之前,我遇到了一些讨厌的内存问题.由于我做了这些修改,内存崩溃已经消失,但我通常非常害怕我不理解的代码.我这样做了吗?
之前:各种内存问题:
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:target];
[invocation setArgument:&data atIndex:2];
[invocation setArgument:&arg atIndex:3];
[invocation invoke];
NSString *returnValue;
[invocation getReturnValue:&returnValue];
Run Code Online (Sandbox Code Playgroud)
之后:没有内存问题,但我不确定我是否正确:
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:target];
[invocation setArgument:&data atIndex:2];
[invocation setArgument:&arg atIndex:3];
[invocation invoke];
CFTypeRef result;
[invocation getReturnValue:&result];
if (result)
CFRetain(result);
NSString *returnValue = (__bridge_transfer NSString *)result;
Run Code Online (Sandbox Code Playgroud)
编辑:
我只想根据下面的答案添加,我使用了objc_msgSend,因此:
NSString * returnValue = objc_msgSend(target, selector, data, arg);
Run Code Online (Sandbox Code Playgroud)
它解决了所有内存问题,而且看起来更简单.如果您发现此问题,请发表评论.
NSInvocation-retainArguments当你不NSInvocation立即运行时,它的方法很有用,但是稍后会这样做; 它保留了对象参数,因此它们在此期间保持有效.
众所周知,应该复制块参数而不是保留.我的问题是,是否-retainArguments知道复制而不是在块类型时保留参数?文档并没有表明它确实如此,但它似乎是一件容易和明智的事情.
更新: iOS 7中的行为似乎已经发生了变化.我刚刚对此进行了测试,并且在iOS 6.1和之前,-retainArguments没有复制块类型的参数.在iOS 7及更高版本中,-retainArguments复制块类型的参数.文档-retainArguments已更新,说它复制块,但它没有说明行为何时发生变化(这对支持旧操作系统的人来说真的很危险).
我正在尝试将块参数传递给a NSInvocation,但应用程序崩溃了.调用发出网络请求并调用成功或失败块.我认为问题是在网络请求完成之前会释放块.我设法让它与一些Block_copyhackery 一起工作,它没有使用Instruments报告任何泄漏.
问题: - 即使静态分析仪或仪器没有报告,泄漏是否可能存在? - 有没有更好的方法来"保留"该区块?
// Create the NSInvocation
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
NSInvocation* invoc = [NSInvocation invocationWithMethodSignature:methodSignature];
[invoc setTarget:target];
[invoc setSelector:selector];
// Create success and error blocks.
void (^successBlock)(id successResponse) = ^(id successResponse) {
// Some success code here ...
};
void (^errorBlock)(NSError *error) = ^(NSError *error) {
// Some failure code here ...
};
/*
Without the two Block_copy lines, the block gets dealloced too soon
and the app crashes with …Run Code Online (Sandbox Code Playgroud) 我有一个调用方法的计时器,但这个方法需要一个参数:
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(timer) userInfo:nil repeats:YES];
Run Code Online (Sandbox Code Playgroud)
应该
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(timer:game) userInfo:nil repeats:YES];
Run Code Online (Sandbox Code Playgroud)
现在这种语法似乎不对.我试过NSInvocation但我遇到了一些问题:
timerInvocation = [NSInvocation invocationWithMethodSignature:
[self methodSignatureForSelector:@selector(timer:game)]];
theTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval
invocation:timerInvocation
repeats:YES];
Run Code Online (Sandbox Code Playgroud)
我该如何使用Invocation?
我正在尝试使用一个NSInvocation从子类调用超类方法.涉及的代码相对简单,如下所示:
- (NSInvocation*) invocationWithSelector:(SEL)selector {
NSInvocation* call = [[NSInvocation alloc] init];
[call retainArguments];
call.target = super; //ERROR: use of undeclared identifier 'super'
call.selector = @selector(selector);
return call;
}
Run Code Online (Sandbox Code Playgroud)
这对我来说有点奇怪,因为我一直认为它super遵循的规则几乎相同self(即它可以被视为对相关对象的直接引用并分配给变量,用作返回值等) .看来实际情况并非如此.
无论如何,是否有任何简单的方法来让我NSInvocation的目标超类实现(我不能self用作目标,因为子类重写超类方法),或者我是否需要寻找其他方法?
这test.m是我用来测试行为的独立文件.
编译:clang test.m -o test.app -fobjc-arc -ObjC -framework Foundation.确保已安装Xcode命令行工具.
#import <Foundation/Foundation.h>
@protocol Protocol
@optional
- (id)objProxyMethod;
@end
@interface ReturnObject: NSObject
@end
@interface Test : NSObject <Protocol>
@end
@interface Proxy : NSObject <Protocol>
- (id)objProxyMethod;
@end
@implementation ReturnObject
- (void)dealloc {
NSLog(@"ERROR:");
NSLog(@"I'm getting deallocated!");
NSLog(@"This shouldn't happen!");
}
- (NSString *)description {
return @"Blank object!";
}
@end
@implementation Proxy
- (id)objProxyMethod {
NSLog(@"in [Proxy objProxyMethod]!");
return [[ReturnObject alloc] init];
}
@end
@implementation Test
- (void)forwardInvocation:(NSInvocation *)invocation …Run Code Online (Sandbox Code Playgroud) 在整个项目中哪一个更好用于将数据从一个类流向另一个类?
NSInvocation
NSNotificationCentre
delegate 方法
或者我不知道的任何其他方法?
以下是我从Apple的"计时器编程主题"中看到的示例代码:
NSMethodSignature *methodSignature = [self
methodSignatureForSelector:@selector(invocationMethod:)];
NSInvocation *invocation = [NSInvocation
invocationWithMethodSignature:methodSignature];
[invocation setTarget:self];
[invocation setSelector:@selector(invocationMethod:)];
NSDate *startDate = [NSDate date];
[invocation setArgument:&startDate atIndex:2];
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我们必须指定invocationMethod:一次在NSMethodSignature中,然后第二次NSInvocation的setSelector.对我来说,这似乎是多余的,苹果设计有这样的理由吗?
我尝试调用一些块,但我遇到了一个EXC_BAD_ACCESS.
-(void) methodA {
self.block = ^ {
[self methodB];
};
}
-(void) webViewDidFinishLoad:(UIWebView *)webView {
[block invoke]; // error here (block is not valid id type).
}
-(void)methodB {
//do something
}
Run Code Online (Sandbox Code Playgroud)
有关为什么会发生这种情况的任何想法?
nsinvocation ×10
objective-c ×6
ios ×5
cocoa-touch ×2
arguments ×1
cocoa ×1
dealloc ×1
delegates ×1
inheritance ×1
iphone ×1
nstimer ×1
retain ×1