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 中。
请帮助诊断问题并指导如何修复代码。
首先,这里有一个小错误,当你修复内存泄漏时会导致崩溃,因此要首先修复它,请删除
[rep release]
调用。当您取得(共享)所有权时,您必须仅致电 release
:
如果您使用名称以“alloc”或“new”开头或包含“copy”的方法创建对象(例如,alloc、newObject 或 mutableCopy),或者向其发送保留消息,则您获得该对象的所有权。您有责任使用release或autorelease放弃您拥有的对象的所有权。任何其他时候,您收到一个对象时,都不得释放它。
有关详细信息,请参阅基本内存管理规则。
修复此问题后,您的问题是您在自动释放池上生成大型对象并且从未耗尽该池。当从非 ObjC 上下文调用您时,您有责任管理该池。
您可以在 GetWindowNumber 中正确执行此操作,方法是创建一个 NSAutoreleasePool,然后对其调用
-drain
。您只是没有将其包含在 CaptureWindowImageData 中。