【问题标题】:I create a filter and set it to a global variable why do I need a retain call?我创建了一个过滤器并将其设置为一个全局变量为什么我需要一个保留调用?
【发布时间】: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

【问题讨论】:

  • 您永远不必自己进行引用计数。您很可能在某个地方遇到了一个错误,导致您的过滤器被释放。也许您通过将对filtercontext 的引用传递给指针来破坏ARC,反之亦然。这行看起来很可疑:Result := PPointer(Field.GetValue(ACanvas).GetReferenceToRawData)^; 你复制了一个引用,但由于转换为指针,它的引用计数没有更新。

标签: ios macos delphi


【解决方案1】:

您必须记住,Delphi 在您的项目中使用 ARC。
为此,它会插入代码以在适当的点增加和减少引用计数。
它只为需要引用计数的类型(接口、字符串等)插入此代码。
它没有为其他类型添加此代码;特别是指针。

我强烈怀疑你的问题在这里:

procedure setValue(value: pointer; forKey : NSString); cdecl;

您将一个对象提供给 setValue,但您将其伪装成一个普通指针。
这意味着将发生以下情况(我在这里推测,因为您没有包含SetValue 的代码)。

您复制 SetValue 中传递的对象并将其分配给Filter。 (这不会增加引用计数,因为您使用的是普通指针)。
旧的过滤器被value 替换并超出范围并被销毁(这是应该的)。
Filter 现在是 SetValue 中传递的对象的副本。

同时原始对象超出范围;它的引用计数变为零并被销毁。
现在 Filter 指向一个被破坏的引用。

解决方案
通过将value 参数设置为引用类型来确保自动引用计数,或者在SetValue 中调用value.retain

【讨论】:

  • setValue 没有代码 - 它是 cocoa 中的一个函数,它实际上是在 NSObject 中声明的,但我在这里声明了它,FMX 将其排除在外。它用于设置参数的值,在本例中为“inputIntensity”,但必须为不同的过滤器设置许多参数。我基于它的可可示例在这里raywenderlich.com/22167/beginning-core-image-in-ios-6。我的应用程序的代码如上,没有别的了。今天晚上我会尝试保留价值,你说的有道理。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-14
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多