<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## Objective-C代碼 ### 一、布局規范 #### 1、頭文件引入 按照一定順序引入頭文件 ``` #import <系統庫> #import <第三方> #import "項目類" ``` #### 2、屬性 和 私有變量 public 和 非public 屬性建議放在相應的地方 ``` @interface ClassA () @property (nonatomic,strong) NSString *name; @end ``` 私有變量建議命名遵循其中一個,為了規避iOS的命名,特性個人推薦第二種方法命名私有變量 ``` NSString *_title; NSString *title_; ``` #### 3、類內部布局 ``` #pragma mark - lifeCycle (這部分寫生命周期方法) #pragma mark - <UITableViewDelegate> (系統的delegate方法) #pragma mark - <CustomDelegate> (自定義的delegate方法) #pragma mark - event response (交互方法) #pragma mark - private methods (非公共方法) #pragma mark - view getter(lazy) (主要是View懶加載) #pragma mark - view setter #pragma mark - data getter #pragma mark - data setter ``` 這個根據具體類的情況去自己可以增刪 對于private methods 和 event response 等等只是在本類中調用的方法建議以p_開頭,以后修改的時候能了解修改會造成的影響范圍 例如: ``` #pragma mark -private methods - (void)p_didSelectQuestionItem:(NSDictionary *)questionItem #pragma mark -event response - (void)p_searchEventClick:(UIButton *)button ``` ### 二、注釋 > 添加必要的注釋 > * 所有的property 需要給出注釋; > * 所有自定義的方法需要給出注釋; > * 比較大的代碼塊需要給出注釋; > * 所有代碼中出現的阿拉伯數字需要給出注釋; > * 程序中出現加密/解密 邏輯的操作地方,需要給出注釋說明過程(無論是系統還是自定義)。 #### 1、每個類頭部有自己的獨白 比如一個viewController是用來做什么的 可以首先寫個注釋說明下 其他的類雷同 ``` /** 這是一個用來測試注釋的viewController 注意點: 設計思路: 等等吧 你想說的告白都在這里 */ #import "ViewController.h" @interface ViewController () @end @implementation ViewController { } - (void)viewDidLoad { [super viewDidLoad]; } @end ``` #### 2、單行,多行注釋 單行注釋 //+說明 ``` // 初始化默認數據 [self p_setupDefaultData]; ``` 多行 /* */ ``` /*! XXX的狀態 - XXXType_1: 孩子找媽媽中 - XXXType_1: 孩子找爸爸中 - XXXType_1: 孩子找小朋友中 */ typedef NS_ENUM(NSUInteger,XXXType) { XXXType_1, XXXType_2, XXXType_3, }; ``` 方法注釋 Commond+option+/ ``` /** 向某人默認打招呼 @param name 某人 */ - (void)sayHelloToPeople:(NSString *)name{ } ``` TODO標記 //TODO:XXX 用//TODO去做一些值得注意 或者 自己未完成的標志 ``` - (void)sayHelloToPeople:(NSString *)name{ //TODO:向某人打招呼邏輯 } ``` ### 三、一些縮進 #### 1、縮進 方法之間保留一行空行, 方法和屬性的格式請按照以下格式書寫: ``` - (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath; ``` ``` - (void)viewDidLoad { [super viewDidLoad]; } ``` ``` @property (nonatomic, strong, readonly, nullable) NSIndexPath *previouslyFocusedIndexPath; ``` ### 四、常見注意點: - 盡量避免在程序中直接出現常數,使用超過一次的應以宏定義或枚舉的形式來替代。 - 常數的宏定義應與它實際使用時的類型相一致。如以3.0來定義浮點類型,用3表示整型。常量的命名應當能夠表達出它的用途,并且用大寫字母表示。例如:#definePI 3.1415926 - 刪除多余的空行、注釋、方法以及未被使用的資源文件。 ``` UIKIT_EXTERN NSNotificationName const UIKeyboardWillShowNotification; ``` ``` UIKIT_EXTERN NSString *const UIKeyboardFrameBeginUserInfoKey ``` ``` typedef NS_ENUM(NSInteger, NSWritingDirectionFormatType) { NSWritingDirectionEmbedding = (0 << 1), NSWritingDirectionOverride = (1 << 1) } ``` ``` typedef NSString * NSAttributedStringDocumentReadingOptionKey ``` --- --- ## Swift 編程規范 > 良好的編程規范有利于項目的維護,同時也能避免因人為的疏忽產生的錯誤。本篇編碼規范主要參考了 [swift-style-guide](https://github.com/raywenderlich/swift-style-guide/blob/master/README.markdown)和[The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)當中的規范,并綜合了實際開發中總結的經驗。 ### 正確性 (Correctness) 力爭讓你的代碼沒有編譯警告(`warning`)。這個規則下延伸出了許多決定,如:使用`#selector`方式,而不是直接使用字符串。 ### 命名 (Naming) 描述性和一致性的命名使得代碼更易讀、更易懂。一些規則如下: * 追求調用方的清晰性 * 優先使用更清晰的命名,而不是更簡潔的 * 使用駝峰樣式 * 類型、協議名首字母大寫,其他的都首字母小寫 * 包含所有需要的單詞,省略不必要的 * 使用基于角色的命名,而不是基于類型的 * 工廠方法使用`make`開頭 * 對方法的命名 * 動詞方法以`-ed`結尾,對于不可變的(`non-mutating`)動詞方法用`-ing`結尾 * 布爾類型(`boolean`)應該以`is`開頭 * 用于描述事物的協議(`protocols`)名稱,用名詞命名 * 用于描述能力的協議(`protocols`)名稱,應以`-able` 或 `-ible`結尾 * 不要用生僻的單詞 * 通常不要用縮寫 * 選用好的參數名來起到描述的作用 #### 類前綴 (Class Prefixes) Swift中的類型自動使用了其所在的模塊(`module`)作為命名空間(`namespace`),所以你不必給類型加前綴。如果來自不同模塊的類型名子沖突,可以顯示的使用模塊名作為調用前綴來避免沖突。 ```swift import SomeModule import OtherModule let someUsefulClass = SomeModule.UsefulClass() let otherUsefulClass = OtherModule.UsefulClass() ``` #### 代理 (Delegates) 當定義一個代理方法時,第一個匿名參數應該是代理的源對象。(`UIKit`中有許多這樣的的例子) **推薦:** ```swift func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String) func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool ``` **不推薦:** ```swift /// `namePicker`應該是匿名的 func didSelectName(namePicker: NamePickerViewController, name: String) /// 至少應包含`_ namePickerView: NamePickerView`參數,來作為代理的源對象 func namePickerShouldReload() -> Bool ``` #### 使用可類型推導的上下文 (Use Type Inferred Context) 利用好編譯器的類型推導特性,來寫出簡短、清晰的代碼。 **推薦:** ```swift let selector = #selector(viewDidLoad) view.backgroundColor = .red let toView = context.view(forKey: .to) let view = UIView(frame: .zero) ``` **不推薦:** ```swift // 以下幾種寫法,都沒有用到編譯器的類型推導特性,寫出來的代碼較冗余 let selector = #selector(ViewController.viewDidLoad) view.backgroundColor = UIColor.red let toView = context.view(forKey: UITransitionContextViewKey.to) let view = UIView(frame: CGRect.zero) ``` #### 泛型 (Generics) 泛型參數(`generic type parameters`)應當使用具有描述性的駝峰樣式來命名。當泛型參數不具有明確的關系或角色時,可使用一個大寫的字母表示即可。如:`T`,`U`,`V` #### 使用的語言 (Language) 應使用美式英語的拼寫方式以匹配Apple的API,應盡量保持命名的可讀性,不應該用多含義且有相反或者混淆意思的單詞。 **推薦:** ```swift let color = "red" ``` **不推薦:** ```swift let colour = "red" ``` ### 代碼組織 (Code Organization) 盡可能的使用擴展(`extensions`)來解耦你的代碼,將代碼劃分到不同的擴展模塊中,每個擴展應以`// MARK: -`開頭,來更好的區分擴展。 #### 協議的遵守 (Protocol Conformance) 具體來講,當讓一個`Model`遵守某個協議時,推薦添加一個獨立的`Model`擴展(`extensions`)來遵守該協議。這樣使得相關的協議方法能組織在一起。 **推薦:** ```swift class MyViewController: UIViewController { // class stuff here } // MARK: - UITableViewDataSource extension MyViewController: UITableViewDataSource { // table view data source methods } // MARK: - UIScrollViewDelegate extension MyViewController: UIScrollViewDelegate { // scroll view delegate methods } ``` **不推薦:** ```swift class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate { // all methods } ``` 對于`UIKit`的視圖控制器(`view controllers`),可以考慮將生命周期(`lifecycle`)相關、自定義訪問器(`custom accessors`)、`IBAction`獨立不同的到類擴展中。 #### 無用的代碼 (Unused Code) 無用的代碼包括Xcode產生的模板代碼、占位的注釋等、方法的默認實現僅僅是調用`super`等,這些都應當移除掉。 **推薦:** ```swift override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return Database.contacts.count } ``` **不推薦:** ```swift override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func numberOfSections(in tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return Database.contacts.count } ``` #### 減小引入 (Minimal Imports) 保存最小的引入(`imports`)。例如, 當只使用`Foundation`時,不要引入`UIKit`。 ### 空格 (Spacing) * 代碼折行使用4個空格(`spaces`) * 方法的大括號和其他大括號(`if`/`else`/`switch`/`while`等),其左括號必須要和語句在同一行,并且右括號要換行; * 變量類型和冒號(:)之間保留一個空格; * 返回值標記`->`兩側各保留一個空格; **推薦:** ```swift if user.isHappy { // Do something } else { // Do something else } ``` **不推薦:** ```swift if user.isHappy { // Do something } else { // Do something else } ``` * 各個方法之間必須有一個空行,這使得代碼視覺上更清晰。 * 方法的實現中,應當適當的添加空行來劃分功能。過多的空行意味著你應該拆分這些功能到不同的方法中,通過這樣來避免一個巨大的方法。 * 通常冒號(Colons)的左邊應當沒有空格,而在右邊有一個空格。 例外: 1. 三目運算符(`ternary operator`) `? :` ```swift let foo = isEnable ? "A" : "B" ``` 2. 空字典(`empty dictionary `) `[:]` ```swift let bar: [String: Int] = [:] ``` 3. `#selector`語法的無參方法`(_:)` ```swift let sel = #selector((_:)) ``` **推薦:** ```swift /// 注意使用空格的位置 class TestDatabase: Database { var data: [String: CGFloat] = ["A": 1.2, "B": 3.2] } ``` **不推薦:** ```swift class TestDatabase : Database { var data :[String:CGFloat] = ["A" : 1.2, "B":3.2] } ``` ### 注釋 (Comments) * 給方法或屬性添加注釋,使用`option + command + /`來讓Xcode自動生成 * 給關鍵邏輯添加一些局部注釋 * 注釋要保持最新狀態 ### 類和結構體 (Classes and Structures) **用哪個呢?** 要知道結構體(`struct`)是值類型,當事物不具有唯一性時,使用結構體。比如,一個數組`[a, b, c]`,那么另一個數組`[a, b, c]`就跟它是一樣的,他們之間可以互為替換,這時應當使用結構體來表示。 類(`class`)是引用類型,當事物具有唯一性或有明確的生命周期時,使用類。比如,一個電話簿列表,它的每一條記錄都是唯一的,這時就要用類來表示。 以下是一個類的定義: ```swift class Circle: Shape { var x: Int var y: Int var radius: Double var diameter: Double { get { return radius * 2 } set { radius = newValue / 2 } } init(x: Int, y: Int, radius: Double) { self.x = x self.y = y self.radius = radius } convenience init(x: Int, y: Int, diameter: Double) { self.init(x: x, y: y, radius: diameter / 2) } override func area() -> Double { return Double.pi * radius * radius } } extension Circle: CustomStringConvertible { var description: String { return "center = \(_centerString) area = \(area())" } private var _centerString: String { return "(\(x),\(y))" } } ``` 上面的代碼遵循了以下規范: * 屬性、變量、常量、參數等語句的定義,都是在冒號后添加一個空格。如:`x: Int`、 `Circle: Shape` * 方法之間保留一個空行 * 有默認的修飾符(如:`internal`)時不用重新添加。同樣的,重載(`override`)方法時,不用重新添加訪問修飾符`access modifier ` * 將功能組織整理到擴展中(`extensions`) * 隱藏一些不必公開的實現細節,如`_centerString`屬性用`private`來修飾。 #### 使用self (Use of Self) 在Swift中,可以省略`self`來訪問一個對象的屬性或方法。但為了消除使用的歧義,建議使用`self`來訪問其屬性或方法。 #### 計算屬性 (Computed Properties) 為了簡潔性,如果一個計算屬性是只讀(`read-only`)的,那么應省略`get { ... }`語句。`get { ... }`語句只在計算屬性是讀寫(`read-write`)時,才要求使用。 **推薦:** ```swift var diameter: Double { return radius * 2 } ``` **不推薦:** ```swift var diameter: Double { get { return radius * 2 } } ``` #### 使用final標記 (Final) 出于某些原因,你可能不希望一個類被繼承,這時你可以將其標記為`final`來表示它不能被繼承。例如:一個單例,可能就不希望被繼承。 ### 函數聲明 (Function Declarations) 將簡短的函數聲明保留在一行中,包括左括號。 ```swift func reticulateSplines(spline: [Double]) -> Bool { /* code goes here */ } ``` 無返回值的函數可以省略`-> Void`返回值 ```swift func foo(arg: Int) { /* code goes here */ } ``` 參數較多的函數可以像`Objective-C`中一樣進行折行處理 ```swift func foo(arg1: Int, arg2: Double, arg3: String, arg4: [Bool], arg5: () -> Void) { /* code goes here */ } ``` ### 閉包表達式 (Closure Expressions) 當參數列表末尾只有一個閉包參數時,才應使用尾隨閉包(`trailing closure`)語法。 **推薦:** ```swift // 參數列表尾部只有一個閉包參數 UIView.animate(withDuration: 1.0) { self.myView.alpha = 0 } // 參數列表尾部有多個閉包參數 UIView.animate(withDuration: 1.0, animations: { self.myView.alpha = 0 }, completion: { finished in self.myView.removeFromSuperview() }) ``` **不推薦:** ```swift UIView.animate(withDuration: 1.0, animations: { self.myView.alpha = 0 }) UIView.animate(withDuration: 1.0, animations: { self.myView.alpha = 0 }) { finished in self.myView.removeFromSuperview() } ``` 對于單行的閉包,可以利用隱式返回(`implicit returns`)的特性: ```swift list.sort { a, b in a > b } ``` 對于使用閉包的鏈式調用,應當讓代碼更清晰、更易讀。可以借助空格(`spacing`)、換行(`line breaks`)和匿名參數(`anonymous arguments`)等方法來讓代碼更清晰、更易讀,但這都依賴于你的選擇。 ```swift let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90) let value = numbers .map {$0 * 2} .filter {$0 > 50} .map {$0 + 10} ``` ### 類型 (Types) 應盡量使用`Swift`的原生類型,當然`Swift`提供了對`Objective-C`類型的橋接方法,你可以使用`Objective-C的所有方法。 **推薦:** ```swift let width = 120.0 // Double let widthString = (width as NSNumber).stringValue // String ``` **不推薦:** ```swift let width: NSNumber = 120.0 // NSNumber let widthString: NSString = width.stringValue // NSString ``` #### 常量 (Constants) 常量使用關鍵字`let`定義,變量使用關鍵字`var`定義。除非值可變,否則都應使用關鍵字`let`定義。 在一個類型的內部,通過關鍵字`static let`來定義靜態常量,這樣可以更好的組織這些靜態常量。 **推薦:** ```swift /// 將所有的接口都定義在一個類型內部,方便外部使用 struct API { static let homeAPI: String = "https://www.example.com/home/" static let mineAPI: String = "https://www.example.com/mine/" } enum Math { static let e = 2.718281828459045235360287 static let pi = 3.14159265358979323846264 } let l = 2 * Math.pi * r ``` **不推薦:** ```swift let homeAPI: String = "https://www.example.com/home/" let mineAPI: String = "https://www.example.com/mine/" let e = 2.718281828459045235360287 let pi = 3.14159265358979323846264 let l = 2 * Math.pi * r ``` 類內部的靜態方法和屬性,有點類似于全局方法和屬性。但應盡量避免使用全局方法和屬性。有一些例外情況,比如當使用到`runtime`的`objc_getAssociatedObject()`函數時,需要定義個全局的key來作為參數: ```swift extension UIView { var foo: Int? { return objc_getAssociatedObject(self, &_fooKey) as? Int } } private var _fooKey: Void? ``` #### 可選類型 (Optionals) 當使用`?`來定義可選類型時,表明它可以接受為`nil`的值。 當使用`!`來定義可選類型時,表明它可以接受為`nil`的值,但必須保證在使用它時,值不為`nil`。就像`ViewController`中的`view`在`viewDidLoad`被調用時就已經創建完成。 使用可選綁定(`optional binding`)來一次性解包單個或多個可選類型值。 **推薦:** ```swift var foo: Int? var bar: Bool? if let foo = foo, let bar = bar { // 同時解包foo和bar后才會執行這里 } ``` **不推薦:** ```swift var foo: Int? var bar: Bool? if let foo = foo { if let bar = bar { // 同時解包foo和bar后才會執行這里 } } ``` #### 懶加載 (Lazy Initialization) 使用懶加載(`lazy initialization`)來延遲初始化,這是一個很好的特性。如: ```swift // 對象不需要進行配置 lazy var foo: SomeBigType = SomeBigType() // 對象需要進行配置時,使用{ ... }()這種形式 lazy var bar: OtherBigType = { [unowned self] in let obt = OtherBigType() obt.delegate = self return obt }() ``` **注意:** > 上面代碼中使用到了`self`,所以需要使用`[unowned self]`來避免產生循環引用(`retain cycle`) #### 類型推導 (Type Inference) 對于局部變量,盡量使用類型推導來讓代碼更緊湊。而對于成員變量來說,應盡量不要使用類型推導來讓類的定義更清晰。 **推薦:** ```swift struct Box: Shape { var width: Int = 10 var height: Int = 10 var foo: [Int] = [] var bar: [String: Int] = [:] func scale(rate: Int) { let width = self.width * rate let height = self.height * rate self.width = width self.height = height } } ``` **不推薦:** ```swift struct Box: Shape { var width = 10 var height = 10 var foo = [Int]() var bar = [String: Int]() func scale(rate: CGFloat) { let width: Int = self.width * rate let height: Int = self.height * rate self.width = width self.height = height } } ``` #### 語法糖 (Syntactic Sugar) 利用語法糖,使用更簡短的聲明方式: **推薦:** ```swift var deviceModels: [String] var employees: [Int: String] var faxNumber: Int? ``` **不推薦:** ```swift var deviceModels: Array<String> var employees: Dictionary<Int, String> var faxNumber: Optional<Int> ``` ### 內存管理 (Memory Management) 可以通過使用`weak` 和 `unowned`來避免循環引用,但也可以直接使用值類型(`struct`, `enum`)來避免循環引用。 通過`guard let`的形式來產生`strongSelf`: **推薦:** ```swift resource.request().onComplete { [weak self] response in guard let strongSelf = self else { return } let model = strongSelf.updateModel(response) strongSelf.updateUI(model) } ``` **不推薦:** ```swift // 當self的釋放先于onComplete回調時,可能引起崩潰 resource.request().onComplete { [unowned self] response in let model = self.updateModel(response) self.updateUI(model) } ``` **不推薦:** ```swift // 當self的釋放介于updateModel()和updateUI()方法之間時,可能會出現意想不到的情況 resource.request().onComplete { [weak self] response in let model = self?.updateModel(response) self?.updateUI(model) } ``` ### 訪問控制 (Access Control) 一般來說,被標記為`private`、`fileprivate`的屬性或方法都應以下劃線(`_`)開頭。 **推薦:** ```swift private var _isEnabled: Bool fileprivate var _isClosed: Bool private func _foo() { // code goes here } fileprivate func _bar() { // code goes here } ``` **不推薦:** ```swift private var isEnabled: Bool fileprivate var isClosed: Bool private func foo() { // code goes here } fileprivate func bar() { // code goes here } ``` 但例外情況是,當標記被修飾為`private(set)`、`fileprivate(set)`時,不需要下劃線(`_`)開頭,因為他們都是可訪問的屬性,只是他們都是只讀屬性而已,如: ```swift private(set) var isEnabled: Bool fileprivate(set) var isClosed: Bool ``` 一般將訪問控制標記(`access control annotation`)放在聲明的最前面,但例外情況是,當有屬性標記`@IBAction`、`@IBOutlet`、`@discardableResult`、`@objc`時,需要將屬性標記放在最前面。 ```swift private let _message = "Great Scott!" class TimeMachine { @IBOutlet private var _lbTitle: UILabel! @objc private func _foo() -> Bool { // code goes here } } ``` ### 控制流 (Control Flow) 優先使用 `for-in` 的方式而不用`while`。 **推薦:** ```swift for _ in 0..<3 { print("Hello three times") } for (index, person) in attendeeList.enumerated() { print("\(person) is at position #\(index)") } for index in stride(from: 0, to: items.count, by: 2) { print(index) } for index in (0...3).reversed() { print(index) } ``` **不推薦:** ```swift var i = 0 while i < 3 { print("Hello three times") i += 1 } var i = 0 while i < attendeeList.count { let person = attendeeList[i] print("\(person) is at position #\(i)") i += 1 } ``` ### 使用Guard語句 通過使用`guard`來避免使用`if`時代碼塊嵌套過深的問題。 **推薦:** ```swift func check(phone: String?, name: String?, age: Int) -> Bool { guard let ph = phone, ph.count > 0 else { return false } guard let nm = name, nm.count > 0 else { return false } guard age > 0 else { return false } return true } ``` **推薦:** ```swift func check(phone: String?, name: String?, age: Int) -> Bool { // 把對phone、name、age的判斷放到一個guard里,更簡潔 guard let ph = phone, ph.count > 0, let nm = name, nm.count > 0, age > 0 else { return false } return true } ``` **不推薦:** ```swift func check(phone: String?, name: String?, age: Int) -> Bool { if let ph = phone, ph.count > 0 { if let nm = name, nm.count > 0 { if age > 0 { return true } else { return false } } else { return false } } else { return false } } ``` ### 分號 (Semicolons) 在Swift中不要求必須加分號,一般情況下你不需要使用分號。只有當你希望將多行代碼寫在一行時,才需要加分號來斷句。 ### 圓括號 (Parentheses) 一般情況下,在類似`if`條件、`for`循環等控制語句中不需要加圓括號。只有當你在進行數學運算時,希望代碼可讀性更高時才需要適當的添加圓括號。 **推薦:** ```swift let result = ((x * y) + 2) / h ``` **不推薦:** ```swift let result = (x * y + 2) / h ``` ### 引用 * [swift-style-guide](https://github.com/raywenderlich/swift-style-guide/blob/master/README.markdown) * [The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)# 凡普信iOS代碼規范V1.0 --- > 凡普信iOS開發端代碼規范 --- ## Objective-C代碼規范 ??? <mark> <br>把布局放在了第一位感覺這個是最重要的,其他的規范感覺大家平時都已經注意了,所以放在了后面 <br> </mark> ??? ### 一、布局規范 #### 1、頭文件引入 按照一定順序引入頭文件 ``` #import <系統庫> #import <第三方> #import "項目類" ``` #### 2、屬性 和 私有變量 public 和 非public 屬性建議放在相應的地方 ``` @interface ClassA () @property (nonatomic,strong) NSString *name; @end ``` 私有變量建議命名遵循其中一個,為了規避iOS的命名,特性個人推薦第二種方法命名私有變量 ``` NSString *_title; NSString *title_; ``` #### 3、類內部布局 ``` #pragma mark - lifeCycle (這部分寫生命周期方法) #pragma mark - <UITableViewDelegate> (系統的delegate方法) #pragma mark - <CustomDelegate> (自定義的delegate方法) #pragma mark - event response (交互方法) #pragma mark - private methods (非公共方法) #pragma mark - view getter(lazy) (主要是View懶加載) #pragma mark - view setter #pragma mark - data getter #pragma mark - data setter ``` 這個根據具體類的情況去自己可以增刪 對于private methods 和 event response 等等只是在本類中調用的方法建議以p_開頭,以后修改的時候能了解修改會造成的影響范圍 例如: ``` #pragma mark -private methods - (void)p_didSelectQuestionItem:(NSDictionary *)questionItem #pragma mark -event response - (void)p_searchEventClick:(UIButton *)button ``` ### 二、注釋 > 添加必要的注釋 > * 所有的property 需要給出注釋; > * 所有自定義的方法需要給出注釋; > * 比較大的代碼塊需要給出注釋; > * 所有代碼中出現的阿拉伯數字需要給出注釋; > * 程序中出現加密/解密 邏輯的操作地方,需要給出注釋說明過程(無論是系統還是自定義)。 #### 1、每個類頭部有自己的獨白 比如一個viewController是用來做什么的 可以首先寫個注釋說明下 其他的類雷同 ``` /** 這是一個用來測試注釋的viewController 注意點: 設計思路: 等等吧 你想說的告白都在這里 */ #import "ViewController.h" @interface ViewController () @end @implementation ViewController { } - (void)viewDidLoad { [super viewDidLoad]; } @end ``` #### 2、單行,多行注釋 單行注釋 //+說明 ``` // 初始化默認數據 [self p_setupDefaultData]; ``` 多行 /* */ ``` /*! XXX的狀態 - XXXType_1: 孩子找媽媽中 - XXXType_1: 孩子找爸爸中 - XXXType_1: 孩子找小朋友中 */ typedef NS_ENUM(NSUInteger,XXXType) { XXXType_1, XXXType_2, XXXType_3, }; ``` 方法注釋 Commond+option+/ ``` /** 向某人默認打招呼 @param name 某人 */ - (void)sayHelloToPeople:(NSString *)name{ } ``` TODO標記 //TODO:XXX 用//TODO去做一些值得注意 或者 自己未完成的標志 ``` - (void)sayHelloToPeople:(NSString *)name{ //TODO:向某人打招呼邏輯 } ``` ### 三、一些縮進 #### 1、縮進 方法之間保留一行空行, 方法和屬性的格式請按照以下格式書寫: ``` - (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath; ``` ``` - (void)viewDidLoad { [super viewDidLoad]; } ``` ``` @property (nonatomic, strong, readonly, nullable) NSIndexPath *previouslyFocusedIndexPath; ``` ### 四、常見注意點: - 盡量避免在程序中直接出現常數,使用超過一次的應以宏定義或枚舉的形式來替代。 - 常數的宏定義應與它實際使用時的類型相一致。如以3.0來定義浮點類型,用3表示整型。常量的命名應當能夠表達出它的用途,并且用大寫字母表示。例如:#definePI 3.1415926 - 刪除多余的空行、注釋、方法以及未被使用的資源文件。 ``` UIKIT_EXTERN NSNotificationName const UIKeyboardWillShowNotification; ``` ``` UIKIT_EXTERN NSString *const UIKeyboardFrameBeginUserInfoKey ``` ``` typedef NS_ENUM(NSInteger, NSWritingDirectionFormatType) { NSWritingDirectionEmbedding = (0 << 1), NSWritingDirectionOverride = (1 << 1) } ``` ``` typedef NSString * NSAttributedStringDocumentReadingOptionKey ``` --- --- ## Swift 編程規范 > 良好的編程規范有利于項目的維護,同時也能避免因人為的疏忽產生的錯誤。本篇編碼規范主要參考了 [swift-style-guide](https://github.com/raywenderlich/swift-style-guide/blob/master/README.markdown)和[The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)當中的規范,并綜合了實際開發中總結的經驗。 ### 正確性 (Correctness) 力爭讓你的代碼沒有編譯警告(`warning`)。這個規則下延伸出了許多決定,如:使用`#selector`方式,而不是直接使用字符串。 ### 命名 (Naming) 描述性和一致性的命名使得代碼更易讀、更易懂。一些規則如下: * 追求調用方的清晰性 * 優先使用更清晰的命名,而不是更簡潔的 * 使用駝峰樣式 * 類型、協議名首字母大寫,其他的都首字母小寫 * 包含所有需要的單詞,省略不必要的 * 使用基于角色的命名,而不是基于類型的 * 工廠方法使用`make`開頭 * 對方法的命名 * 動詞方法以`-ed`結尾,對于不可變的(`non-mutating`)動詞方法用`-ing`結尾 * 布爾類型(`boolean`)應該以`is`開頭 * 用于描述事物的協議(`protocols`)名稱,用名詞命名 * 用于描述能力的協議(`protocols`)名稱,應以`-able` 或 `-ible`結尾 * 不要用生僻的單詞 * 通常不要用縮寫 * 選用好的參數名來起到描述的作用 #### 類前綴 (Class Prefixes) Swift中的類型自動使用了其所在的模塊(`module`)作為命名空間(`namespace`),所以你不必給類型加前綴。如果來自不同模塊的類型名子沖突,可以顯示的使用模塊名作為調用前綴來避免沖突。 ```swift import SomeModule import OtherModule let someUsefulClass = SomeModule.UsefulClass() let otherUsefulClass = OtherModule.UsefulClass() ``` #### 代理 (Delegates) 當定義一個代理方法時,第一個匿名參數應該是代理的源對象。(`UIKit`中有許多這樣的的例子) **推薦:** ```swift func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String) func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool ``` **不推薦:** ```swift /// `namePicker`應該是匿名的 func didSelectName(namePicker: NamePickerViewController, name: String) /// 至少應包含`_ namePickerView: NamePickerView`參數,來作為代理的源對象 func namePickerShouldReload() -> Bool ``` #### 使用可類型推導的上下文 (Use Type Inferred Context) 利用好編譯器的類型推導特性,來寫出簡短、清晰的代碼。 **推薦:** ```swift let selector = #selector(viewDidLoad) view.backgroundColor = .red let toView = context.view(forKey: .to) let view = UIView(frame: .zero) ``` **不推薦:** ```swift // 以下幾種寫法,都沒有用到編譯器的類型推導特性,寫出來的代碼較冗余 let selector = #selector(ViewController.viewDidLoad) view.backgroundColor = UIColor.red let toView = context.view(forKey: UITransitionContextViewKey.to) let view = UIView(frame: CGRect.zero) ``` #### 泛型 (Generics) 泛型參數(`generic type parameters`)應當使用具有描述性的駝峰樣式來命名。當泛型參數不具有明確的關系或角色時,可使用一個大寫的字母表示即可。如:`T`,`U`,`V` #### 使用的語言 (Language) 應使用美式英語的拼寫方式以匹配Apple的API,應盡量保持命名的可讀性,不應該用多含義且有相反或者混淆意思的單詞。 **推薦:** ```swift let color = "red" ``` **不推薦:** ```swift let colour = "red" ``` ### 代碼組織 (Code Organization) 盡可能的使用擴展(`extensions`)來解耦你的代碼,將代碼劃分到不同的擴展模塊中,每個擴展應以`// MARK: -`開頭,來更好的區分擴展。 #### 協議的遵守 (Protocol Conformance) 具體來講,當讓一個`Model`遵守某個協議時,推薦添加一個獨立的`Model`擴展(`extensions`)來遵守該協議。這樣使得相關的協議方法能組織在一起。 **推薦:** ```swift class MyViewController: UIViewController { // class stuff here } // MARK: - UITableViewDataSource extension MyViewController: UITableViewDataSource { // table view data source methods } // MARK: - UIScrollViewDelegate extension MyViewController: UIScrollViewDelegate { // scroll view delegate methods } ``` **不推薦:** ```swift class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate { // all methods } ``` 對于`UIKit`的視圖控制器(`view controllers`),可以考慮將生命周期(`lifecycle`)相關、自定義訪問器(`custom accessors`)、`IBAction`獨立不同的到類擴展中。 #### 無用的代碼 (Unused Code) 無用的代碼包括Xcode產生的模板代碼、占位的注釋等、方法的默認實現僅僅是調用`super`等,這些都應當移除掉。 **推薦:** ```swift override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return Database.contacts.count } ``` **不推薦:** ```swift override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func numberOfSections(in tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return Database.contacts.count } ``` #### 減小引入 (Minimal Imports) 保存最小的引入(`imports`)。例如, 當只使用`Foundation`時,不要引入`UIKit`。 ### 空格 (Spacing) * 代碼折行使用4個空格(`spaces`) * 方法的大括號和其他大括號(`if`/`else`/`switch`/`while`等),其左括號必須要和語句在同一行,并且右括號要換行; * 變量類型和冒號(:)之間保留一個空格; * 返回值標記`->`兩側各保留一個空格; **推薦:** ```swift if user.isHappy { // Do something } else { // Do something else } ``` **不推薦:** ```swift if user.isHappy { // Do something } else { // Do something else } ``` * 各個方法之間必須有一個空行,這使得代碼視覺上更清晰。 * 方法的實現中,應當適當的添加空行來劃分功能。過多的空行意味著你應該拆分這些功能到不同的方法中,通過這樣來避免一個巨大的方法。 * 通常冒號(Colons)的左邊應當沒有空格,而在右邊有一個空格。 例外: 1. 三目運算符(`ternary operator`) `? :` ```swift let foo = isEnable ? "A" : "B" ``` 2. 空字典(`empty dictionary `) `[:]` ```swift let bar: [String: Int] = [:] ``` 3. `#selector`語法的無參方法`(_:)` ```swift let sel = #selector((_:)) ``` **推薦:** ```swift /// 注意使用空格的位置 class TestDatabase: Database { var data: [String: CGFloat] = ["A": 1.2, "B": 3.2] } ``` **不推薦:** ```swift class TestDatabase : Database { var data :[String:CGFloat] = ["A" : 1.2, "B":3.2] } ``` ### 注釋 (Comments) * 給方法或屬性添加注釋,使用`option + command + /`來讓Xcode自動生成 * 給關鍵邏輯添加一些局部注釋 * 注釋要保持最新狀態 ### 類和結構體 (Classes and Structures) **用哪個呢?** 要知道結構體(`struct`)是值類型,當事物不具有唯一性時,使用結構體。比如,一個數組`[a, b, c]`,那么另一個數組`[a, b, c]`就跟它是一樣的,他們之間可以互為替換,這時應當使用結構體來表示。 類(`class`)是引用類型,當事物具有唯一性或有明確的生命周期時,使用類。比如,一個電話簿列表,它的每一條記錄都是唯一的,這時就要用類來表示。 以下是一個類的定義: ```swift class Circle: Shape { var x: Int var y: Int var radius: Double var diameter: Double { get { return radius * 2 } set { radius = newValue / 2 } } init(x: Int, y: Int, radius: Double) { self.x = x self.y = y self.radius = radius } convenience init(x: Int, y: Int, diameter: Double) { self.init(x: x, y: y, radius: diameter / 2) } override func area() -> Double { return Double.pi * radius * radius } } extension Circle: CustomStringConvertible { var description: String { return "center = \(_centerString) area = \(area())" } private var _centerString: String { return "(\(x),\(y))" } } ``` 上面的代碼遵循了以下規范: * 屬性、變量、常量、參數等語句的定義,都是在冒號后添加一個空格。如:`x: Int`、 `Circle: Shape` * 方法之間保留一個空行 * 有默認的修飾符(如:`internal`)時不用重新添加。同樣的,重載(`override`)方法時,不用重新添加訪問修飾符`access modifier ` * 將功能組織整理到擴展中(`extensions`) * 隱藏一些不必公開的實現細節,如`_centerString`屬性用`private`來修飾。 #### 使用self (Use of Self) 在Swift中,可以省略`self`來訪問一個對象的屬性或方法。但為了消除使用的歧義,建議使用`self`來訪問其屬性或方法。 #### 計算屬性 (Computed Properties) 為了簡潔性,如果一個計算屬性是只讀(`read-only`)的,那么應省略`get { ... }`語句。`get { ... }`語句只在計算屬性是讀寫(`read-write`)時,才要求使用。 **推薦:** ```swift var diameter: Double { return radius * 2 } ``` **不推薦:** ```swift var diameter: Double { get { return radius * 2 } } ``` #### 使用final標記 (Final) 出于某些原因,你可能不希望一個類被繼承,這時你可以將其標記為`final`來表示它不能被繼承。例如:一個單例,可能就不希望被繼承。 ### 函數聲明 (Function Declarations) 將簡短的函數聲明保留在一行中,包括左括號。 ```swift func reticulateSplines(spline: [Double]) -> Bool { /* code goes here */ } ``` 無返回值的函數可以省略`-> Void`返回值 ```swift func foo(arg: Int) { /* code goes here */ } ``` 參數較多的函數可以像`Objective-C`中一樣進行折行處理 ```swift func foo(arg1: Int, arg2: Double, arg3: String, arg4: [Bool], arg5: () -> Void) { /* code goes here */ } ``` ### 閉包表達式 (Closure Expressions) 當參數列表末尾只有一個閉包參數時,才應使用尾隨閉包(`trailing closure`)語法。 **推薦:** ```swift // 參數列表尾部只有一個閉包參數 UIView.animate(withDuration: 1.0) { self.myView.alpha = 0 } // 參數列表尾部有多個閉包參數 UIView.animate(withDuration: 1.0, animations: { self.myView.alpha = 0 }, completion: { finished in self.myView.removeFromSuperview() }) ``` **不推薦:** ```swift UIView.animate(withDuration: 1.0, animations: { self.myView.alpha = 0 }) UIView.animate(withDuration: 1.0, animations: { self.myView.alpha = 0 }) { finished in self.myView.removeFromSuperview() } ``` 對于單行的閉包,可以利用隱式返回(`implicit returns`)的特性: ```swift list.sort { a, b in a > b } ``` 對于使用閉包的鏈式調用,應當讓代碼更清晰、更易讀。可以借助空格(`spacing`)、換行(`line breaks`)和匿名參數(`anonymous arguments`)等方法來讓代碼更清晰、更易讀,但這都依賴于你的選擇。 ```swift let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90) let value = numbers .map {$0 * 2} .filter {$0 > 50} .map {$0 + 10} ``` ### 類型 (Types) 應盡量使用`Swift`的原生類型,當然`Swift`提供了對`Objective-C`類型的橋接方法,你可以使用`Objective-C的所有方法。 **推薦:** ```swift let width = 120.0 // Double let widthString = (width as NSNumber).stringValue // String ``` **不推薦:** ```swift let width: NSNumber = 120.0 // NSNumber let widthString: NSString = width.stringValue // NSString ``` #### 常量 (Constants) 常量使用關鍵字`let`定義,變量使用關鍵字`var`定義。除非值可變,否則都應使用關鍵字`let`定義。 在一個類型的內部,通過關鍵字`static let`來定義靜態常量,這樣可以更好的組織這些靜態常量。 **推薦:** ```swift /// 將所有的接口都定義在一個類型內部,方便外部使用 struct API { static let homeAPI: String = "https://www.example.com/home/" static let mineAPI: String = "https://www.example.com/mine/" } enum Math { static let e = 2.718281828459045235360287 static let pi = 3.14159265358979323846264 } let l = 2 * Math.pi * r ``` **不推薦:** ```swift let homeAPI: String = "https://www.example.com/home/" let mineAPI: String = "https://www.example.com/mine/" let e = 2.718281828459045235360287 let pi = 3.14159265358979323846264 let l = 2 * Math.pi * r ``` 類內部的靜態方法和屬性,有點類似于全局方法和屬性。但應盡量避免使用全局方法和屬性。有一些例外情況,比如當使用到`runtime`的`objc_getAssociatedObject()`函數時,需要定義個全局的key來作為參數: ```swift extension UIView { var foo: Int? { return objc_getAssociatedObject(self, &_fooKey) as? Int } } private var _fooKey: Void? ``` #### 可選類型 (Optionals) 當使用`?`來定義可選類型時,表明它可以接受為`nil`的值。 當使用`!`來定義可選類型時,表明它可以接受為`nil`的值,但必須保證在使用它時,值不為`nil`。就像`ViewController`中的`view`在`viewDidLoad`被調用時就已經創建完成。 使用可選綁定(`optional binding`)來一次性解包單個或多個可選類型值。 **推薦:** ```swift var foo: Int? var bar: Bool? if let foo = foo, let bar = bar { // 同時解包foo和bar后才會執行這里 } ``` **不推薦:** ```swift var foo: Int? var bar: Bool? if let foo = foo { if let bar = bar { // 同時解包foo和bar后才會執行這里 } } ``` #### 懶加載 (Lazy Initialization) 使用懶加載(`lazy initialization`)來延遲初始化,這是一個很好的特性。如: ```swift // 對象不需要進行配置 lazy var foo: SomeBigType = SomeBigType() // 對象需要進行配置時,使用{ ... }()這種形式 lazy var bar: OtherBigType = { [unowned self] in let obt = OtherBigType() obt.delegate = self return obt }() ``` **注意:** > 上面代碼中使用到了`self`,所以需要使用`[unowned self]`來避免產生循環引用(`retain cycle`) #### 類型推導 (Type Inference) 對于局部變量,盡量使用類型推導來讓代碼更緊湊。而對于成員變量來說,應盡量不要使用類型推導來讓類的定義更清晰。 **推薦:** ```swift struct Box: Shape { var width: Int = 10 var height: Int = 10 var foo: [Int] = [] var bar: [String: Int] = [:] func scale(rate: Int) { let width = self.width * rate let height = self.height * rate self.width = width self.height = height } } ``` **不推薦:** ```swift struct Box: Shape { var width = 10 var height = 10 var foo = [Int]() var bar = [String: Int]() func scale(rate: CGFloat) { let width: Int = self.width * rate let height: Int = self.height * rate self.width = width self.height = height } } ``` #### 語法糖 (Syntactic Sugar) 利用語法糖,使用更簡短的聲明方式: **推薦:** ```swift var deviceModels: [String] var employees: [Int: String] var faxNumber: Int? ``` **不推薦:** ```swift var deviceModels: Array<String> var employees: Dictionary<Int, String> var faxNumber: Optional<Int> ``` ### 內存管理 (Memory Management) 可以通過使用`weak` 和 `unowned`來避免循環引用,但也可以直接使用值類型(`struct`, `enum`)來避免循環引用。 通過`guard let`的形式來產生`strongSelf`: **推薦:** ```swift resource.request().onComplete { [weak self] response in guard let strongSelf = self else { return } let model = strongSelf.updateModel(response) strongSelf.updateUI(model) } ``` **不推薦:** ```swift // 當self的釋放先于onComplete回調時,可能引起崩潰 resource.request().onComplete { [unowned self] response in let model = self.updateModel(response) self.updateUI(model) } ``` **不推薦:** ```swift // 當self的釋放介于updateModel()和updateUI()方法之間時,可能會出現意想不到的情況 resource.request().onComplete { [weak self] response in let model = self?.updateModel(response) self?.updateUI(model) } ``` ### 訪問控制 (Access Control) 一般來說,被標記為`private`、`fileprivate`的屬性或方法都應以下劃線(`_`)開頭。 **推薦:** ```swift private var _isEnabled: Bool fileprivate var _isClosed: Bool private func _foo() { // code goes here } fileprivate func _bar() { // code goes here } ``` **不推薦:** ```swift private var isEnabled: Bool fileprivate var isClosed: Bool private func foo() { // code goes here } fileprivate func bar() { // code goes here } ``` 但例外情況是,當標記被修飾為`private(set)`、`fileprivate(set)`時,不需要下劃線(`_`)開頭,因為他們都是可訪問的屬性,只是他們都是只讀屬性而已,如: ```swift private(set) var isEnabled: Bool fileprivate(set) var isClosed: Bool ``` 一般將訪問控制標記(`access control annotation`)放在聲明的最前面,但例外情況是,當有屬性標記`@IBAction`、`@IBOutlet`、`@discardableResult`、`@objc`時,需要將屬性標記放在最前面。 ```swift private let _message = "Great Scott!" class TimeMachine { @IBOutlet private var _lbTitle: UILabel! @objc private func _foo() -> Bool { // code goes here } } ``` ### 控制流 (Control Flow) 優先使用 `for-in` 的方式而不用`while`。 **推薦:** ```swift for _ in 0..<3 { print("Hello three times") } for (index, person) in attendeeList.enumerated() { print("\(person) is at position #\(index)") } for index in stride(from: 0, to: items.count, by: 2) { print(index) } for index in (0...3).reversed() { print(index) } ``` **不推薦:** ```swift var i = 0 while i < 3 { print("Hello three times") i += 1 } var i = 0 while i < attendeeList.count { let person = attendeeList[i] print("\(person) is at position #\(i)") i += 1 } ``` ### 使用Guard語句 通過使用`guard`來避免使用`if`時代碼塊嵌套過深的問題。 **推薦:** ```swift func check(phone: String?, name: String?, age: Int) -> Bool { guard let ph = phone, ph.count > 0 else { return false } guard let nm = name, nm.count > 0 else { return false } guard age > 0 else { return false } return true } ``` **推薦:** ```swift func check(phone: String?, name: String?, age: Int) -> Bool { // 把對phone、name、age的判斷放到一個guard里,更簡潔 guard let ph = phone, ph.count > 0, let nm = name, nm.count > 0, age > 0 else { return false } return true } ``` **不推薦:** ```swift func check(phone: String?, name: String?, age: Int) -> Bool { if let ph = phone, ph.count > 0 { if let nm = name, nm.count > 0 { if age > 0 { return true } else { return false } } else { return false } } else { return false } } ``` ### 分號 (Semicolons) 在Swift中不要求必須加分號,一般情況下你不需要使用分號。只有當你希望將多行代碼寫在一行時,才需要加分號來斷句。 ### 圓括號 (Parentheses) 一般情況下,在類似`if`條件、`for`循環等控制語句中不需要加圓括號。只有當你在進行數學運算時,希望代碼可讀性更高時才需要適當的添加圓括號。 **推薦:** ```swift let result = ((x * y) + 2) / h ``` **不推薦:** ```swift let result = (x * y + 2) / h ``` ### 引用 * [swift-style-guide](https://github.com/raywenderlich/swift-style-guide/blob/master/README.markdown) * [The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看