[swift-evolution] @NSCopying currently does not affect initializers
Torin Kwok
torin at kwok.im
Sat Jan 28 23:47:06 CST 2017
Yep, I also admit the design of forbidding calling a setter before full
class initialization is reasonable and what's really annoying is the
inconsistency.
However, making @NSCopying attribute not subjects to the fact that
setters would not be invoked in initializers perhaps is viable too. In
the other words, assigning a value to a property whether or not by
calling a setter has no influence on whether @NSCopying semantic'd work:
copying should always take place after a property has been declared as
@NSCopying.
Jean-Daniel writes:
>> Le 28 janv. 2017 à 05:34, Torin Kwok via swift-evolution <swift-evolution at swift.org> a écrit :
>>
>> Hello guys,
>>
>> Note: This issue has been originally presented inswift-usersmailling list <https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20170123/004552.html>. And then I post it again here at the suggestion <https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20170123/004561.html> of Jordan Rose:
>>
>> It might be reasonable to change this behavior, but it probably deserves a bit of discussion on swift-evolution; it's not 100%, for-sure a bug.
>> --- the original content follows this line ---
>>
>> I encountered a strange behavior when I declared a property with the @NSCopying attribute:
>>
>> // `Person` class inherits from `NSObject` class and conforms to `NSCopying` protocol
>> @NSCopying var employee: Person
>> and then assigned an external instance of Person class protocol to this property within the designated init methods:
>>
>> // Designated initializer of `Department` class
>> init( employee externalEmployee: Person ) {
>> self.employee = externalEmployee
>> super.init()
>>
>> // Assertion would fail since Swift do not actually copy the value assigned to this property
>> // even though `self.employee` has been marked as `@NSCoyping`
>> // assert( self.employee !== externalEmployee )
>> }
>> If I indeed require the deep copying behavior during the init process, instead of taking advantage of @NSCopying attribute, I would have to invoke the copy() method manually:
>>
>> init( employee externalEmployee: Person ) {
>> // ...
>> self.employee = externalEmployee.copy() as! Person
>> // ...
>> }
>> In fact, what really makes me confusing is that @NSCopying semantic does work properly within the other parts of the class definition such as normal instance methods, or external scope. For instance, if we're assigning an external instance of Person to the self.employee proper of Department directly through setter rather than initializer:
>>
>> department.employee = johnAppleseed
>> then self.employee property and johnAppleseed variable will no longer share the same underlying object now. In the other words, @NSCopying attribute makes sense.
>>
>> After I looked through a great deal of results given by Google, and dicussions on StackOverflow, I finally end up with nothing helpful — the vast majority of articles, documentations as well as issues talking about this similar topics only focus on the basic concepts and effects of @NSCopying itself but do not mentioned this strange behavior at all — besides one radar descriping the same problem (rdar://21383959 <rdar://21383959>) and a final conclusion mentioned in a guy's Gist comment: ... values set during initialization are not cloned ...
>>
>> That is, @NSCopying semantic has no effect in initializers.
>>
>> Then, what I want to figure out is the reason why @NSCopying semantic will become effectless implicitly whithin initializers of a class, and the special considerations behind this behavior, if any.
>>
>> --- END ---
>>
>> Jordan:
>>
>> Your observation is correct: @NSCopying currently does not affect initializers. This is because accessing a property in an initializer always does direct access to the storage rather than going through the setter.
>> I have tested the identical logic in Objective-C and the NSCopying semantic works perfectly within Obj-C's class initializer.
>>
> This is because Obj-C guarantee that all ivars are zero initialized and does not enforce initializer safety (but forcing initialization of ivars before calling other methods).
>
> Calling a setter (like any other method) before full class initialization is unsafe as the setter may be overridden or simply customized, and may need to access to the class or subclasses ivars.
>
> That said, I’m not sure what is the best way to solve that inconsistency.
--
Torin Kwok (郭桐)
OpenPGP/GnuPG: https://keybase.io/kwok
More information about the swift-evolution
mailing list