從Objective-C向Swift轉換學習到的經驗

摘要: SendBird是國外一款針對移動App和網站的Chat API,其開發團隊成員Jed Gyeong分享了他們在將產品從Objective-C向Swift轉換過程中所學習到的一些心得體會。

從Objective-C向Swift轉換學習到的經驗

SendBird為常見系統均提供了示例UI,方便開發者構建自己的聊天和簡訊功能。以前只有Objective-C的iOS示例UI,後來聽到諸多要求開發Swift版本的呼聲,於是我們將示例UI的語言從Objective-C轉換成了Swift。此過程中的最大感受是:兩種語言確實存在不少差異。今天特意分享一些心得給大家,希望對你們有借鑒價值。

注意:示例UI並不使用Interface Builder(IB,Mac OS X平台上用於設計和測試用戶界面的應用程序),而是從零開始構建的。所以以下范例不適用於使用Interface Builder的開發者。

Objective-C和Swift語言示例項目

SendBird示例UI可從Github庫下載。Objective-C和Swift語言項目均在同一個庫中,我們建議比較兩種代碼庫以更好理解其差異。

初始化UIView的子類

在iOS應用上做到UI就需要子類化UIView,也就是要重寫UIView的init方法。注意:兩種語言有所區別。

Objective-C只需在UIView子類中重寫必要的init方法。要初始化一個UIView框架,就要重寫initWithFrame:框架,如下所示:

@implementation SubUIView
– (id) initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self != nil) {
        // …
    }
    return self;
}
@end

然而Swift需要多一些步驟來重寫同一個init方法。首先,重寫使用CGRect框架作為其參數的init方法。根據UIView文檔,用Swift語言構建時,須重寫init(coder:),但我們不需要這種方法,就用如下代碼處理。類屬性初始化所需的代碼可以在init(frame:)中執行。

class SubUIView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        // …
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError(“init(coder:) has not been implemented”)
    }
}


初始化UIViewController的子類

子類化UIViewController是iOS開發的重要步驟。使用Interface Builder的開發者需要重寫initWithNibName:bundle:,但既然我們用代碼來構建UI,就不需要執行這一方法了。只需重寫init方法,在其中初始化類屬性即可。

@implementation SubUIViewController
– (id) init
{
    self = [super init];
    if (self != nil) {
        // …
    }
    return self;
}
@end

Swift也一樣要重寫init()方法。做到指定的初始化init(nibName:bundle:)來子類化UIViewController。重申:此過程不適用Interface Builder,所以無需定義nibName和bundle的值,而是調用比指定初始化更簡單的convenience初始化,將指定初始化init(nibName:bundle:)設為零。現在調用init()來初始化類,用重寫的(nibName:bundle:)執行類屬性。

class SubUIViewController: UIViewController {
    convenience init() {
        self.init(nibName: nil, bundle: nil)
    }
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        // Initialize properties of class
    }   
    required init?(coder aDecoder: NSCoder) {
        fatalError(“init(coder:) has not been implemented”)
    }
}


現在可以創建和調用UIViewController的子類,如下所示:

let viewController: SubUIViewController = SubUIViewController()
self.navigationController?.pushViewController(viewController, animated: false)


使用Auto Layout來做到View

沒有Interface Builder的情況下,就用Auto Layout中的NSLayoutConstraint類來設置View的大小和位置——注意Objective-C和Swift在這裡有微妙差別。

Objective-C使用NSLayoutConstraint類中的constraintWithItem方法。

+ (instancetype)constraintWithItem:(id)view1
                         attribute:(NSLayoutAttribute)attr1
                         relatedBy:(NSLayoutRelation)relation
                            toItem:(id)view2
                         attribute:(NSLayoutAttribute)attr2
                        multiplier:(CGFloat)multiplier
                          constant:(CGFloat)c

Swift使用同一個類中的init方法。

convenience init(item view1: AnyObject,
       attribute attr1: NSLayoutAttribute,
       relatedBy relation: NSLayoutRelation,
          toItem view2: AnyObject?,
       attribute attr2: NSLayoutAttribute,
      multiplier multiplier: CGFloat,
        constant c: CGFloat)


如果是Objective-C,則執行以下代碼。這段代碼將創建NSLayoutConstraint(定義self.profileImageView和self之間的位置),然後添加到self上。


[self addConstraint:[NSLayoutConstraint constraintWithItem:self.profileImageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1 constant:kMessageCellLeftMargin]];


使用Swift也可以創建NSLayoutConstraint,具體如下:

self.addConstraint(NSLayoutConstraint.init(item: self.profileImageView!, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: kMessageCellLeftMargin))

比較兩種語言版本你會發現,不同於Objective-C,Swift是從NSLayoutConstraint調用init方法的,而且屬性和relatedBy的枚舉值也有差別。

兩種語言NSLayoutConstraint中的枚舉值分別是:

NSLayoutAttribute

Objective-C

typedef enum: NSInteger {
   NSLayoutAttributeLeft = 1,
   NSLayoutAttributeRight,
   NSLayoutAttributeTop,
   NSLayoutAttributeBottom,
   NSLayoutAttributeLeading,
   NSLayoutAttributeTrailing,
   NSLayoutAttributeWidth,
   NSLayoutAttributeHeight,
   NSLayoutAttributeCenterX,
   NSLayoutAttributeCenterY,
   NSLayoutAttributeBaseline,
   NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline,
   NSLayoutAttributeFirstBaseline,
   NSLayoutAttributeLeftMargin,
   NSLayoutAttributeRightMargin,
   NSLayoutAttributeTopMargin,
   NSLayoutAttributeBottomMargin,
   NSLayoutAttributeLeadingMargin,
   NSLayoutAttributeTrailingMargin,
   NSLayoutAttributeCenterXWithinMargins,
   NSLayoutAttributeCenterYWithinMargins,
   NSLayoutAttributeNotAnAttribute = 0
} NSLayoutAttribute;

Swift

enum NSLayoutAttribute : Int {
    case Left
    case Right
    case Top
    case Bottom
    case Leading
    case Trailing
    case Width
    case Height
    case CenterX
    case CenterY
    case Baseline
    static var LastBaseline: NSLayoutAttribute { get }
    case FirstBaseline
    case LeftMargin
    case RightMargin
    case TopMargin
    case BottomMargin
    case LeadingMargin
    case TrailingMargin
    case CenterXWithinMargins
    case CenterYWithinMargins
    case NotAnAttribute
}

NSLayoutRelation

Objective-C

enum {
   NSLayoutRelationLessThanOrEqual = -1,
   NSLayoutRelationEqual = 0,
   NSLayoutRelationGreaterThanOrEqual = 1,
};
typedef NSInteger NSLayoutRelation;

Swift

enum NSLayoutRelation : Int {
    case LessThanOrEqual
    case Equal
    case GreaterThanOrEqual
}

選擇器

使用UIButton、NSNotificationCenter、NSTimer等時,使用選擇器來分配要執行的方法。在Objective-C中,@selector指令代表使用選擇器。

– (void)test
{
    // …
    mTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerCallback:) userInfo:nil repeats:YES];
}
– (void)timerCallback:(NSTimer *)timer
{
    // …
}

在Swift中,不需要使用指令或字符串來分配方法。

func test() {
    // …
    self.mTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: “timerCallback:”, userInfo: nil, repeats: true)
    // …
}
func timerCallback(timer: NSTimer) {
    // …
}


字符串

盡管在Swift代碼中也可以用Objective-C專門處理字符串的NSString,但要使用以String對象為屬性的UITextField上的文本或其他的話,就要清楚NSString和String的區別。

在Objective-C中,UITextField上的文本為NSString,所以屬性的長度就是字符串的長度。

– (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    NSString *message = [textField text];
    if ([message length] > 0) {
        // …
    }
    return YES;
}

Swift是沒有長度屬性的,所以要用characters屬性的count屬性。

func textFieldShouldReturn(textField: UITextField) -> Bool {
    let message: String = textField.text!
    if message.characters.count > 0 {
        // …
    }
    return true
}

在Objective-C中,我們用stringWithFormat:來創建一個格式化字符串。


[self.typingLabel setText:[NSString stringWithFormat:@”%d Typing something cool….”, count]];


但在Swift中,String裡沒有stringWithFormat方法,所以用init(format:_ arguments:)代之。我們可以分配一個與NSString格式化結構相同的格式化字符串來創建一個新字符串,然後給arguments賦以相關的值。

self.typingLabel?.text = String.init(format: “%d Typing something cool…”, count)

從數據類型得到最小&最大值

就從數字格式上得到最小和最大值而言,Objective-C和Swift也有差別。Objective-C使用一個預定義宏來得到最小和最大值,但Swift則可以直接從數據類型上得到這些值。Objective-C使用的是如下的宏:

CGFLOAT_MAX
CGFLOAT_MIN
INT32_MAX
INT32_MIN
LLONG_MAX
LLONG_MIN


而Swift則從數據類型上得到最小和最大值,如下:

CGFloat.max
CGFloat.min
Int32.max
Int32.min
Int64.max
Int64.min

字典和枚舉值

Objective-C用NSDictionary來定義NSAttributedString的屬性。Swift則用Dictionary而不是NSDictionary,但想為Dictionary分配枚舉值的時候,做法稍有不同。

Objective-C直接為NSDictionary分配鍵值,如下所示:稱為NSUnderlineStyleSingle的枚舉值不能作為NSDictionary值直接分配,所以要先用@()將它轉換成一個對象。

NSDictionary *underlineAttribute = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)};

Swift可以直接為Dictionary分配鍵值(如下所示)。如果該值定義為AnyObject,那麼Swift就跟Objective-C一樣不能直接使用枚舉值,而是使用rawValue屬性代之。


let underlineAttribute: [String: AnyObject] = [NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue]

其他心得

下表列出了SendBird示例UI項目語言轉換過程中所發現的Objective-C和Swift的其他差異。

從Objective-C向Swift轉換學習到的經驗

結論

  1. 相比Objective-C,Swift有更為嚴格的類型轉換原則,就算有Xcode的自動糾正功能也須嚴格遵守;

  2. 學習類指定初始化和convenience初始化可以讓語言轉化更輕鬆一些;

  3. Xcode的自動代碼補全和糾正讓Objective-C到Swift的轉換更方便,但太依賴這一功能並不能讓你一勞永逸,還是以Swift的語言指南(Language Guide)為準;

  4. 即使使用相同名稱的類,也會在兩種語言中遇到針對同一功能的不同方法名稱,所以以類參考文件為準比較保險。

如果決定使用Swift,建議先學習其基本知識,並試著將手頭現有的Objective-C項目轉化為Swift版本練練手。

英文來源:Lessons Learned from Converting Objective-C Project into Swift Language 
作者:Jed Gyeong 
翻譯:張新慧 
審校/責任編輯:唐小引,歡迎技術投稿、約稿,給文章糾錯,請郵件[email protected]


SDCC 2016 數據庫&架構技術峰會(上海站)將於3月18日-19日舉行,講師和議題已全部確認。目前6折限時報名,團購享有更多優惠!詳情請戳「閱讀原文」

從Objective-C向Swift轉換學習到的經驗

閱讀原文


關於作者:
CSDN精彩內容每日推薦。我們關注IT產品研發背後的那些人、技術和故事。

微信號:CSDNnews

推薦閱讀:

》生不出男孩被婆家逼走,獨自養兩個女兒流落街頭,如今她是身價60億的水餃皇后!

》她是中國史上最強女海盜,讓多少洋人感受過被她支配的恐懼!