Do You Need to Release Cgcontextref in Swift

Do you need to release CGContextRef in Swift?

CFTypes are automatically managed unless explicitly specified as Unmanaged.
According to the documentation.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html

Core Foundation objects returned from annotated APIs are automatically
memory managed in Swift—you do not need to invoke the CFRetain,
CFRelease, or CFAutorelease functions yourself. If you return Core
Foundation objects from your own C functions and Objective-C methods,
annotate them with either CF_RETURNS_RETAINED or
CF_RETURNS_NOT_RETAINED. The compiler automatically inserts memory
management calls when it compiles Swift code that invokes these APIs.
If you use only annotated APIs that do not indirectly return Core
Foundation objects, you can skip the rest of this section. Otherwise,
continue on to learn about working with unmanaged Core Foundation
objects.

When Swift imports APIs that have not been annotated, the compiler
cannot automatically memory manage the returned Core Foundation
objects. Swift wraps these returned Core Foundation objects in an
Unmanaged structure.

Unmanaged types will have the type signature

func StringByAddingTwoStrings(CFString!, CFString!) -> Unmanaged<CFString>!

CGBitmapContextCreate has the type signature

func CGBitmapContextCreate(...) -> CGContext!

Hence its managed automatically by swift.

How to correctly clean up the memory when creating a CGContextRef?

Yes. You do have to eventually free() bitmapData (once the CGContext is done drawing etc.) if you malloc()ed it.

See my (corrected) answer to your other question.

If you use the NULL option (i.e. if you do not malloc() your bitmapData), I would expect the data pointer to be valid until you CGContextRelease() your context. So you'd either need to do whatever you want to do with the data before releasing the context, or copy the bitmap data somewhere (in which case you could just as well malloc() the memory beforehand and pass it to the context).

Converting Objective-C code to Swift: okay to omit release calls?

Per the docs:

Core Foundation types are automatically imported as full-fledged Swift classes. Wherever memory management annotations have been provided, Swift automatically manages the memory of Core Foundation objects, including Core Foundation objects that you instantiate yourself

So you can omit the calls.

Redrawing CGContext after it's already been drawn in Swift?

You need to do all of your drawing inside override func draw(_ rect: CGRect) (or inside routines called from there). So your draw needs a flag set outside (by your actions) to tell it which of your styles to draw. You trigger the redraw with setNeedsDisplay() - but that should be called from your action, not from the drawing code. Your current func drawStyleOne() tries to draw (but probably to an invalid currentContext) and then calls setNeedsDisplay(), which just schedules your actual draw routine to be run, which always draws the same "style".

The UIView class works behind the scenes to do quite a lot - if makes sure that draw isn't called unnecessarily, and schedules it when it is necessary, it figures out which parts of the view need re-drawn if they have been obscured (that's why it passes you a rect), and it sets up the current graphics context for draw, amongst other things.

See this Ray Wenderlich tutorial, or the Apple docs. As the Apple docs say:

For custom views, you must override the drawRect: method and perform
all your drawing inside it.

(my emphasis)

So your code should look something like this:

class VectorView: UIView {
public var whichStyle: SomeEnumOfYours = // your default value

override func draw(_ rect: CGRect) {
switch self.whichStyle {
case .style1:
self.drawStyleOne()
case .style2:
self.drawStyleTwo()
//...
}
}
}

//...
func styleButtonPressed() {
NSLog("STYLE BUTTON PRESSED")
_vectorView?.whichStyle = // whichever style required
_vectorView?.setNeedsDisplay()
}

How to correctly update a CGContext with a function call?

You should not call draw() method directly ... You ca call setNeedsDisplay() or layoutIfNeeded()

As per Apple docs

This method is called when a view is first displayed or when an event
occurs that invalidates a visible part of the view. You should never
call this method directly yourself. To invalidate part of your view,
and thus cause that portion to be redrawn, call the setNeedsDisplay()
or setNeedsDisplay(_:) method instead.

So your code becomes

public func updateMap(scene: GameScene){
x = scene.current_xcoords
y = scene.current_ycoords
let w = self.frame.width
let h = self.frame.height
drect = CGRect(x: (w/50) + CGFloat(x)*w/8, y: (h/100) + CGFloat(y)*h/8, width: (h / 11), height: (h / 11))
setNeedsDisplay()
}

Empty CGContext

I'm not sure what the code in the liked article would do, but two things are different with your Swift code.

  • bytesPerRow: width // width * 4 (== bitmapBytesPerRow)
  • space : NULL // CGColorSpaceCreateDeviceRGB()

The documentation of CGBitmapContextCreate does not say anything about supplying NULL for colorspace, but the header doc says The number of components for each pixel is specified by space, so, at least, CGColorSpaceCreateDeviceRGB() is not appropriate for alphaOnly (which should have only 1 component per pixel).

As far as I tested, this code returns non-nil CGContext:

    let bitmapBytesPerRow = width //<-
let bitmapByteCount = bitmapBytesPerRow * height

let pixelData = UnsafeMutablePointer<UInt8>.allocate(capacity: bitmapByteCount)

let context = CGContext(data: pixelData,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: bitmapBytesPerRow,
space: CGColorSpaceCreateDeviceGray(), //<-
bitmapInfo: CGImageAlphaInfo.alphaOnly.rawValue)

But, not sure if this works for your purpose or not.

CGContext AND CGContextRef the same in Swift? How does this work?

There's nothing particularly magical here. CGContextRef and CGContext are just different names for (almost) the same thing. Swift just blurs the typedef for you.

<CoreGraphics/CGContext.h>

typedef struct CGContext *CGContextRef;

See also the Using Swift with Cocoa and Objective-C guide section on Core Foundation:

When Swift imports Core Foundation types, the compiler remaps the names of these types. The compiler removes Ref from the end of each type name because all Swift classes are reference types, therefore the suffix is redundant.

They just chose not to take away the Ref form of the name, I guess for old timers who are used to typing it :D

How do I get UIImage from a CGContextRef?

Something like this :

-(UIImage*)doImageOperation
{
// Do your stuff here
CGImageRef imgRef = CGBitmapContextCreateImage(context);
UIImage* img = [UIImage imageWithCGImage:imgRef];
CGImageRelease(imgRef);
CGContextRelease(context);
return img;
}


Related Topics



Leave a reply



Submit