LoginSignup
190
191

More than 5 years have passed since last update.

SwiftでObjCも利用したアプリ開発のときにハマったこと

Posted at

192_ 体力のない _-_いなげな言葉いじり.jpg

スカラー型のキャストはコンバージョンを使う

Swiftではスカラー型のキャスト(Casting)はありません。その代わりにコンバージョン(Conversion)を利用します。

let i: Int = Int(1.0)
let n: NSInteger = NSInteger(1.0)

let f: Float = Float(100)
let d: Double = Double(100)
let g: CGFloat = CGFloat(100)

ObjCライクなキャストはできない

ObjCのようなキャスト構文は文法エラーになります。

let index: NSInteger = 1
let width: CGFloat = 100
let r = width * (CGFloat)index
// => ERROR Consecutive statements on a line must be separated by ';'

補足

オブジェクト型はキャスト可能です。

CGFloatとFloatまたはDoubleの演算でエラーになる場合がある

Swiftで扱うCGFloatはビルドするアーキテクチャによってエイリアスが異なります。
シミュレーターがiPhone5s(arm64)とiPhone5,iPhone4s(armv7, armv7s)では挙動が変わります。

アーキテクチャ CGflotのエイリアス
arm64 Double
armv7, armv7s Float

一見、コンパイルが通りそうですがiPhone5sシミュレーターでコンパイルするとエラーになります。

let index: NSInteger = 1
let width: CGFloat = 100
let r1 = width * Float(index)
// => ERROR Could not find an overload for '*' that accepts the supplied arguments        

この場合は、Doubleでコンバージョンすると演算可能です。

let index: NSInteger = 1
let width: CGFloat = 100
let r2 = width * Double(index)
// => OK

CGFloatとの演算は常ににCGFloatにコンバージョンして演算することで、この問題を回避できます。

let index: NSInteger = 1
let width: CGFloat = 100
let r = width * CGFloat(index)
// => OK

ObjCのNSIntegerとSwiftのIntの演算

NSIntegerとSwiftのInttypealiasされた同じ型です。

// typealias NSInteger = Int
let x: CGFloat = 3
let y: Float = 3
let i: Int = 1
let n: NSInteger = 1
let w = i + Int(x) + Int(y) // => OK
let q = n + Int(x) + Int(y) // => OK

if n == i {
    NSLog("HERE!") // => OK
}

SwiftではNSUIntegerを利用できません。

let u: NSUInteger = 1

// ERROR => Use of undeclared type 'NSUInteger'; did you mean to use 'Int'?

ObjCのid型をパラメータにとるメソッドにSwiftのクロージャを渡す方法

以下の様なObjCメソッドにSwiftのクロージャを渡す方法です。

@interface HogeFuga : NSObject 
+ (void) hogeUsingBlock:(id)bock;
@end

ObjCのid型はSwiftのAnyObject?型と同義です。
クロージャは対応した型を有するのでAnyObject型にはなりません。
よってid型にクロージャを代入できない問題が発生します。

HogeFuga.hogeUsingBlock( { () -> () in
    println("Fuga")
})
// => ERROR Type '() -> ()' does not conform to protocol 'AnyObject'


var closures: AnyObject = { () -> () in
    println("Fuga")
}
// => ERROR Type '() -> ()' does not conform to protocol 'AnyObject'

解決策はObjCで型を明示したブロックを持つメソッドでラップします。

typedef void (^BlockHogeWrapper)();

@interface HogeFuga (Wrapper)
+ (void) hogeUsingBlockWrapper:(BlockHogeWrapper)block;
@end

@implementation HogeFuga (Wrapper)
+ (void) hogeUsingBlockWrapper:(BlockHogeWrapper)block;
{
    [HogeFuga hogeUsingBlock:block];
}
@end

NSObject#descriptionをオーバーライドする

NSObject#descriptionのオーバーライドはメソッドをオーバーライドするかと思いきや、プロパティのオーバーライドになります。

class Hoge: NSObject {
    var name: String?
    var note: String?

    override var description: String! {
        get {
            return "Name = \(self.name), Note = \(self.note)"
        }
    }
}

メソッドをオーバーライドしようするとエラーになります。

class Hoge: NSObject {
    var name: String?
    var note: String?

    override func description() -> String {
        return "Name = \(self.name), Note = \(self.note)"
    }
}
// ERROR Method does not override any method from its superclass

参考

クロージャ型プロパティのオプショナルな書き方

クロージャ型を丸括弧で括ります。

class Fuga {
  var completion: ( () -> () )?
}

ObjCのenum値をSwift記法で記載する方法がわからない

Swiftではenum値を記述するにはObjCと異りなり、enum型 ドット enum値の文法になります。
この記述

記述方法がわかりやすい例

enum型とPrefixに値を分割できます。

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
  UIViewAutoresizingNone                 = 0,
  UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
  
}

// ObjC
UIViewAutoresizingFlexibleLeftMargin
// Swift
UIViewAutoresizing.FlexibleLeftMargin

記述方法の差異が少ない例

enum型と値のPrefixが一部(複数形のs)異なります。

typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) {
  UIViewAnimationOptionLayoutSubviews        = 1 <<  0,
  UIViewAnimationOptionAllowUserInteraction  = 1 <<  1,
  
}

// ObjC
UIViewAnimationOptionLayoutSubviews
// Swift
UIViewAnimationOptions.LayoutSubviews

記述方法がわかりにくい例

enum型と値のPrefixがPrefixが一致していない場合です。
一致している部分のPrefixで分割しています。

typedef NS_ENUM(NSInteger, HogeStyle) {
    HogeFugaDefault,
    HogeFugaValue1,
    HogeFugaValue2,
    HogeFugaSubtitle
};

// ObjC
HogeFugaValue1
// Swift
HogeStyle.FugaValue1

enum型のメソッド引数や変数にenumで定義された定数以外を設定するとエラーになる

NSIntegerで定義されたenum型にNSInteger型の値(数値)を直接代入するとコンパイルエラーになります。

typedef NS_ENUM(NSInteger, UIViewContentMode) {
    UIViewContentModeScaleToFill,
    ...
}

self.view.contentMode = 0
// => ERROR Cannot convert the expression's type '()' to type 'UIViewContentMode'

ObjCのClass型をパラメータにとるメソッドにSwiftのClassを渡す方法 (Restkit)

Class型をパラメータにとるメソッドにSwiftのClassを代入し、そのClassがObjC側でnewされるケースです。

@interface RKObjectMapping : RKMapping
{
// クラス型を引数にとる
+ (instancetype)mappingForClass:(Class)objectClass
}

...

@implementation RKObjectMappingOp 
{
    // 渡したクラス型でnewされる
    return [mapping.objectClass new];
}

NSObject.Type型を代入すれば、うまくObjC側でもnewできました。

class HogeModel: NSObject {
}

var clazz: NSObject.Type = WTDHogeModel.self
let mapping: RKObjectMapping = RKObjectMapping(forClass: clazz) 

以下のObjC由来のClassの取得方法で代入するとエラーになり,
うまくできませんでした。

// newするとERROR
var clazz: AnyClass = object_getClass(WTDHogeModel())

// ERROR => EXC_BAD_INSTRUNCTION
var clazz: AnyClass = NSClassFromString("WTDHogeModel") 
190
191
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
190
191