Skip to content
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

[ASPrimitiveTraitCollection] Always treat preferredContentSize as a potential nil #trivial #757

Merged
merged 3 commits into from
Mar 21, 2018

Conversation

ypogribnyi
Copy link
Contributor

As ASPrimitiveTraitCollection is a struct, the application can always initialize it in its own way, without using Texture-provided initialization functions. This may lead to preferredContentSize field being nil, which, in turn, may lead to frustrating crashes when the struct is given to Texture.

While simply advising the user to always use the provided initialization functions should solve the problem, it's better to always treat this structure as possibly improperly initialized and guard against nil value in this field.

Additionally this PR explicitly marks all UIContentSizeCategory usage in ASTraitCollection class as non-null.

Add nil-value checks for preferredContentSizeCategory.
@ay8s
Copy link
Collaborator

ay8s commented Jan 19, 2018

It seems better now when changing device orientation @ypogribnyi. Is there documentation around initializing ASPrimitiveTraitCollection? Might be missing something.

We were seeing this on iOS 10 & 11.

@ypogribnyi
Copy link
Contributor Author

The preferred way to create ASPrimitiveTraitCollection is to use ASPrimitiveTraitCollectionMakeDefault() or ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection*). There wasn't any documentation about this, but I added a comment to ASPrimitiveTraitCollection definition in this PR (not sure if that's enough).

By the way, why do we expose two trait collections interfaces (ASPrimitiveTraitCollection and ASTraitCollection) to the user? Might it be better to hide one of them?

@nguyenhuy
Copy link
Member

By the way, why do we expose two trait collections interfaces (ASPrimitiveTraitCollection and ASTraitCollection) to the user? Might it be better to hide one of them?

Good questions! Based on facebookarchive/AsyncDisplayKit#1592, I think the original reason is to have ASTraitCollection as a friendly object-based API and ASPrimitiveTraitCollection as the internal actual struct that is used throughout the framework. We should at least document this reasoning, and/or move ASPrimitiveTraitCollection to a framework-private interface.

Copy link
Member

@nguyenhuy nguyenhuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ypogribnyi Great reasoning on the possibility that ASPrimitiveTraitCollection may be initialized manually, and defend against such case.

@ay8s This PR should mitigate your crash, although I'd suggest to dig a bit deeper into your codebase and fix the root cause (initialize ASPrimitiveTraitCollection using the provided functions or better yet, switch to ASTraitCollection). Let us know what you find!

@@ -175,7 +181,7 @@ AS_SUBCLASSING_RESTRICTED
@property (nonatomic, assign, readonly) UIUserInterfaceStyle userInterfaceStyle;
#endif

@property (nonatomic, assign, readonly) UIContentSizeCategory preferredContentSizeCategory;
@property (nonatomic, assign, readonly) UIContentSizeCategory _Nonnull preferredContentSizeCategory;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need _Nonnull annotations since we already use NS_ASSUME_NONNULL_BEGIN at the beginning of this file (here). It's always good to be defensive and guard against unexpected values in the implementation though.

} else {
return AS_UIContentSizeCategoryUnspecified();
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, feel free to ignore this: It's a bit shorter to use ternary operator here, i.e:

return sizeCategory ? sizeCategory : AS_UIContentSizeCategoryUnspecified();

@@ -92,7 +100,7 @@ ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() {
.displayGamut = UIDisplayGamutUnspecified,
.userInterfaceIdiom = UIUserInterfaceIdiomUnspecified,
.layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified,
.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(AS_UIContentSizeCategoryUnspecified()),
.preferredContentSizeCategory = AS_UIContentSizeCategoryUnspecified(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we remove the ASPrimitiveContentSizeCategoryMake in here. If you read the comment above ASPrimitiveContentSizeCategory we have to use ASPrimitiveContentSizeCategoryMake

* Mark [ASViewController overrideDisplayTraitsWithWindowSize] as deprecated.

Code review changes:
* Remove unneeded nonnull annotations
* Add null check in ASTraitCollection constructor implementation
* Codestyle
* Add some documentation about ASPrimitiveTraitCollection vs ASTraitCollection usage
@ay8s
Copy link
Collaborator

ay8s commented Jan 23, 2018

Thanks @ypogribnyi. We don't actually initialize either ASPrimitiveCollectionTraits or ASTraitCollection ourselves.

I wonder if this might help with digging into the cause http://crashes.to/s/058c9ea3e54

@ypogribnyi
Copy link
Contributor Author

@ay8s, thank you for your input! I finally managed to reproduce the crash. Looks like it occurs in ASViewControllers that are used without a node (i.e. not using -[ASViewController initWithNode:]). Could you please confirm if that is your case?

I'm reproducing it on master in ASDKgrams UIKit tab. It crashes after device rotation when trying to log the trait collection fetched with

ASPrimitiveTraitCollection traitCollection = _node.primitiveTraitCollection;

while having nil in _node.

Although having a nil node in a view controller is perfectly valid and fully documented behaviour, I didn't expect that at all.

Anyways, this PR should fix the problem.

@ay8s
Copy link
Collaborator

ay8s commented Jan 26, 2018

Yup that'd be it! Thanks @ypogribnyi!

@@ -32,6 +32,14 @@ ASDISPLAYNODE_INLINE UIContentSizeCategory AS_UIContentSizeCategoryUnspecified()
}
}

ASDISPLAYNODE_INLINE ASPrimitiveContentSizeCategory safePrimitiveContentSizeCategory(ASPrimitiveContentSizeCategory sizeCategory) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still should prefix this functions with AS

@@ -92,7 +100,7 @@ ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() {
.displayGamut = UIDisplayGamutUnspecified,
.userInterfaceIdiom = UIUserInterfaceIdiomUnspecified,
.layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified,
.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(AS_UIContentSizeCategoryUnspecified()),
.preferredContentSizeCategory = AS_UIContentSizeCategoryUnspecified(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we remove the ASPrimitiveContentSizeCategoryMake in here. If you read the comment above ASPrimitiveContentSizeCategory we have to use ASPrimitiveContentSizeCategoryMake

@@ -32,6 +32,14 @@ ASDISPLAYNODE_INLINE UIContentSizeCategory AS_UIContentSizeCategoryUnspecified()
}
}

ASDISPLAYNODE_INLINE ASPrimitiveContentSizeCategory safePrimitiveContentSizeCategory(ASPrimitiveContentSizeCategory sizeCategory) {
return sizeCategory ? sizeCategory : AS_UIContentSizeCategoryUnspecified();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in both cases we have to use ASPrimitiveContentSizeCategoryMake(AS_UIContentSizeCategoryUnspecified())

Remove safePrimitiveContentSizeCategory in favour of AS_safeContentSizeCategory.
Copy link
Member

@nguyenhuy nguyenhuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for dropping the ball here, @ypogribnyi. LGTM now. Thanks!

@nguyenhuy nguyenhuy merged commit e954b10 into TextureGroup:master Mar 21, 2018
bernieperez pushed a commit to AtomTickets/Texture that referenced this pull request Apr 25, 2018
…otential nil #trivial (TextureGroup#757)

* Fix ASPrimitiveTraitCollection initialization on iOS 9.
Add nil-value checks for preferredContentSizeCategory.

* * Mark -[ASTraitCollection init] as deprecated.
* Mark [ASViewController overrideDisplayTraitsWithWindowSize] as deprecated.

Code review changes:
* Remove unneeded nonnull annotations
* Add null check in ASTraitCollection constructor implementation
* Codestyle
* Add some documentation about ASPrimitiveTraitCollection vs ASTraitCollection usage

* Rename safeContentSizeCategory to AS_safeContentSizeCategory.

Remove safePrimitiveContentSizeCategory in favour of AS_safeContentSizeCategory.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants