Swift's Practical Combat Skills
From OC to Swift, the difference is quite big. This article records that I went from OC to Swift. OC Turn to some of the problems I encountered in Swift development process, and then write down the problems I encountered. In general, some of the techniques under Swift language are helpful to those in need.
- Processing of OC Call Method
To call OC, we need to add @objc tag, general action-target processing method, notification processing method and so on.
@objc func onRefresh(){ self.refreshCallback?() }
- Processing SEL selectors
Use method types such as # selector (method name)
eg.
`#selector(self.onRefresh))`
A more detailed introduction can be found in this article: http://swifter.tips/selector/
Here's an example of adding callback handlers to mj_header and mj_footer using MJRefresh
self.mj_header.setRefreshingTarget(self, refreshingAction: #selector(self.onRefresh)) self.mj_footer.setRefreshingTarget(self, refreshingAction: #selector(self.onLoadMore))
- Use of try keyword
Methods that may have exceptions use try? Methods for optional catch exceptions
let jsonStr=try?String(contentsOfFile: jsonPath!)
- Class object parameters and class object parameter values
AnyClass is the formal parameter of the method, and the class name. self (modelClass.self) is the argument.
func registerCellNib(nib:UINib,modelClass:AnyClass){ self.register(nib, forCellReuseIdentifier: String(describing:modelClass.self)) } ... self.tableView?.registerCellNib(nib: R.nib.gameCell(), modelClass: GameModel.self)
- Interthreaded calls
The main thread uses DispatchQueue.main, and the global sub-thread uses DispatchQueue.global(), and the methods can use sync, async, asyncAfter, and so on.
Here is an example of interthread invocation of network requests
let _ = URLSession.shared.dataTask(with: url, completionHandler: { [weak self] (data, response, error) in guard let weakSelf = self else { return } if error == nil { if let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) { let data = json as! [Any] DispatchQueue.main.async { weakSelf.suggestions = data[1] as! [String] if weakSelf.suggestions.count > 0 { weakSelf.tableView.reloadData() weakSelf.tableView.isHidden = false } else { weakSelf.tableView.isHidden = true } } } } }).resume()
- Use weak to prevent looping references in closures
URLSession.shared.dataTask(with: requestURL) {[weak self] (data, response, error) in guard let weakSelf = self else { return } weakSelf.tableView.reloadData() }
- Escape closure and non-escape closure
Escape closure, which is called escape closure only after method execution is completed, usually asynchronous processing time-consuming tasks are done in the method. After the task is completed, the result is called escape closure by callback processing, which needs to be modified with the @escaping keyword.
Non-escaping closures, called before method execution is complete, are called escaping closures, such as those used by the snapkit framework that are processed after method execution is complete.
Closures after Swift3 default to non-escape( @noescape The declaration cannot be displayed), and this type cannot be displayed with the @noescape keyword modifier
// Simulated network requests, completion closures are asynchronously delayed, so you need to add `escaping'to modify them. class func fetchVideos(completion: @escaping (([Video]) -> Void)) { DispatchQueue.global().async { let video1 = Video.init(title: "What Does Jared Kushner Believe", channelName: "Nerdwriter1") let video2 = Video.init(title: "Moore's Law Is Ending. So, What's Next", channelName: "Seeker") let video3 = Video.init(title: "What Bill Gates is afraid of", channelName: "Vox") var items = [video1, video2, video3] items.shuffle() DispatchQueue.main.asyncAfter(deadline: DispatchTime.init(uptimeNanoseconds: 3000000000), execute: { completion(items) }) } }
- Packaging of Notification.Name
Notification name in swift3 is a special Notification.Name type. enum is used for encapsulation processing, and an extension of Notification Center is created to handle the sending of notification messages.
// Define Notification.Name enumeration enum YTNotification: String { case scrollMenu case didSelectMenu case openPage case hideBar var stringValue: String { return "YT" + rawValue } // Enumeration members return the corresponding Notification.Name type var notificationName: NSNotification.Name { return Notification.Name.init(stringValue) } } extension NotificationCenter { func yt_post(custom notification: YTNotification, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil) { self.post(name: notification.notificationName, object: anObject, userInfo: aUserInfo) } }
Usage method
Adding notification observers uses the Notification.Name type value returned by notificationName of the YTNotification enumeration member
Messages are sent using YTNotification enumeration members
// Add Notification Observation NotificationCenter.default.addObserver(self, selector: #selector(self.changeTitle(notification:)), name: YTNotification.scrollMenu.notificationName, object: nil) // send message NotificationCenter.default.yt_post(custom: YTNotification.scrollMenu, object: nil, userInfo: ["length": scrollIndex])
Lazy lazy lazy loading attributes that initialize variables only when used
// The way closures are done let menuTitles = ["History", "My Videos", "Notifications", "Watch Later"] lazy var menuItems : [MenuItem] = { var tmenuItems = [MenuItem]() for menuTitle in menuTitles { let menuItem = MenuItem(iconImage: UIImage.init(named: menuTitle)!, title: menuTitle) tmenuItems.append(menuItem) } return tmenuItems }() // In the ordinary way, lazy var titles = ["A", "B"]
- Type Judgment
Use is to determine the type and use if-let and as?
// MARK:- Examples of type checking let sa = [ Chemistry(physics: "Solid State Physics", equations: "hertz"), Maths(physics: "fluid dynamics", formulae: "Gigahertz"), Chemistry(physics: "Thermophysics", equations: "Decibel"), Maths(physics: "Astrophysics", formulae: "Megahertz"), Maths(physics: "differential equation", formulae: "Cosine series")] var chemCount = 0 var mathsCount = 0 for item in sa { // If it is an instance of Chemistry type, return true, instead return false. Equivalent to isKindOfClass if item is Chemistry { chemCount += 1 } else if item is Maths { mathsCount += 1 } } // Use if-let and as? To determine types for item in sa { // If it is an instance of Chemistry type, return true, instead return false. Equivalent to isKindOfClass if let _ = item as? Chemistry { chemCount += 1 } else if let _ = item as? Maths { mathsCount += 1 } }
Use switch-case and as to determine types
// Any can represent any type, including method type var exampleany = [Any]() exampleany.append(12) exampleany.append(3.14159) exampleany.append("Any Example") exampleany.append(Chemistry(physics: "Solid State Physics", equations: "Megahertz")) // Use switch-case and as to determine types for item2 in exampleany { switch item2 { case let someInt as Int: print("Integer value \(someInt)") case let someDouble as Double where someDouble > 0: print("Pi The value is \(someDouble)") case let someString as String: print("\(someString)") case let phy as Chemistry: print("theme '\(phy.physics)', \(phy.equations)") default: print("None") } }
- Swift uses KVC, and variables that perform KVC operations need to be tagged with @objc
class Feed: NSObject, HandyJSON { // Use KVC to add @objc keyword @objc var id = 0 var type = "" var payload: PayLoad? var user: PostUser? required override init() {} }
- CGRect-type operations in swift
The operation of CGRect type is simplified in swift. For example, there is an instance of CGRect type as frame. The following example illustrates the corresponding operation in OC. swift Next Grammar
OC | Swift |
---|---|
CGRectGetMaxX(frame) | frame.maxX |
CGRectGetMinY(frame) | frame.minY |
CGRectGetMidX(frame) | frame.midX |
CGRectGetWidth(frame) | frame.width |
CGRectGetHeight(frame) | frame.height |
CGRectContainsPoint(frame, point) | frame.contains(point) |
- Processing of Pointer in Swift
Detailed descriptions can be found in this article: http://swifter.tips/unsafe/
Here's an example of using the OC library RegexKitLite, where the return value is pointer type and needs to be converted to the corresponding swift object type
func composeAttrStr(text: String) -> NSAttributedString { // Rules of expression let emotionPattern = "\\[[0-9a-zA-Z\\u4e00-\\u9fa5]+\\]"; // @ Rules let atPattern = "@[0-9a-zA-Z\\u4e00-\\u9fa5-_]+"; // # Rules for Topic # let topicPattern = "#[0-9a-zA-Z\\u4e00-\\u9fa5]+#"; // Rules for url links let urlPattern = "\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^[:punct:]\\s]|/)))"; let pattern = "\(emotionPattern)|\(atPattern)|\(topicPattern)|\(urlPattern)" var textParts = [TextPart]() (text as! NSString).enumerateStringsMatched(byRegex: pattern) { (captureCount: Int, capString: UnsafePointer<NSString?>?, capRange: UnsafePointer<NSRange>?, stop: UnsafeMutablePointer<ObjCBool>?) in let captureString = capString?.pointee as! String let captureRange = capRange?.pointee as! NSRange let part = TextPart() part.text = captureString part.isSpecial = true part.range = captureRange textParts.append(part) } // ... }
- Protocols that can only be implemented by classes have a scenario. As a delegate, protocol needs to specify the type of delegate as ptotocol when it needs to be modified with weak keywords. This ptotocol needs to add class modifiers, such as the protocol below, because the object of class type only has reference counting, so there is weak. Concepts, struct type without reference count is without weak concept
/// Protocol for Interactive Use of ImageViewer and ImageCell protocol YTImageCellProtocol : class { // Cell's click event handles dismiss func imageCell(_ imageCell: YTImageCell, didSingleTap: Bool); }