【发布时间】:2016-04-13 12:11:09
【问题描述】:
在面板绘制方法中,我想在第一次调用时创建一个 TCIFilter,然后在后续调用中重用该过滤器。所以这是代码。它只是切换表单级变量 FFirst。所以这里有一些代码
if FFirst then
begin
...
filter := TCIFilter.Wrap(TCIFilter.OCClass.filterWithName(NSSTR('CISepiaTone')));
...
filter.retain;
FFirst := false;
end;
如果我忽略了保留,那么当我尝试使用过滤器时,对paint方法的后续调用会引发异常(发送到实例的无法识别的选择器 - 因为我猜过滤器不再是 TCFilter)。
但是过滤器我已经创建了一个全局变量,所以它永远不会超出范围,那么为什么我需要保留?为什么接口会丢失引用?我缺少的东西,这是在使用 XE6 的 OSX 上,但我认为这同样适用于 iOS,tia
编辑:这是所有代码 - 也许还有其他我做错的事情。在表单上放置一个面板和一个轨迹栏并连接 panel.OnPaint 和 trackbar.OnChange 事件
unit Unit3;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
system.Rtti, FMX.Platform.Mac, FMX.Canvas.Mac, Macapi.CoreGraphics,
FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
Macapi.Helpers, MacApi.Foundation, Macapi.QuartzCore,
MacApi.CocoaTypes, Macapi.ObjectiveC;
type
TForm3 = class(TForm)
Button1: TButton;
Button2: TButton;
Panel1: TPanel;
TrackBar1: TTrackBar;
procedure Panel1Paint(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
procedure FormCreate(Sender: TObject);
procedure TrackBar1Change(Sender: TObject);
private
cgImage : CGImageRef;
FFirst : Boolean;
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
const
CoreImageFwk = '/System/Library/Frameworks/CoreImage.framework/CoreImage';
implementation
{$R *.fmx}
type
CIFilterClass = interface(NSObjectClass)
['{FB4A9CD9-7D60-482D-A0F4-4F78FC6E7E8D}']
{class} function filterNamesInCategories(categories: NSArray): NSArray; cdecl;
{class} function filterNamesInCategory(category: NSString): NSArray; cdecl;
{class} function filterWithImageData(data: NSData; options: NSDictionary): Pointer; cdecl;
{class} function filterWithImageURL(url: NSURL; options: NSDictionary): Pointer; cdecl;
{class} function filterWithName(name: NSString): Pointer; cdecl; overload;
{class} function filterWithName(name: NSString; keysAndValues: Pointer): Pointer; cdecl; overload;
{class} function localizedDescriptionForFilterName(filterName: NSString): NSString; cdecl;
{class} function localizedNameForCategory(category: NSString): NSString; cdecl;
{class} function localizedNameForFilterName(filterName: NSString): NSString; cdecl;
{class} function localizedReferenceDocumentationForFilterName(filterName: NSString): NSURL; cdecl;
end;
CIFilter = interface(NSObject)
['{2ACA27E7-D365-4AAC-A474-E72867CDE89A}']
function apply(apply: CIKernel): CIImage; cdecl; overload;
function apply(k: CIKernel; arguments: NSArray; options: NSDictionary): CIImage; cdecl; overload;
function attributes: NSDictionary; cdecl;
function inputKeys: NSArray; cdecl;
function isEnabled: Boolean; cdecl;
function outputKeys: NSArray; cdecl;
procedure setDefaults; cdecl;
// I added these 2 methods because they're not in FMX
procedure setValue(value: pointer; forKey : NSString); cdecl;
function outputImage : Pointer {CIImage}; cdecl;
end;
TCIFilter = class(TOCGenericImport<CIFilterClass, CIFilter>) end;
procedure TForm3.FormCreate(Sender: TObject);
begin
Ffirst := true;
end;
var
fileNameAndPath : Macapi.Foundation.NSURL;
beginImage, outputImage : CIImage;
context : CIContext;
filter : CIFilter;
pImage : Pointer;
p : CGPoint;
r : CGRect;
procedure TForm3.Panel1Paint(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
var
d : double;
function GetCGContextFromCanvas(ACanvas: TCanvas): CGContextRef;
var
Context: TRttiContext;
Field: TRttiField;
begin
Field := Context.GetType(ACanvas.ClassType).GetField('FContext');
Assert(Field <> nil);
Result := PPointer(Field.GetValue(ACanvas).GetReferenceToRawData)^;
end;
function kCIContextOutputColorSpace: NSString;
begin
Result := CocoaNSStringConst(CoreImageFwk, 'kCIContextOutputColorSpace');
end;
begin
if FFirst then
begin
fileNameAndPath := TNSUrl.Wrap(TNSUrl.OCClass.fileURLWithPath(StrToNSStr('/path/to/an/image.png')));
beginImage := TCIImage.Wrap(TCIImage.OCClass.imageWithContentsOfURL(fileNameAndPath));
context := TCIContext.Wrap(TCIContext.OCClass.contextWithCGContext( GetCGContextFromCanvas(Canvas), nil ));
filter := TCIFilter.Wrap(TCIFilter.OCClass.filterWithName(NSSTR('CISepiaTone')));
filter.setValue( (beginImage as ILocalObject).GetObjectID, NSSTR('inputImage'));
filter.retain; // comment out this line and it crashes
context.retain; // same with this one
FFirst := false;
end;
d := TrackBar1.value/100;
filter.setValue( TNSNumber.OCClass.numberWithFloat(d), NSSTR('inputIntensity'));
outputImage := TCIImage.Wrap(filter.outputImage);
cgImage := context.createCGImage(outputImage, outputImage.extent);
p := CGPointMake(0,0);
r := CGRectMake(0,0, outputImage.extent.size.width, outputImage.extent.size.height);
CGContextDrawImage(GetCGContextFromCanvas(Canvas), r, cgImage);
CGImageRelease(cgImage);
end;
procedure TForm3.TrackBar1Change(Sender: TObject);
begin
Panel1.Repaint;
end;
end.
编辑 2:基本问题见此处 Delphi XE6 ARC on OSX releasing variables
【问题讨论】:
-
您永远不必自己进行引用计数。您很可能在某个地方遇到了一个错误,导致您的过滤器被释放。也许您通过将对
filter和context的引用传递给指针来破坏ARC,反之亦然。这行看起来很可疑:Result := PPointer(Field.GetValue(ACanvas).GetReferenceToRawData)^;你复制了一个引用,但由于转换为指针,它的引用计数没有更新。