Discussion:
C++ pointer to Cocoa object
Casey McDermott
2018-09-07 17:46:43 UTC
Permalink
We need to link some of our C++ classes to a matching Cocoa class.
It's easy for Cocoa to reference C++ objects. Going the other way is harder.

We have been using a linker class that has a void pointer to the Obj-C object
in the C++ header.  We then cast it to a Cocoa object in the Obj-C++ source.
For example, in the C++ header we have:

void *mCocoaPopupPtr = nil;

Then in the source:

void GSCocoaPopupLinker::setCocoaFieldVisible(const BOOL inValue)
{
if (mCocoaPopupPtr != nil)
{
GSPopupButton *cocoaPopup = (__bridge GSPopupButton *)mCocoaPopupPtr;
[cocoaPopup setHidden : !inValue];
}
}

Problem is, with ARC turned on, the pointer is never nil, so it crashes.
The void pointer somehow becomes an NSAtom instead of 0.

There's very little documentation on NSAtom, but it appears to be Apple's way to
use the excess bits in a 64-bit address to store class info.

Is there some other way to test for an invalid void pointer?

Thanks,

Casey McDermott
TurtleSoft.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to ***@ml-in
Gary L. Wade
2018-09-07 21:03:49 UTC
Permalink
You might also find WWDC 2018, Session 409, informative.
--
Gary L. Wade
http://www.garywade.com/
Post by Casey McDermott
Problem is, with ARC turned on, the pointer is never nil, so it crashes.
The void pointer somehow becomes an NSAtom instead of 0.
Nil is nil, I think your issue is rather that you do not properly retain the pointer before storing it as void*.
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.narkive.net
James Walker
2018-09-07 21:04:24 UTC
Permalink
Post by Casey McDermott
We need to link some of our C++ classes to a matching Cocoa class.
It's easy for Cocoa to reference C++ objects. Going the other way is harder.
We have been using a linker class that has a void pointer to the Obj-C object
in the C++ header.  We then cast it to a Cocoa object in the Obj-C++ source.
void *mCocoaPopupPtr = nil;
void GSCocoaPopupLinker::setCocoaFieldVisible(const BOOL inValue)
{
if (mCocoaPopupPtr != nil)
{
GSPopupButton *cocoaPopup = (__bridge GSPopupButton *)mCocoaPopupPtr;
[cocoaPopup setHidden : !inValue];
}
}
Problem is, with ARC turned on, the pointer is never nil, so it crashes.
The void pointer somehow becomes an NSAtom instead of 0.
There's very little documentation on NSAtom, but it appears to be Apple's way to
use the excess bits in a 64-bit address to store class info.
Is there some other way to test for an invalid void pointer?
I don't use ARC, so I'm just speculating here, but maybe a solution
would be to use the private implementation pattern. That is, in your
public C++ header, the only member would look like

std::unique_ptr< MyClassImp > _imp;

MyClassImp would be declared and defined only in the Objective-C++
source file (except for a forward declaration in the C++ header), so it
could use proper Objective-C types as members.

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email se
Saagar Jha
2018-09-07 21:50:55 UTC
Permalink
Usually the way you get an NSAtom is because you’re reading garbage–either somebody scribbled over your pointer or it was garbage to begin with. Does mCocoaPopupPtr ever get set to nil? Does it have a consistent value? What happens if you run with the Address Sanitizer enabled, or with NSZombieEnabled set?

Saagar Jha
Post by Casey McDermott
We need to link some of our C++ classes to a matching Cocoa class.
It's easy for Cocoa to reference C++ objects. Going the other way is harder.
We have been using a linker class that has a void pointer to the Obj-C object
in the C++ header. We then cast it to a Cocoa object in the Obj-C++ source.
void *mCocoaPopupPtr = nil;
void GSCocoaPopupLinker::setCocoaFieldVisible(const BOOL inValue)
{
if (mCocoaPopupPtr != nil)
{
GSPopupButton *cocoaPopup = (__bridge GSPopupButton *)mCocoaPopupPtr;
[cocoaPopup setHidden : !inValue];
}
}
Problem is, with ARC turned on, the pointer is never nil, so it crashes.
The void pointer somehow becomes an NSAtom instead of 0.
There's very little documentation on NSAtom, but it appears to be Apple's way to
use the excess bits in a 64-bit address to store class info.
Is there some other way to test for an invalid void pointer?
Thanks,
Casey McDermott
TurtleSoft.com
_______________________________________________
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
https://lists.apple.com/mailman/options/cocoa-dev/saagar%40saagarjha.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This
Jens Alfke
2018-09-07 22:48:58 UTC
Permalink
Post by Casey McDermott
Problem is, with ARC turned on, the pointer is never nil, so it crashes.
The void pointer somehow becomes an NSAtom instead of 0.
Something wrote to that pointer, then. If you initialize it to nullptr, it will stay that way. NSAtom is a red herring — probably the mCocoaPopupPtr was pointing to a valid object, but it got freed, and there is now (by chance) an NSAtom instance residing at that address.

The reason the object got freed is probably that your C++ pointer isn't known to ARC so it didn't bump the refcount of the object assigned to it. You'll need to use CFRetain/CFRelease to manually retain objects assigned to that pointer.

—Jens
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.n
Greg Parker
2018-09-07 23:25:53 UTC
Permalink
Post by Jens Alfke
Post by Casey McDermott
Problem is, with ARC turned on, the pointer is never nil, so it crashes.
The void pointer somehow becomes an NSAtom instead of 0.
Something wrote to that pointer, then. If you initialize it to nullptr, it will stay that way. NSAtom is a red herring — probably the mCocoaPopupPtr was pointing to a valid object, but it got freed, and there is now (by chance) an NSAtom instance residing at that address.
NSAtom is one of the tagged pointer object classes. On 64-bit macOS, if you have an address whose lowest four bits are 0x…1, and you use it as if it were an Objective-C object, then it will be an NSAtom. (Same for 64-bit iOS, except with an address that starts with 0x8….)

Nothing in the OS actually uses class NSAtom. (We're trying to get rid of it but there are some binary compatibility problems.) Instead of "pointer variable somehow becomes an NSAtom" you should be looking for "pointer variable somehow has a random or uninitialized value". For example, if the object that contains this mCocoaPopupPtr field were itself deallocated then a use-after-free could cause this symptom.
Post by Jens Alfke
Post by Casey McDermott
Is there some other way to test for an invalid void pointer?
There is no way to programmatically distinguish all valid Objective-C object pointers from all invalid ones. It's just like C and C++ in that respect.
--
Greg Parker ***@apple.com <mailto:***@apple.com> Runtime Wrangler


_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email
Alex Zavatone
2018-09-07 23:46:36 UTC
Permalink
I ran into this while writing wrappers for pjsip in C++ to Obj-C and vice versa.

The only conclusion that I came to was to store what the valid pointer value was after it was created. The failure cases could be an undefined or out of range pointer but they proved not easily able to test for. All I could reliably test for was a known good pointer value, so that’s what I did.

What I did was assume that if a wrapping Obj-C class which allocated the C++ object was valid, that the C++ object was valid and then store the pointer values. If at any time the pointers changed I could not assume that they were pointing to a valid location, so recreate the C++ object again and store the new pointers. It worked.

IIRC, I think I also passed in a weak reference to the Obj-C class so that the C++ functions could access the container. Passing data back and forth by casting was a little tricky. I can look at what we did to work with this this when I get back home

I’m sure it’s not best, but it never crashed for us. I would love to hear more of a real solution if anyone has one.

Hope this helps.

Alex

Sent from my iPhone
Post by Casey McDermott
We need to link some of our C++ classes to a matching Cocoa class.
It's easy for Cocoa to reference C++ objects. Going the other way is harder.
We have been using a linker class that has a void pointer to the Obj-C object
in the C++ header. We then cast it to a Cocoa object in the Obj-C++ source.
void *mCocoaPopupPtr = nil;
void GSCocoaPopupLinker::setCocoaFieldVisible(const BOOL inValue)
{
if (mCocoaPopupPtr != nil)
{
GSPopupButton *cocoaPopup = (__bridge GSPopupButton *)mCocoaPopupPtr;
[cocoaPopup setHidden : !inValue];
}
}
Problem is, with ARC turned on, the pointer is never nil, so it crashes.
The void pointer somehow becomes an NSAtom instead of 0.
There's very little documentation on NSAtom, but it appears to be Apple's way to
use the excess bits in a 64-bit address to store class info.
Is there some other way to test for an invalid void pointer?
Thanks,
Casey McDermott
TurtleSoft.com
_______________________________________________
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
https://lists.apple.com/mailman/options/cocoa-dev/zav%40mac.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.
Casey McDermott
2018-09-08 00:30:22 UTC
Permalink
  NSAtom is a red herring
Quite possible.

Usually we create the C++ object, which constructs a linker in the next line or two.
Then that creates the Cocoa object in the next line or two and hooks them up. 
They all stick around until all are deleted.  Not much chance for object lifetime problems.

This error happened when I accidentally put something after the linker was made,
but before it made the Cocoa object. It's probably the first time the nil test has even
happened on a nil pointer to a Cocoa object.

Unfortunately that specific error is now fixed several times over, and I didn't commit
when it was breaking. But if it happens again I'll do more in-depth testing on it.

Thanks,

Casey McDermott
www.TurtleSoft.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.nar
Jean-Daniel
2018-09-08 09:15:33 UTC
Permalink
Post by Casey McDermott
We need to link some of our C++ classes to a matching Cocoa class.
It's easy for Cocoa to reference C++ objects. Going the other way is harder.
We have been using a linker class that has a void pointer to the Obj-C object
in the C++ header. We then cast it to a Cocoa object in the Obj-C++ source.
How are you setting the pointer ? If you are using ARC and want to get a strong reference on the object, you have to use __bridge_retained, which tell ARC to keep the reference valid.
Once you are done with the objc object, you have to release it.
Post by Casey McDermott
void *mCocoaPopupPtr = nil;
void GSCocoaPopupLinker::setCocoaFieldVisible(const BOOL inValue)
{
if (mCocoaPopupPtr != nil)
{
GSPopupButton *cocoaPopup = (__bridge GSPopupButton *)mCocoaPopupPtr;
[cocoaPopup setHidden : !inValue];
}
}
Problem is, with ARC turned on, the pointer is never nil, so it crashes.
The void pointer somehow becomes an NSAtom instead of 0.
There's very little documentation on NSAtom, but it appears to be Apple's way to
use the excess bits in a 64-bit address to store class info.
Is there some other way to test for an invalid void pointer?
Thanks,
Casey McDermott
TurtleSoft.com
_______________________________________________
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
https://lists.apple.com/mailman/options/cocoa-dev/mailing%40xenonium.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent
Casey McDermott
2018-09-08 14:32:14 UTC
Permalink
Post by Jean-Daniel
If you are using ARC and want to get a strong
reference on the object, you have to use __bridge_retained

That is handy to know! I see there is also __bridge_transfer to go the other way.

In this case we put the Cocoa controls into a NSView, which releases them.
The NSView deletes our LView, which deletes the C++ controls.

Thanks,

Casey McDermott
www.TurtleSoft.com
_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to ***@ml-in.narkive.net
Alastair Houghton
2018-09-10 09:09:20 UTC
Permalink
Post by Jean-Daniel
Post by Jean-Daniel
If you are using ARC and want to get a strong
reference on the object, you have to use __bridge_retained
That is handy to know! I see there is also __bridge_transfer to go the other way.
IMO the Core Foundation wrappers CFBridgingRetain( ) and CFBridgingRelease() are slightly easier to follow in code; I’ve never been a huge fan of having lots of double-underscore things floating about.

Kind regards,

Alastair.

--
http://alastairs-place.net

_______________________________________________

Cocoa-dev mailing list (Cocoa-***@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/gegs%40ml-in.narkive.net

This email sent to geg

Continue reading on narkive:
Loading...