Skip to content

[SR-6139] Implement NSString.copy() method #1291

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

Merged
merged 1 commit into from
Nov 28, 2017
Merged

[SR-6139] Implement NSString.copy() method #1291

merged 1 commit into from
Nov 28, 2017

Conversation

sashabelonogov
Copy link
Contributor

@sashabelonogov sashabelonogov commented Nov 1, 2017

A potential fix for this issue:
https://bugs.swift.org/browse/SR-6139

Before, the method that is responsible to create the immutable copy of NSString was simply
returning self, that’s why copying didn’t happen and this test was failing:

func test_constructorWithCopyItems() {
        let foo = "foo" as NSMutableString

        let array1 = NSArray(array: [foo], copyItems: false)
        let array2 = NSArray(array: [foo], copyItems: true)

        XCTAssertEqual(array1[0] as! String, "foo")
        XCTAssertEqual(array2[0] as! String, "foo")

        foo.append("1")
        XCTAssertEqual(array1[0] as! String, "foo1")
        // The below test fails on sclf
        XCTAssertEqual(array2[0] as! String, "foo")
    }

Please, let me know what you think.

@spevans
Copy link
Contributor

spevans commented Nov 1, 2017

@swift-ci please test

@sashabelonogov
Copy link
Contributor Author

sashabelonogov commented Nov 1, 2017

I apologize for a failed build, I rushed with solving merge conflicts in a test and missed one "}" in the end. I fixed it in the same commit. Everything should be fine now.

@ianpartridge
Copy link
Contributor

@swift-ci please test

1 similar comment
@ianpartridge
Copy link
Contributor

@swift-ci please test

@spevans
Copy link
Contributor

spevans commented Nov 2, 2017

@sashabelonogov Could you also uncomment the following lines in TestFoundation/TestNSArray.swift

//foo.append("1")
//XCTAssertEqual(array1[0] as! String, "foo1")
//XCTAssertEqual(array2[0] as! String, "foo")

As this was the original failing test case so checking it now works would be good, thanks.

@sashabelonogov
Copy link
Contributor Author

@spevans sure! I also deleted my test because it's identical to the existing one.

@spevans
Copy link
Contributor

spevans commented Nov 2, 2017

@swift-ci please test

1 similar comment
@spevans
Copy link
Contributor

spevans commented Nov 2, 2017

@swift-ci please test

alblue
alblue previously requested changes Nov 2, 2017
Copy link
Contributor

@alblue alblue left a comment

Choose a reason for hiding this comment

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

Minor changes - can you make them, then rebase against master and squash changes before resubmitting?

throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadInapplicableStringEncoding.rawValue, userInfo: [
"NSDebugDescription" : "Unable to create a string using the specified encoding."
])
throw NSError(domain: NSCocoaErrorDomain,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you revert this reformatting, since it isn't really relevant or part of this change?

@@ -46,7 +46,7 @@ class TestNSArray : XCTestCase {
("test_mutableCopying", test_mutableCopying),
("test_writeToFile", test_writeToFile),
("test_initWithContentsOfFile", test_initWithContentsOfFile),
("test_readWriteURL", test_readWriteURL)
("test_readWriteURL", test_readWriteURL),
Copy link
Contributor

Choose a reason for hiding this comment

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

Although in principle having a trailing comma makes for less invasive changes in the future, since you aren't actually adding anything in this line then it's not relevant for this commit. Can you revert this line change and then squash the commits?

@@ -1169,6 +1170,17 @@ class TestNSString : XCTestCase {
let s6 = NSString(string: "Beyonce\u{301} and Tay")
XCTAssertEqual(s6.substring(with: NSMakeRange(7, 9)), "\u{301} and Tay")
}

func test_createCopy() {
//SR-6139
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment isn't particularly relevant - as long as the SR-6139 is mentioned in the commit message, people will be able to identify that it came from this bug in the first place. Can you remove this comment?

@sashabelonogov
Copy link
Contributor Author

sashabelonogov commented Nov 2, 2017

@alblue, makes sense, sure.
The code is updated, branch is rebased and commits are squashed. Thank you for pointing it out.

}
let result = NSString()
result._storage = self._storage
return result
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't make a lot of sense to me. Why shouldn't an immutable NSString be allowed to return self for a copy?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@parkera you're absolutely right, I didn't think about it. I updated the commit adding the case of the immutable NSString copy, what do you think about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

even though this method will not do what the name of it says, it makes sense to return self for performance reasons

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 the best answer here is going to depend on if we can get a factory pattern proposal through swift-evolution... the way it works in the Darwin Foundation today is that when you use any of NSString's init methods, you're most likely getting a subclass of NSString, which implements copy by returning self.

For now, I think we should do this:

  1. if type(of: self) === NSString.self then return self
  2. else, use one of NSString's other public initializers to create a new instance with the contents.
  3. Don't assume result._storage exists, because a subclass would not have access to that and may not use it to hold the contents

@parkera parkera self-requested a review November 2, 2017 19:48
Copy link
Contributor

@parkera parkera left a comment

Choose a reason for hiding this comment

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

This would have some pretty serious performance concerns.

@sashabelonogov
Copy link
Contributor Author

I updated the code handling the case of the immutable NString.copy()

@alblue alblue dismissed their stale review November 7, 2017 14:59

Points have been actioned

@alblue
Copy link
Contributor

alblue commented Nov 7, 2017

Seems to be OK for me now. @parkera is the proposed approach with the copy done correctly, or do we have to worry about other immutable string implementations like CF implementations?

@sashabelonogov
Copy link
Contributor Author

@parkera thanks for updates, that makes total sense, I didn't think about subclasses. I updated it to this:

    open func copy(with zone: NSZone? = nil) -> Any {
        if type(of: self) === NSString.self {
            return self
        }
        if type(of: self) === NSMutableString.self {
            if let contents = _fastContents {
                return NSString(characters: contents, length: length)
            }
            let result = NSString()
            result._storage = _storage
            return result
        }
        let characters = UnsafeMutablePointer<unichar>.allocate(capacity: length)
        getCharacters(characters, range: NSMakeRange(0, length))
        let result = NSString(characters: characters, length: length)
        characters.deinitialize()
        characters.deallocate(capacity: length)
        return result
    }

What do you think? Or should I rather remove the _storage usage at all? I can also replace it with NSString(string: self._bridgeToSwift()) that will do pretty much the same.

@parkera
Copy link
Contributor

parkera commented Nov 7, 2017

The part about NSMutableString should be in NSMutableString itself (subclasses need to override this to provide behavior that works for them, until we have factory patterns).

The rest looks fine, I think.

@sashabelonogov
Copy link
Contributor Author

sashabelonogov commented Nov 7, 2017

The NSMutableString part is deleted. I can't disagree with "subclasses need to override this to provide behavior that works for them". It's especially interesting to notice that most of the similar methods for other classes are implemented for both cases: mutable and immutable. Thanks for the feedback!

@alblue
Copy link
Contributor

alblue commented Nov 10, 2017

@swift-ci please test

@sashabelonogov
Copy link
Contributor Author

@parkera I'm sorry for disturbing you but what is the status with this PR? I updated it with all the changes requested by you.

@parkera
Copy link
Contributor

parkera commented Nov 28, 2017

Thanks for the ping. We should merge this.

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.

5 participants