Go 调用 Objective-C 导致内存快速增长

问题描述 投票:0回答:1

MacOS 12.6.8 苹果 M1 Pro

go版本go1.20.7 darwin/arm64

调用函数

GetWindowImage
会导致内存快速扩展。

代码:

package main

/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Cocoa -framework ApplicationServices

#import <Cocoa/Cocoa.h>
#import <ApplicationServices/ApplicationServices.h>

CGWindowID GetWindowNumber(const char *title) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *windowTitle = [NSString stringWithUTF8String:title];
    CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
    CGWindowID windowNumber = 0;
    for (NSDictionary *entry in (__bridge NSArray *)windowList) {
        NSString *winTitle = [entry objectForKey:(id)kCGWindowName];
        // NSLog(@"Window Title: %@", winTitle);
        if ([winTitle isEqualToString:windowTitle]) {
            windowNumber = (CGWindowID)[[entry objectForKey:(id)kCGWindowNumber] integerValue];
            break;
        }
    }
    CFRelease(windowList);
    [pool drain];
    return windowNumber;
}


CFDataRef CaptureWindowImageData(CGWindowID windowNumber) {
    CGWindowID windowList[] = {windowNumber};
    CFArrayRef windowArray = CFArrayCreate(NULL, (const void **)windowList, 1, NULL);
    CGImageRef image = CGWindowListCreateImageFromArray(CGRectNull, windowArray, kCGWindowImageDefault);
    NSImage *nsImage = [[NSImage alloc] initWithCGImage:image size:NSZeroSize];
    CFRelease(windowArray);
    CGImageRelease(image);

    NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[nsImage TIFFRepresentation]];
    NSDictionary *props = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor];
    CFDataRef imageData = (CFDataRef)[rep representationUsingType:NSBitmapImageFileTypePNG properties:props];

    [nsImage release];
    [rep release];

    return imageData;
}
*/
import "C"
import (
    "bytes"
    "fmt"
    "image"
    "image/png"
    "unsafe"
)

func GetWindowNumber(name string) uint {
    cTitle := C.CString(name)
    defer C.free(unsafe.Pointer(cTitle))
    return uint(C.GetWindowNumber(cTitle))
}

func GetWindowImage(windowNumber uint) (image.Image, error) {
    imageData := C.CaptureWindowImageData(C.uint(windowNumber))
    if imageData == C.CFDataRef(unsafe.Pointer(nil)) {
        return nil, fmt.Errorf("failed to get window image data for %d", windowNumber)
    }
    defer C.CFRelease(C.CFTypeRef(imageData))
    dataLength := C.CFDataGetLength(imageData)
    dataPtr := C.CFDataGetBytePtr(imageData)
    data := C.GoBytes(unsafe.Pointer(dataPtr), C.int(dataLength))
    return png.Decode(bytes.NewReader(data))
}

func main() {
    winId := GetWindowNumber("Clock")
    fmt.Println(winId)
    for i := 0; i < 100; i++ {
        _, err := GetWindowImage(winId)
        if err != nil {
            panic(err)
        }
    }
}

我目前无法确定问题出在哪里,是在 Objective-C 还是 C 中。

请帮助诊断问题并指导如何修复代码。

c objective-c macos go
1个回答
0
投票

首先,这里有一个小错误,当你修复内存泄漏时会导致崩溃,因此要首先修复它,请删除

[rep release]
调用。当您取得(共享)所有权时,您必须仅致电
release

如果您使用名称以“alloc”或“new”开头或包含“copy”的方法创建对象(例如,alloc、newObject 或 mutableCopy),或者向其发送保留消息,则您获得该对象的所有权。您有责任使用release或autorelease放弃您拥有的对象的所有权。任何其他时候,您收到一个对象时,都不得释放它。

有关详细信息,请参阅基本内存管理规则

修复此问题后,您的问题是您在自动释放池上生成大型对象并且从未耗尽该池。当从非 ObjC 上下文调用您时,您有责任管理该池。

您可以在 GetWindowNumber 中正确执行此操作,方法是创建一个 NSAutoreleasePool,然后对其调用

-drain
。您只是没有将其包含在 CaptureWindowImageData 中。

© www.soinside.com 2019 - 2024. All rights reserved.