Making NSPoint/NSRect/NSSize Objects

January 9, 2008

Updated 2010/12/9 to improve the solution (see below). Updated 2016/8/25 to add Swift.

In Cocoa the common NSPoint/NSRect/NSSize/etc. data types are C structures. But what if you need to pass one as an id data type (an object), as I needed to do this while implementing undo for a Cocoa application? The solution is NSValue, which serves as a basic wrapping/container object for these structures:

NSPoint point = NSMakePoint(x, y);
NSValue *pointValue = [NSValue valueWithPoint:point];
[undoManager registerUndoWithTarget:obj 
    selector:@selector(setPositionObject:) object:pointValue];

In Swift:

let point = NSPoint(x: ..., y: ...)
let pointValue = NSValue(point: point)
undoManager.registerUndoWithTarget(obj,
    selector: #selector(setPositionObject(_:)), object: pointValue)

On the receiving end you would extract the value like this:

- (void)setPositionObject:(NSValue *)positionValue {
    NSPoint position = positionValue.pointValue;
    ...
}
@objc func setPositionObject(positionValue: NSValue) {
    let position = positionValue.pointValue
    ...
}

If you’re working on iOS, you’ll be using CGPoint instead: NSValue(CGPoint:) and CGPointValue().


Prior to December 9th, 2010 this post suggested that the following was your best approach (“a few extra steps, but it gets the job done,” I said). Thanks to Fred G for the (embarrassing) correction. I reproduce it here because it does demonstrate a useful technique but is hardly the most sane way to go about it.

NSPoint point = NSMakePoint(x, y);
NSValue *pointValue = [[[NSValue alloc] initWithBytes:&point; 
    objCType:@encode(NSPoint)] autorelease];
[undoManager registerUndoWithTarget:obj 
    selector:@selector(setPositionObject:) object:pointValue];

I suspect that this escaped me at the time because of Apple’s habit of separating documentation for category methods (which valueWithPoint: is) from the normal class documentation. Thanks for improving upon that, Apple.