-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add a safe API for NSValue and migrate NSValue value fetching to the size variants for validation #22265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a safe API for NSValue and migrate NSValue value fetching to the size variants for validation #22265
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,31 @@ ${ ObjectiveCBridgeableImplementationForNSValue("CGVector") } | |
${ ObjectiveCBridgeableImplementationForNSValue("CGSize") } | ||
${ ObjectiveCBridgeableImplementationForNSValue("CGAffineTransform") } | ||
|
||
extension NSValue { | ||
public func value<StoredType>(of type: StoredType.Type) -> StoredType? { | ||
if StoredType.self is AnyObject.Type { | ||
let encoding = String(cString: objCType) | ||
// some subclasses of NSValue can return @ and the default initialized variant returns ^v for encoding | ||
guard encoding == "^v" || encoding == "@" else { | ||
return nil | ||
} | ||
return nonretainedObjectValue as? StoredType | ||
} else if _isPOD(StoredType.self) { | ||
var storedSize = 0 | ||
var storedAlignment = 0 | ||
NSGetSizeAndAlignment(objCType, &storedSize, &storedAlignment) | ||
guard MemoryLayout<StoredType>.size == storedSize && MemoryLayout<StoredType>.alignment == storedAlignment else { | ||
return nil | ||
} | ||
let allocated = UnsafeMutablePointer<StoredType>.allocate(capacity: 1) | ||
defer { allocated.deallocate() } | ||
if #available(OSX 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) { | ||
getValue(allocated, size: storedSize) | ||
} else { | ||
getValue(allocated) | ||
} | ||
return allocated.pointee | ||
} | ||
return nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little concerned about returning struct Foo {
let name: String
let value: Any
} but not be able to unbox it safely this way? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is, but it is also fundamentally unsafe to do it, since String is not a POD. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you cant really safely box that in the first place tbh, that is something that should be addressed later There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, the crux of that example was actually meant to be the It would be nicer to be less restrictive on fetching values out and more restrictive on stuffing values in, but I guess we can do that in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's practically possible for someone to have ended up with an already-NSValue-boxed Foo. The Swift runtime won't ever box it in a |
||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels potentially dangerous to me. Are we absolutely guaranteed that the read from this address to copy the value out will happen prior to the deallocation?
I don't have a better answer, this is just triggering alarms in my head. Who can we ask about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning by value needs to make that copy so that it be returned; I'm not sure what sequence of events would cause the return out not to be filled before this defer is invoked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, the deallocation happens at the end of the scope, after the assignment of the return value to send back from my understanding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirming that this is guaranteed to do the right thing; Philippe is correct.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I figured as much. I just wanted to be absolutely certain.