gpt4 book ai didi

ios - 为什么使用 ARC 将其标记为潜在泄漏?

转载 作者:塔克拉玛干 更新时间:2023-11-02 09:18:41 25 4
gpt4 key购买 nike

我仍然对 ARC、桥接和某些非免费桥接 CF 对象感到困惑。我目前的困惑是围绕 CFSocket。我很确定我正在正确清理,但分析工具却告诉我并非如此。也许我的不和谐让我看不到泄漏,或者工具是错误的。我还没准备好责备这个工具,所以我正在寻找其他人来指出这个问题。例如,我是否缺少将所有权转让给我的 __bridge 形式?

在我的项目中,使用 ARC,我有一个基于 TCP 的服务器。让我们称这个类为“MyServer”。在 MyServer 中,我有一个内部属性 socket,定义如下:

@property (assign) CFSocketRef socket;

此属性保存服务器运行时的套接字引用。停止服务器将释放引用,删除服务器对象也是如此。我还试图清理在启动服务器过程中产生的任何潜在泄漏。正是在这个领域,我遇到了静态分析问题。

服务器是用这个方法启动的:

- (BOOL)startServer
{
BOOL started = NO;

NSLog(@"[%@ %@] starting server on port %u", NSStringFromClass([self class]), NSStringFromSelector(_cmd),self.port);

self.lastError = nil;

if ([self createSocket]) {
started = YES;
_state = SERVER_STATE_STARTING;
};

return started;
}

createSocket 方法创建套接字(duh),如下所示:

-(BOOL)createSocket
{
BOOL result = YES;

self.socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM,
IPPROTO_TCP, 0, NULL, NULL);
if (self.socket != NULL) {
int reuse = true;
int fileDescriptor = CFSocketGetNative(self.socket);
if (setsockopt(fileDescriptor, SOL_SOCKET, SO_REUSEADDR,
(void *)&reuse, sizeof(int)) == 0) {

struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(self.port);


CFDataRef addressData = CFDataCreate(NULL,
(const UInt8 *)&address,
sizeof(address));

if (addressData && CFSocketSetAddress(self.socket, addressData) == kCFSocketSuccess) {
self.listenHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor
closeOnDealloc:YES];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveIncomingConnectionNotification:)
name:NSFileHandleConnectionAcceptedNotification
object:nil];
[self.listenHandle acceptConnectionInBackgroundAndNotify];

_state = SERVER_STATE_RUNNING;
} else {
result = NO;
[self errorWithName:@"Unable to bind socket to address."];
}
CFRelease(addressData);
} else {
[self errorWithName:@"Unable to set socket options."];
CFRelease(self.socket);
CFSocketInvalidate(self.socket);
CFRelease(self.socket);
self.socket = nil;
result = NO;
}
} else {
[self errorWithName:@"Unable to create socket."];
// CFRelease(self.socket); //NO - CFRelease(NULL) is a runtime error!
result = NO;
}

return result;
}

当我对此代码运行静态分析时,Xcode 报告了一系列围绕 self.socket 的潜在泄漏。这是一个示例,来自上面的 createSocket 方法:

(static analysis error)

的确,我不再在此路径中引用该对象。也许有一些方法可以告诉系统我想拥有该对象,而它提示的原因是它无法告诉我我拥有该对象。我是否应该使用其中一个 __bridge 转换来传达该信息?我尝试使该属性保留或强壮,但这并没有建立,因为它不是一个对象。还有其他想法吗?

最佳答案

几个想法:

  1. 正如 Jesse 指出的那样,问题在于您正在使用 socket 的访问器方法。分析器有点困惑,认为对象传递给了 setSocket方法正在泄漏,没有意识到您将它保存在实例变量中。如果您替换这些出现的 self.socket_socket ,你的警告与 self.socket 有关走开。

  2. 您的代码生成关于 addressData 的第二个警告与您有一个执行路径的事实有关 addressData可能是 NULL ,但您仍在调用 CFRelease .您应该让代码明确检查不是 NULL在尝试之前 CFRelease .

  3. 您将释放您的套接字两次,一次是在使它无效之前,一次是在它之后。显然,您不想发布两次。我还建议将套接字设置为 NULL , 不是 nil ,并不是说它有那么重要。

  4. 部分原因是我对第 2 点所做的更改(如果 elseaddressData,我需要在此处添加另一个 NULL 子句),但也作为逻辑上的一般转变,因为你有很多与失败相关的执行路径,但只有一个与成功相关,我建议默认 resultNO并将其设置为 YES在那个单一的成功执行路径中。这确保我们在创建成功但监听不成功的所有不同路径中使套接字无效并释放。我相信之前有一些执行路径没有被正确覆盖。

因此,我最终得到了 createSocket 的演绎版:

-(BOOL)createSocket
{
BOOL result = NO;

_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM,
IPPROTO_TCP, 0, NULL, NULL);

if (_socket != NULL) {
int reuse = true;
int fileDescriptor = CFSocketGetNative(self.socket);
if (setsockopt(fileDescriptor, SOL_SOCKET, SO_REUSEADDR,
(void *)&reuse, sizeof(int)) == 0) {

struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(self.port);


CFDataRef addressData = CFDataCreate(NULL,
(const UInt8 *)&address,
sizeof(address));

if (addressData) {
if (CFSocketSetAddress(_socket, addressData) == kCFSocketSuccess) {
self.listenHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor
closeOnDealloc:YES];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveIncomingConnectionNotification:)
name:NSFileHandleConnectionAcceptedNotification
object:nil];
[self.listenHandle acceptConnectionInBackgroundAndNotify];

result = YES;
_state = SERVER_STATE_RUNNING;
} else {
[self errorWithName:@"Unable to bind socket to address."];
}
CFRelease(addressData);
}
}

if (result != YES) {
[self errorWithName:@"Unable to set socket options."];
CFSocketInvalidate(_socket);
CFRelease(_socket);
_socket = NULL;
}
} else {
[self errorWithName:@"Unable to create socket."];
}

return result;
}

我最初的回答侧重于核心基础内存管理的基础知识,虽然很重要,但与手头的问题并不直接相关。

原答案:

是的,如果 Core Foundation 函数调用有 CreateCopy在名称中,您拥有该对象。因此,您必须:

显然,前者适用于此,但通常这两种方法都适用。

关于ios - 为什么使用 ARC 将其标记为潜在泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14306318/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com