Swift or not
Programmers have one advantage: If the tool is not easy to use, you can optimize it yourself. Swift makes this especially simple. It includes several features that allow you to expand and customize the language in a natural way.
In this article, I will share several examples of how Swift has improved my programming experience. I hope that after reading this article, you can understand the pain points of using this language and put them into practice. (Of course, you need to think about it first !)
Controversial duplicate identifier
The following is a situation that you are familiar with in Objective-C: enumeration values and string constants have long descriptions of detailed names:
label.textAlignment = NSTextAlignmentCenter;
(This reminds me of the motto of the middle school science class: Repeat the question when answering, or RQIA,How is text aligned? The text is centered and aligned.This is useful when the answer method exceeds the context, but it seems redundant in other cases .)
Swift reduces this redundancy because enumeration values can be accessed by type names + vertices. If you omit a type name, it can still be automatically inferred:
Label. textAlignment = NSTextAlignment. Center // more concise: label. textAlignment =. Center
But sometimes you don't use enumeration, but are trapped by a smelly and long constructor.
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
How many "timingfunctions" are there? Too much.
A fact that is not so well known is that the abbreviated Point Symbol is of any typeAnyAll static members are valid. Combined with the ability to add custom property in extension, we get the following code...
Extension CAMediaTimingFunction {// This attribute will be initialized upon first access. // (@ Nonobjc needs to be added to prevent the compiler from generating a dynamic accessor for the static (or final) attribute .) @ Nonobjc static let EaseInEaseOut = CAMediaTimingFunction (name: kCAMediaTimingFunctionEaseInEaseOut) // another option is to use the computing attribute, which is equally effective, // But * Every time * is accessed, the value is "static var EaseInEaseOut: CAMediaTimingFunction {//. init is self. return. init (name: kCAMediaTimingFunctionEaseInEaseOut )}}
Now we get an elegant shorthand:
animation.timingFunction = .EaseInEaseOut
Duplicate identifier in Context
The code used to process Core Graphics Context and color space is often lengthy.
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), CGColorCreate(CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), [0.792, 0.792, 0.816, 1]))
Use the extension of dimensions again:
extension CGContext{ static func currentContext() -> CGContext? { return UIGraphicsGetCurrentContext() }}extension CGColorSpace{ static let GenericRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)}CGContextSetFillColorWithColor(.currentContext(), CGColorCreate(.GenericRGB, [0.792, 0.792, 0.816, 1]))
Is it simpler? Obviously, there are more ways to expand the Core Graphics type to meet your needs.
Repeated identifier in Auto Layout
Does the following code look familiar?
spaceConstraint = NSLayoutConstraint( item: label, attribute: .Leading, relatedBy: .Equal, toItem: button, attribute: .Trailing, multiplier: 1, constant: 20)widthConstraint = NSLayoutConstraint( item: label, attribute: .Width, relatedBy: .LessThanOrEqual, toItem: nil, attribute: .NotAnAttribute, multiplier: 0, constant: 200)spaceConstraint.active = truewidthConstraint.active = true
It is quite difficult to understand, right? Apple recognizes this as a common problem, so it redesigned the new NSLayoutAnchorAPI (applicable on iOS9 and OS X 10.11) to handle this problem:
spaceConstraint = label.leadingAnchor.constraintEqualToAnchor(button.trailingAnchor, constant: 20)widthConstraint = label.widthAnchor.constraintLessThanOrEqualToConstant(200)spaceConstraint.active = truewidthConstraint.active = true
However, I think it can be better. In my opinion, the following code is easier to read and use than the built-in interface:
SpaceConstraint = label. constrain (. leading ,. equal, to: button ,. trailing, plus: 20) widthConstraint = label. constrain (. width ,. lessThanOrEqual, to: 200) // "sets the leading edge of the label to 20 apart from the trailing edge of the button" // "sets the width of the label to be less than or equal to 200. "
The above code is implemented by adding extension to UIView or NSView. These helper functions may seem a little poor, but they are very convenient to use and easy to maintain. (Here I have provided other parameters that contain default values-a multiplier, priority, and identifier-so you can choose to further customize the constraints .)
extension UIView{ func constrain( attribute: NSLayoutAttribute, _ relation: NSLayoutRelation, to otherView: UIView, _ otherAttribute: NSLayoutAttribute, times multiplier: CGFloat = 1, plus constant: CGFloat = 0, atPriority priority: UILayoutPriority = UILayoutPriorityRequired, identifier: String? = nil) -> NSLayoutConstraint { let constraint = NSLayoutConstraint( item: self, attribute: attribute, relatedBy: relation, toItem: otherView, attribute: otherAttribute, multiplier: multiplier, constant: constant) constraint.priority = priority constraint.identifier = identifier constraint.active = true return constraint } func constrain( attribute: NSLayoutAttribute, _ relation: NSLayoutRelation, to constant: CGFloat, atPriority priority: UILayoutPriority = UILayoutPriorityRequired, identifier: String? = nil) -> NSLayoutConstraint { let constraint = NSLayoutConstraint( item: self, attribute: attribute, relatedBy: relation, toItem: nil, attribute: .NotAnAttribute, multiplier: 0, constant: constant) constraint.priority = priority constraint.identifier = identifier constraint.active = true return constraint }}Hello ~ Operator
First, I must remind you that you must think twice before using a custom operator. They are easy to use, but may eventually get a bunch of code like shit. Be sure to be skeptical about the robustness of the code, and then you will find that in some scenarios, custom operators are indeed very useful.
Reload them
If you have developed functions related to drag gestures, you may have written the following code:
// Touch start/press the mouse: let touchPos = touch. locationInView (container) objectOffset = CGPoint (x: object. center. x-touchPos. x, y: object. center. y-touchPos. y) // touch and move/drag the mouse: let touchPos = touch. locationInView (container) object. center = CGPoint (x: touchPos. x + objectOffset. x, y: touchPos. y + objectOffset. y)
In this Code, we only perform simple addition and subtraction, but because CGPoint contains x and y, we have to write every expression twice. Therefore, we need some functions that simplify operations.
ObjectOffset indicates the distance between the touch position and the object position. The best way to describe this distance is not CGPoint, but a CGVector that is not so well known. It does not use x and y, but uses dx and dy to represent the distance or "deltas".
Therefore, it is logical to subtract two vertices to get a vector. In this way, we get an overload of the-OPERATOR:
///-Return: The vector from 'rhs 'to 'lhs. Func-(lhs: CGPoint, rhs: CGPoint)-> CGVector {return CGVector (dx: lhs. x-rhs. x, dy: lhs. y-rhs. y )}
Then, on the opposite side, add a vector and a vertex to get another vertex:
//-Return: a func + (lhs: CGPoint, rhs: CGVector) Point obtained after the 'lhs 'offset is 'rhs'-> CGPoint {return CGPoint (x: lhs. x + rhs. dx, y: lhs. y + rhs. dy )}
Now the following code looks very good!
// Touch start: objectOffset = object. center-touch. locationInView (container) // touch Mobile: object. center = touch. locationInView (container) + objectOffset
Exercise: Think about other operators that can be used on points and vectors and reload them.
Suggestion:-(CGPoint, CGVector), * (CGVector, CGFloat), and-(CGVector ).
Exclusive stunt
The following are some more creative content. Swift provides someCompound value assignment operatorThese operators assign values while executing an operation:
A + = B // equivalent to "a = a + B" a % = B // equivalent to "a = a % B"
However, there are still other operators that do not include the built-in compound assignment form. The most common example is ??, Null merge operator. That is, | = in Ruby. For example, first, implement a version that is assigned only when the variable is nil (or not. This is of great significance to the optional values in Swift and is easy to implement:
Infix operator ?? = {Associativity right precedence 90 assignment} // matches other value assignment operators. // If 'lhs 'is 'nil', assign the value of 'rhs 'to func ?? = (Inout lhs: T ?, @ Autoclosure rhs: ()-> T) {lhs = lhs ?? Rhs ()}
This code may seem complicated-here we have done the following.
The infix operator declaration is used to tell Swift ?? = As an operator. Use the generic function to support any type of value. Inout indicates that the Operation count @ autoclosure on the Left can be modified to support short-circuit assignment. If necessary, you can only assign values to the right. (It also depends on ?? Short-circuit support .)
However, in my opinion, the functions implemented by the above Code are very clear and easy to use:
A ?? = B // equivalent to "a = ?? B"
Scheduling
The best way to use GCD in Swift is to read the official documentation, but I will still introduce some basic knowledge points in this article. For more information, see this overview.
Swift 2 introduces Protocol extensions. Therefore, many of the previous global standard library functions have become quasi-member functions, such as map (seq, transform), which have become the current seq. map (transform), join (separator, seq) becomes the current seq. joinWithSeparator (separator) and so on. In this caseStrictly speakingFunctions that do not belong to the class instance method can still be used. symbol access, but also reduces the number of commas (PS: the original text is parentheses, may be the author's mistake), so that the Code will not be too messy.
However, such changes do not apply to free functions outside the Swift standard library, such as dispatch_async () and UIImageJPEGRepresentation (). These functions are still very difficult to use. If you often use them, it is worth thinking about how to use Swift to help you transform them. Below are some examples of getting started GCD.
Sync or non-sync
These are all very simple; let's start:
Extension dispatch_queue_t {final func async (block: dispatch_block_t) {dispatch_async (self, block)} // The 'block' here must be @ noescape, but cannot be linked like this:
Final func sync (block: dispatch_block_t) {dispatch_sync (self, block )}}
The two simplified functions directly call common scheduling functions, but they can be called through the. symbol, which we previously could not do.
Note: GCD objects are exported to Swift in an odd way. Although they can be implemented in a class way, dispatch_queue_t is actually just a protocol. Here I mark both functions as final to demonstrate our intention: we do not want to use dynamic scheduling here, although in my opinion it is good to use protocol extension in this case, but do not use it anywhere.
mySerialQueue.sync { print("I’m on the queue!") threadsafeNum++}dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0).async { expensivelyReticulateSplines() print("Done!") dispatch_get_main_queue().async { print("Back on the main queue.") }}
A further version of sync is inspired by the with * family in the Swift standard library function. What we need to do is to return a result calculated in the closure:
Extension dispatch_queue_t {final func sync
(Block: ()-> Result {var result: Result? Dispatch_sync (self) {result = block ()} return result! } // Capture some data on the serial queue. let currentItems = mySerialQueue. sync {print ("I'm on the queue! ") Return mutableItems. copy ()}
Group Thinking
The other two simple extensions allow us to use dispatch group very well:
extension dispatch_queue_t{ final func async(group: dispatch_group_t, _ block: dispatch_block_t) { dispatch_group_async(group, self, block) }}extension dispatch_group_t{ final func waitForever() { dispatch_group_wait(self, DISPATCH_TIME_FOREVER) }}
Now you can include an additional group parameter when calling async.
let group = dispatch_group_create()concurrentQueue.async(group) { print("I’m part of the group")}concurrentQueue.async(group) { print("I’m independent, but part of the same group")}group.waitForever()print("Everything in the group has now executed")
Note: We can easily select group. async (queue) or queue. async (group ). You can use either of them to fully view yourself-or you can even implement both.
Elegant redefinition
If your project includes both Objective-C and Swift, you may encounter a situation where the API of Obj-C does not look so Swift. NS_REFINED_FOR_SWIFT is needed to save us.
In Obj-C, it is normal to use functions, methods, and variables marked with (new in Xcode 7) macros, but after export to Swift, they will contain a "_" prefix.
@ Interface MyClass: NSObject // @ returns the subscript of @ c. If not provided, NSNotFound is returned. -(NSUInteger) indexOfThing :( id) thing NS_REFINED_FOR_SWIFT; @ end // when exported to Swift, it becomes: public class MyClass: NSObject {public func _ indexOfThing (thing: anyObject)-> UInt}
Now put the Obj-C method aside, you can reuse the same name to provide a more friendly Swift version API (the implementation method is usually to call the original version with the prefix ):
Extension MyClass {//-return: Given the subscript of 'the', if not, return 'nil '. Func indexOfThing (thing: AnyObject)-> Int? {Let idx = Int (_ indexOfThing (thing) // call the original method if idx = NSNotFound {return nil} return idx }}
Now you can be satisfied with the "if let!
Further steps
Swift is still very young, and its code libraries have different styles. A large number of third-party mini libraries have emerged. The Code of these libraries reflects the author's different views on operators, helper functions, and naming conventions. In this case, you need to be more picky when dealing with dependencies and establishing specifications in the team.
The most important reason for using the technology in this article is not to write the latest and coolest Swift code. Of course, the person responsible for maintaining your code-maybe in the future-may have different opinions. For them, dear readers, You need to extend Swift to make the code clearer and more reasonable, rather than simply to make the code simpler.