将CIContext定义为NSObject属性以重用

时间:2020-06-05 14:16:51

标签: objective-c macos cocoa

OSX ELCapitan,XCode8.2,Objective-c可可项目。

在我的一个NSobject类中,一个方法需要一个CIContext才能将CIImage位图数据呈现到内存中(CIContext渲染至Bitmap)。这种方法被频繁调用,因此速度是一个重要因素。由于为每个渲染创建一个CIContext非常耗时,因此我尝试创建一个CIContext属性,在类init中进行init,然后在方法中将其作为self.context调用。

但是,如果以这种方式进行操作,则会在CIContext的呈现命令中收到一条BADEXECESS错误消息。 如果我将声明和初始化放在方法中,并每次创建一个CIContext,则在调用此方法时,一切正常。 如果我在其他任何地方声明它,则会导致错误。

@interface myClass:NSObject
@property CIContext *ciContext;
@end

@implementation myClass

-(instancetype)init {
    self = [super init];
    _ciContext = [CIContext context];
    //self.ciContext = [CIContext context]; same result
    return self;
}

-(void)myMethod {

    [_ciContext render:xxx toBitmap:xxx rowBytes:xxx bounds:xxx format:xxx colorSpace:xxx];
    //[self.ciContext render:xxx toBitmap:xxx rowBytes:xxx bounds:xxx format:xxx colorSpace:xxx]; same result

}

导致BADEXCESS。以下内容也不起作用:

@interface myClass:NSObject
@end

@implementation myClass {
  CIContext *ciContext;
}

-(instancetype)init {
    self = [super init];
    ciContext = [CIContext context];
    return self;
}

-(void)myMethod {

    [ciContext render:xxx toBitmap:xxx rowBytes:xxx bounds:xxx format:xxx colorSpace:xxx];

}

但是:

@interface myClass:NSObject
@end

@implementation myClass

-(void)myMethod {

 CIContext *ciContext = [CIContext context];
 [ciContext render:xxx toBitmap:xxx rowBytes:xxx bounds:xxx format:xxx colorSpace:xxx];

}

完美运行。

???

我该如何解决?如何创建班级的可重用CIContext?还是真的,我每次调用要使用的方法时都必须创建一个新的?

1 个答案:

答案 0 :(得分:0)

警告

OS X El Capitan,Xcode 8.2

我没有OS X El Capitan。以下代码已在启用了ARC的macOS Catalina&Xcode 11.5上进行了测试(自OS X Lion IIRC以来,您也应该能够使用ARC,受到完全支持)。

评论

  • 基于注释-我真的很想知道您的示例代码是否为真实代码(@property ciContext *CIContext,...)。
  • 请勿使用myClass作为类名,它实际上应该是MyClass

CIContext

Documentation引用:

CIContextCIImage对象是不可变的,因此多个线程可以使用相同的CIContext对象来呈现CIImage对象。但是,CIFilter对象是可变的,因此不能在线程之间安全地共享。每个线程必须创建自己的CIFilter对象,但是您可以在线程之间传递过滤器的不变的输入和输出CIImage对象。

因此,CIContextCIImage可以在多个线程中使用,而没有任何问题。

示例

计算 100 张图像的平均颜色。

ImageProcessing.h

@import CoreImage;
@import AppKit;

@interface ImageProcessing : NSObject

+ (nonnull instancetype)sharedInstance;

- (nullable NSColor *)averageColorOf:(nonnull CIImage *)image;

@end

ImageProcessing.m

#import "ImageProcessing.h"

@interface ImageProcessing ()

@property (nonnull, nonatomic, strong) CIContext *context;

@end

@implementation ImageProcessing

+ (instancetype)sharedInstance {
    static ImageProcessing *instance = nil;
    static dispatch_once_t token;

    dispatch_once(&token, ^{
        instance = [[self alloc] init];
    });

    return instance;
}

- (instancetype)init {
    if ((self = [super init]) == nil) {
        return nil;
    }

    _context = [CIContext context];
    return self;
}

- (NSColor *)averageColorOf:(CIImage *)image {
    CIVector *extent = [[CIVector alloc] initWithX:image.extent.origin.x
                                                 Y:image.extent.origin.y
                                                 Z:image.extent.size.width
                                                 W:image.extent.size.height];

    CIFilter *filter = [CIFilter filterWithName:@"CIAreaAverage"
                                  keysAndValues:kCIInputImageKey, image, kCIInputExtentKey, extent, nil];

    CIImage *output = [filter outputImage];

    if (output == nil) {
        return nil;
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
    if (colorSpace == NULL) {
        return nil;
    }

    char bitmap[4] = {0};

    [_context render:output
            toBitmap:&bitmap
            rowBytes:4 bounds:CGRectMake(0, 0, 1, 1)
              format:kCIFormatRGBA8
          colorSpace:colorSpace];

    CGColorSpaceRelease(colorSpace);

    return [NSColor colorWithSRGBRed:(CGFloat)bitmap[0] / 255.0
                               green:(CGFloat)bitmap[1] / 255.0
                                blue:(CGFloat)bitmap[2] / 255.0
                               alpha:(CGFloat)bitmap[3] / 255.0];
}

@end

AppDelegate.m

将其放置在任何地方,我只是使用了应用程序委托,因为我已经打开了另一个示例项目。

#import "AppDelegate.h"
#import "ImageProcessing.h"
#import "pthread.h"

@interface AppDelegate ()

@property (nonatomic, strong) NSOperationQueue *queue;

@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    _queue = [[NSOperationQueue alloc] init];
    _queue.maxConcurrentOperationCount = 10;
    _queue.qualityOfService = NSQualityOfServiceUtility;

    // Be aware that this is an image from bundle, not from Assets
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"image" withExtension:@"png"];
    CIImage *image = [CIImage imageWithContentsOfURL:url];

    for (NSUInteger i = 0 ; i < 100 ; i++) {
        [_queue addOperationWithBlock:^{
            mach_port_t threadID = pthread_mach_thread_np(pthread_self());            
            NSColor *color = [[ImageProcessing sharedInstance] averageColorOf:image];
            dispatch_async(dispatch_get_main_queue(), ^{
                NSString *output = [NSString stringWithFormat:@"%4lu - %08x - %@", (unsigned long)i, threadID, color];
                NSLog(@"%@", output);
            });
        }];
    }
}

@end

输出:

...
   84 - 00003f03 - sRGB IEC61966-2.1 colorspace -0.501961 -0.180392 -0.45098 -0.00392157
   89 - 0000d217 - sRGB IEC61966-2.1 colorspace -0.501961 -0.180392 -0.45098 -0.00392157
   98 - 0001470b - sRGB IEC61966-2.1 colorspace -0.501961 -0.180392 -0.45098 -0.00392157
   99 - 00007b07 - sRGB IEC61966-2.1 colorspace -0.501961 -0.180392 -0.45098 -0.00392157
   97 - 0000d313 - sRGB IEC61966-2.1 colorspace -0.501961 -0.180392 -0.45098 -0.00392157
   94 - 00003a07 - sRGB IEC61966-2.1 colorspace -0.501961 -0.180392 -0.45098 -0.00392157
   91 - 00001907 - sRGB IEC61966-2.1 colorspace -0.501961 -0.180392 -0.45098 -0.00392157
   95 - 00014f13 - sRGB IEC61966-2.1 colorspace -0.501961 -0.180392 -0.45098 -0.00392157
...