Difference between NSPoint, NSSize, NSRect and the CG Versions


Dave
 

Hi,

Is there any real difference between NSPoint, NSRect, NSSize etc. and the CG versions?

I can use the CG Versions on Mac and iOS but the NS versions only on Mac.

If I only used the CG versions it would compile ok for both platforms, but is this wise, or is it best to use NS on Mac?

All the Best
Dave


Alex Zavatone
 

Look at the types that back them.

Structs and CGFloats?

On Aug 31, 2017, at 6:03 AM, Dave <dave@...> wrote:

Hi,

Is there any real difference between NSPoint, NSRect, NSSize etc. and the CG versions?

I can use the CG Versions on Mac and iOS but the NS versions only on Mac.

If I only used the CG versions it would compile ok for both platforms, but is this wise, or is it best to use NS on Mac?

All the Best
Dave




Dave
 

Well,

typedef CGRect NSRect;

So, it looks like they are one and the same thing? But in that case why are there two sets?

I’m just trying to understand what these different types are supposed to be used for.

Cheers
Dave

On 31 Aug 2017, at 13:05, Alex Zavatone <zav@...> wrote:

Look at the types that back them.

Structs and CGFloats?

On Aug 31, 2017, at 6:03 AM, Dave <dave@...> wrote:

Hi,

Is there any real difference between NSPoint, NSRect, NSSize etc. and the CG versions?

I can use the CG Versions on Mac and iOS but the NS versions only on Mac.

If I only used the CG versions it would compile ok for both platforms, but is this wise, or is it best to use NS on Mac?

All the Best
Dave





 



On Aug 31, 2017, at 4:12 AM, Dave <dave@...> wrote:

So, it looks like they are one and the same thing? But in that case why are there two sets?

Historical reasons. AppKit predates Core Graphics (it predates Apple's acquisition of NeXT.)

—Jens


Quincey Morris
 

On Aug 31, 2017, at 04:12 , Dave <dave@...> wrote:

But in that case why are there two sets?

There are “really” four different SDKs:

— 32-bit macOS

— 64-bit macOS

— 32-bit iOS

— 64-bit iOS

They are different in the sense that certain basic C types (such as int and float) are potentially different byte sizes, and certain derived Obj-C types (such as CGFloat, NSInteger and NSUInteger) are based on different C types (which may or may not affect their size).

In all SDKs other than 32-bit macOS, the NS and CG variants of points, rects, etc are synonyms, and can be used interchangeably.

In the 32-bit macOS SDK, they are based on different underlying types. Even if the struct members are the same size (they are, IIRC, all 4 bytes), the difference in type means the NS and CG versions are not technically compatible at the ABI level — at the calling interface between functions, for example.

Somewhere around macOS 10.5, when 64-bit macOS was introduced, it also became possible to compile 32-bit apps with a “compile like 64-bit” setting, which brought the 32-bit SDK in line with all the rest, and made the NS and CG types compatible. However, you had to opt into that manually, because using that option would make your code ABI-incompatible with (say) existing 3rd party frameworks that didn’t use the option.

In short, there are two sets (as Jens said) for historical reasons.

Apple seems to have settled on the CG versions as the new normal, so you should always use those in new code, except in the rare case where you’re writing an app that needs to deployable on old 32-bit only Macs. IIRC, Macs running 10.6.8 or higher have full 64-bit support, and again IIRC current macOS versions don’t support 32-bit code at all.


 



On Aug 31, 2017, at 10:45 AM, Quincey Morris <quinceymorris@...> wrote:

In the 32-bit macOS SDK, they are based on different underlying types. Even if the struct members are the same size (they are, IIRC, all 4 bytes), the difference in type means the NS and CG versions are not technically compatible at the ABI level — at the calling interface between functions, for example.

I don't see how they could be incompatible if they're the same size. The only 4-byte floating point type is 'float', so if the CG and NS types are using the same-size values, how can they be different underlying types?

(I don't remember the details, but perhaps in 32-bit the NS types used float while the CG types used double?)

—Jens


Quincey Morris
 

On Aug 31, 2017, at 12:19 , Jens Alfke <jens@...> wrote:

I don't see how they could be incompatible if they're the same size.

I don’t remember the exact details either, but one issue is that there are places in the Obj-C runtime/metadata/whatever where the @encode string of a method signature matters, and some similar same-size, same-representation types are represented by different letters in the string. This could mean that although method parameters and return values were bit-for-bit identical, the type encoding made the methods incompatible.

One particular example I remember, unrelated to this thread’s topic, is that in 64-bit, NSUInteger encoded to “Q” (quad-word, i.e. long long) not “L” (long). Both types were 8 bytes, and NSUInteger was even typedef’ed to long (IIRC), but “L” was never used in that architecture.

The NSPoint/CGPoint thing was something like that. IIRC, NSPoint used float, and CGPoint used CGFloat, and those @encoded differently for some reason.


Steve Christensen <punster@...>
 

Here are the definitions copied from my iOS SDK headers. The types/sizes for CGFloat and NS[U]Integer track the 32-/64-bit architecture used to build a particular executable.

typedef CGFLOAT_TYPE CGFloat;

#if defined(__LP64__) && __LP64__
# define CGFLOAT_TYPE double
#else
# define CGFLOAT_TYPE float
#endif

#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif


Regarding NSPoint, I see it defined as

typedef CGPoint NSPoint;

so its x and y fields will be CGFloats.


On Aug 31, 2017, at 12:39 PM, Quincey Morris <quinceymorris@...> wrote:

On Aug 31, 2017, at 12:19 , Jens Alfke <jens@...> wrote:

I don't see how they could be incompatible if they're the same size.

I don’t remember the exact details either, but one issue is that there are places in the Obj-C runtime/metadata/whatever where the @encode string of a method signature matters, and some similar same-size, same-representation types are represented by different letters in the string. This could mean that although method parameters and return values were bit-for-bit identical, the type encoding made the methods incompatible.

One particular example I remember, unrelated to this thread’s topic, is that in 64-bit, NSUInteger encoded to “Q” (quad-word, i.e. long long) not “L” (long). Both types were 8 bytes, and NSUInteger was even typedef’ed to long (IIRC), but “L” was never used in that architecture.

The NSPoint/CGPoint thing was something like that. IIRC, NSPoint used float, and CGPoint used CGFloat, and those @encoded differently for some reason.


Quincey Morris
 

On Aug 31, 2017, at 14:07 , Steve Christensen <punster@...> wrote:

Regarding NSPoint, I see it defined as

typedef CGPoint NSPoint;

so its x and y fields will be CGFloats.

Opening up a macOS project and looking at the definition of NSPoint, I see:

#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64

typedef CGPoint NSPoint;


#else

typedef struct _NSPoint {
    CGFloat x;
    CGFloat y;
} NSPoint;


#endif

So, it was the same size as CGPoint, but a different struct name, which was enough to create an ABI incompatibility. You could opt into the new typedef for 32-bit by using NS_BUILD_32_LIKE_64.



Dave
 

Thanks a lot Quincey and Jens for your through explanation - I had imported some Mac code from another project which uses NSRect’s and as my original project was iOS this caused compile errors, so rather than trying to handle both sets, I’ll just use the CG versions from now on.

There are a few calls to NSInsetRect, NSOffsetRect, etc., too, I’m guessing these can just be replaced with the CG version too.

Thanks again
All the Best
Dave


On 1 Sep 2017, at 01:11, Quincey Morris <quinceymorris@...> wrote:

On Aug 31, 2017, at 14:07 , Steve Christensen <punster@...> wrote:

Regarding NSPoint, I see it defined as

typedef CGPoint NSPoint;

so its x and y fields will be CGFloats.

Opening up a macOS project and looking at the definition of NSPoint, I see:

#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64

typedef CGPoint NSPoint;


#else

typedef struct _NSPoint {
    CGFloat x;
    CGFloat y;
} NSPoint;


#endif

So, it was the same size as CGPoint, but a different struct name, which was enough to create an ABI incompatibility. You could opt into the new typedef for 32-bit by using NS_BUILD_32_LIKE_64.