在Panel paint方法中,我想在第一次调用时创建一个TCIFilter,然后在后续调用中重用过滤器。所以这是代码。它只是切换一个表单级变量FFirst。所以这里有一些代码
if FFirst then
begin
...
filter := TCIFilter.Wrap(TCIFilter.OCClass.filterWithName(NSSTR('CISepiaTone')));
...
filter.retain;
FFirst := false;
end;
如果我省略了retain,那么后来对paint方法的调用会在我尝试使用过滤器时抛出异常(无法识别的选择器被发送到实例 - 因为我猜过滤器不再是TCFilter)。
但是过滤器我已经创建了一个全局变量,因此它永远不会超出范围,所以为什么我需要保留?为什么界面会丢失参考?我缺少的东西,这是在OSX上使用XE6,但我认为这同样适用于iOS,tia
编辑:这是所有的代码 - 也许还有别的我做错了。删除表单上的面板和轨迹栏并连接面板.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
答案 0 :(得分:0)
您必须记住Delphi在您的项目中使用ARC 为了做到这一点,它插入代码以增加和减少适当点的引用计数 它仅为需要引用计数的类型(接口,字符串等)插入此代码 它不会为其他类型添加此代码;特别是指针。
我强烈怀疑你的问题在这里:
procedure setValue(value: pointer; forKey : NSString); cdecl;
您将对象提供给setValue,但您将其伪装成普通指针
这意味着将发生以下情况(我在这里推测,因为您没有包含SetValue
的代码)。
您复制在SetValue中传递的对象并将其分配给Filter
。 (这不会增加引用计数,因为您使用的是普通指针)
旧的过滤器由value
替换,并且超出范围并被销毁(这应该是这样)
Filter现在是SetValue中传递的对象的副本。
同时原始对象超出范围;它的引用数量变为零并且它被破坏了 现在Filter指向已销毁的引用。
解决方案
通过将value
参数设为refcounted类型或在value.retain
内调用SetValue
来确保自动引用计数。