Swift 中 Selector 方法的访问权限控制问题

来源:互联网 时间:2015-12-04

Swift中Selector方法的访问权限控制问题

今天用Swift写了个视图,在视图上加个手势,如下所示:

 

 

 

 

 

 

Objective-C

 

1

2

panGestureRecognizer = UIPanGestureRecognizer(target: self, action: "beginDragged:")

addGestureRecognizer(panGestureRecognizer)

运行了下程序,然后崩溃了。崩溃日志如下:

 

 

 

 

 

 

Objective-C

 

1

[**.SwipeCardView beginDragged:]: unrecognized selector sent to instance 0x125e5bc10

而我已经在SwipeCardView类中定义了beginDragged:方法,如下所示:

 

 

 

 

 

Objective-C

 

1

2

3

private func beginDragged(gestureRecognizer: UIPanGestureRecognizer) {

    // ....

}

由于我并不想将beginDragged:方法暴露出去,所以将其定义为一个private方法。方法的定义一切正常,手势的Selector方法也设置正常,却报了未找到方法的异常。那问题可能就应该在访问权限问题上了。

我们知道Selector是Objective-C的产物,它用于在运行时作为一个键值去找到对应方法的实现。一个Objective-C的方法是由objc_method结束体定义的,其声明如下:

 

 

 

 

 

Objective-C

 

1

2

3

4

5

6

struct objc_method {

 

    SEL method_name OBJC2_UNAVAILABLE; // 方法名

    char *method_types OBJC2_UNAVAILABLE;

    IMP method_imp OBJC2_UNAVAILABLE; // 方法实现

}

这就要求selector引用的方法必须对ObjC运行时是可见的。而Swift是静态语言,虽然继承自NSObject的类默认对ObjC运行时是可见的,但如果方法是由private关键字修饰的,则方法默认情况下对ObjC运行时并不是可见的,所以就导致了以上的异常:运行时并没找到SwipeCardView类的beginDragged:方法。

所以,我们必须将private修饰的方法暴露给运行时。正确的做法是在 private 前面加上 @objc 关键字,这样就OK了。

 

 

 

 

 

 

Objective-C

 

1

2

3

@objc private func beginDragged(gestureRecognizer: UIPanGestureRecognizer) {

    // ....

}

另外需要注意的是,如果我们的类是纯Swift类,而不是继承自NSObject,则不管方法是private还是internal或public,如果要用在Selector中,都需要加上@objc修饰符。

参考

  1. SELECTOR
  2. @selector() in Swift?

 

零碎

Swift中枚举项设置相同的值

在Objective-C及C语言中,在枚举中我们可以设置两个枚举项的值相等,如下所示:

 

 

 

 

 

Objective-C

 

1

2

3

4

5

6

typedef NS_ENUM(NSUInteger, Type) {

    TypeIn      = 0,

    TypeOut     = 1,

    TypeInOut   = 2,

    TypeDefault = TypeIn

};

在上例中,我们让枚举项TypeDefault的值等于TypeIn。

而在Swift中,要求枚举项的rawValue是唯一的,如果像下面这样写,则编译器会报错:

 

 

 

 

 

 

Objective-C

 

1

2

3

4

5

6

enum Type: UInt {

    case In         = 0

    case Out        = 1

    case InOut      = 2

    case Default    = 0      // Error: Raw value for enum case is not unique

}

那如果我们希望上面枚举中Default的值与In的值一样,那应该怎么做呢?这时候就需要充分利用Swift中enum的特性了。我们知道,Swift中的enum与结构体、类一样,可以为其定义属性和方法,所以我们可以如下处理:

 

 

 

 

 

Objective-C

 

1

2

3

4

5

6

7

8

9

10

11

enum Type: UInt {

    case In         = 0

    case Out        = 1

    case InOut      = 2

 

    static var Default: Type {

        get {

            return .In

        }

    }

}

我们将Default定义为Type的一个静态只读属性,这个属性与枚举的其它枚举项的调用方式是一样的,可以如下调用:

 

 

 

 

 

Objective-C

 

1

let type: Type = .Default

参考

  1. Swift enum multiple cases with the same value

Swift中如何实现IBOutletCollection

在使用IB做界面开发时,我们经常需要将界面上的元素连接到我们的代码中。IBOutlet和IBAction就是专门用来做这事的两个关键字。另外在Objective-C还提供了一个伪关键字IBOutletCollection,它的实际作用是将界面上的一组相同的控件连接到一个数组中。具体可以参考iOS知识小集 第一期(2015.05.10)中的IBOutletCollection一节。

在Swift中,同样提供了@IBOutlet和@IBAction实现Objective-C中对应的功能,不过却没提供@IBOutletCollection来将一组相同控件连接到一个数组中。那如果我们想实现类似的功能,需要怎么处理呢?

实际上,我们在IB中选中一组相同的控件,然后将其连到到代码中时,会生成一个IBOutlet修饰的控件数组,类似于如下代码:

 

 

 

 

 

Objective-C

 

1

@IBOutlet var numberButtons: [UIButton]!

这就是Swift中类IBOutletCollection的处理。如果需要往数组中添加新建的对应的控件,则只需要在代码前面的小圆点与UI上的控件做个连线就OK了。而如果要想将控件从数组中移除,则只需要将对应的连接关系移除就可以了。

全能程序员交流QQ群290551701,聚集很多互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!

相关阅读:
Top