您的位置:首页 > 移动开发 > Objective-C

Using Swift with Cocoa and Objective-C--互用性-与ObjC API交互

2014-06-11 08:44 585 查看
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_26
http://www.cocoachina.com/newbie/basic/2014/0610/8757.html

Initialization

To instantiate an Objective-C class in Swift, you call one of its initializers with Swift syntax. When Objective-C
init
methods come over to Swift, they take on native Swift initializer syntax. The “init” prefix gets sliced off and becomes a keyword to indicate that the method is an initializer. For
init
methods that begin with “initWith,“ the “With” also gets sliced off. The first letter of the selector piece that had “init” or “initWith” split off from it becomes lowercase, and that selector piece is treated as the name
of the first argument. The rest of the selector pieces also correspond to argument names. Each selector piece goes inside the parentheses and is required at the call site.
For example, where in Objective-C you would do this:

Objective-C

UITableView *myTableView
= [[UITableView
alloc] initWithFrame:CGRectZero
style:UITableViewStyleGrouped];


In Swift, you do this:

Swift

let myTableView:
UITableView = UITableView(frame:
CGRectZero, style: .Grouped)


You don’t need to call
alloc
; Swift correctly handles this for you. Notice that “init” doesn’t appear anywhere when calling the Swift-style initializer.
You can be explicit in typing the object during initialization, or you can omit the type. Swift’s type inference correctly determines the type of the object.

Swift

let myTextField =
UITextField(frame:
CGRect(0.0, 0.0,
200.0, 40.0))


These
UITableView
and
UITextField
objects have the same familiar functionality that they have in Objective-C. You can use them in the same way you would in Objective-C, accessing any properties and calling any methods defined on the respective classes.
For consistency and simplicity, Objective-C factory methods get mapped as convenience initializers in Swift. This mapping allows them to be used with the same concise, clear syntax as initializers. For example, whereas in Objective-C you would
call this factory method like this:

Objective-C

UIColor *color
= [UIColor
colorWithRed:0.5 green:0.0
blue:0.5 alpha:1.0];


In Swift, you call it like this:

Swift

let color =
UIColor(red: 0.5,
green: 0.0, blue:
0.5, alpha: 1.0)


Accessing Properties

Access and set properties on Objective-C objects in Swift using dot syntax.

Swift

myTextField.textColor =
UIColor.darkGrayColor()

myTextField.text =
"Hello world"

if myTextField.editing {

myTextField.editing =
false

}


When getting or setting a property, use the name of the property without appending parentheses. Notice that
darkGrayColor
has a set of parentheses. This is because
darkGrayColor
is a class method on
UIColor
, not a property.
In Objective-C, a method that returns a value and takes no arguments can be treated as an implicit getter—and be called using the same syntax as a getter for a property. This is not the case in Swift. In Swift, only properties that are written
using the
@property
syntax in Objective-C are imported as properties. Methods are imported and called as described in
Working with Methods.

Working with Methods

When calling Objective-C methods from Swift, use dot syntax.
When Objective-C methods come over to Swift, the first part of an Objective-C selector becomes the base method name and appears outside the parentheses. The first argument appears immediately inside the parentheses, without a name. The rest
of the selector pieces correspond to argument names and go inside the parentheses. All selector pieces are required at the call site.
For example, whereas in Objective-C you would do this:

Objective-C

[myTableView
insertSubview:mySubview
atIndex:2];


In Swift, you do this:

Swift

myTableView.insertSubview(mySubview,
atIndex: 2)


If you’re calling a method with no arguments, you must still include the parentheses.

Swift

myTableView.layoutIfNeeded()


id Compatibility

Swift includes a protocol type named
AnyObject
that represents any kind of object, just as
id
does in Objective-C. The
AnyObject
protocol allows you to write type-safe Swift code while maintaining the flexibility of an untyped object. Because of the additional safety provided by the
AnyObject
protocol, Swift imports
id
as
AnyObject
.
For example, as with
id
, you can assign an object of any class type to a constant or variable typed as
AnyObject
. You can also reassign a variable to an object of a different type.

Swift

var myObject:AnyObject = UITableViewCell()

myObject =
NSDate()


You can also call any Objective-C method and access any property without casting to a more specific class type. This includes Objective-C compatible methods marked with the
@objc
attribute.

Swift

let futureDate =
myObject.dateByAddingTimeInterval(10)

let timeSinceNow =
myObject.timeIntervalSinceNow


However, because the specific type of an object typed as
AnyObject
is not known until runtime, it is possible to inadvertently write unsafe code. Additionally, in contrast with Objective-C, if you invoke a method or access a property that does not exist on an
AnyObject
typed object, it is a runtime error. For example, the following code compiles without complaint and then causes an unrecognized selector error at runtime:

Swift

myObject.characterAtIndex(5)

// crash, myObject does't respond to that method


However, you can take advantage of optionals in Swift to eliminate this common Objective-C error from your code. When you call an Objective-C method on an
AnyObject
type object, the method call actually behaves like an implicitly unwrapped optional. You can use the same optional chaining syntax you would use for optional methods in protocols to optionally invoke a method on
AnyObject
. This same process applies to properties as well.

For example, in the code listing below, the first and second lines are not executed because the
length
property and the
characterAtIndex:
method do not exist on an
NSDate
object. The
myLength
constant is inferred to be an optional
Int
, and is set to
nil
. You can also use an
if
let
statement to conditionally unwrap the result of a method that the object may not respond to, as shown on line three.

Swift

let myLength =
myObject.length?

let myChar =
myObject.characterAtIndex?(5)

if let
fifthCharacter = myObject.characterAtIndex(5) {

println("Found
\(fifthCharacter) at index 5")

}


As with all downcasts in Swift, casting from
AnyObject
to a more specific object type is not guaranteed to succeed and therefore returns an optional value. You can check that optional value to determine whether the cast succeeded.

Swift

let userDefaults =
NSUserDefaults.standardUserDefaults()

let lastRefreshDate:AnyObject? = userDefaults.objectForKey("LastRefreshDate")

if let
date = lastRefreshDate
as? NSDate {

println("\(date.timeIntervalSinceReferenceDate)")

}


Of course, if you are certain of the type of the object (and know that it is not
nil
), you can force the invocation with the
as
operator.

Swift

let myDate =
lastRefreshDate as
NSDate

let timeInterval =
myDate.timeIntervalSinceReferenceDate


Working with nil

In Objective-C, you work with references to objects using raw pointers that could be
NULL
(also referred to as
nil
in Objective-C). In Swift, all values—including structures and object references—are guaranteed to be non–nil. Instead, you represent a value that could be missing by wrapping the type of the value in an optional type. When you need to indicate that
a value is missing, you use the value
nil
. You can read more about optionals in

Optionals.
Because Objective-C does not make any guarantees that an object is non-nil, Swift makes all classes in argument types and return types optional in imported Objective-C APIs. Before you use an Objective-C object, you should check to ensure that
it is not missing.
In some cases, you might be absolutely certain that an Objective-C method or property never returns a
nil
object reference. To make objects in this special scenario more convenient to work with, Swift imports object types as
implicitly unwrapped optionals. Implicitly unwrapped optional types include all of the safety features of optional types. In addition, you can access the value directly without checking for
nil
or unwrapping it yourself. When you access the value in this kind of optional type without safely unwrapping it first, the implicitly unwrapped optional checks whether the value is missing. If the value is missing, a runtime
error occurs. As a result, you should always check and unwrap an implicitly unwrapped optional yourself, unless you are sure that the value cannot be missing.

Extensions

A Swift extension is similar to an Objective-C category. Extensions expand the behavior of existing classes, structures, and enumerations, including those defined in Objective-C. You can define an extension on a type from either a system
framework or one of your own custom types. Simply import the appropriate module, and refer to the class, structure, or enumeration by the same name that you would use in Objective-C.
For example, you can extend the
UIBezierPath
class to create a simple Bézier path with an equilateral triangle, based on a provided side length and starting point.

Swift

extension UIBezierPath {

convenience
init(triangleSideLength: Float,
origin: CGPoint) {

self.init()

let squareRoot =
Float(sqrt(3))

let altitude = (squareRoot *
triangleSideLength) / 2

moveToPoint(origin)

addLineToPoint(CGPoint(triangleSideLength,
origin.x))

addLineToPoint(CGPoint(triangleSideLength /
2, altitude))

closePath()

}

}


You can use extensions to add properties (including class and static properties). However, these properties must be computed; extensions can’t add stored properties to classes, structures, or enumerations.
This example extends the
CGRect
structure to contain a computed
area
property:

Swift

extension CGRect {

var area:
CGFloat {

return width *
height

}

}

let rect =
CGRect(x: 0.0,
y: 0.0, width:
10.0, height: 50.0)

let area =
rect.area

// area: CGFloat = 500.0


You can also use extensions to add protocol conformance to a class without subclassing it. If the protocol is defined in Swift, you can also add conformance to it to structures or enumerations, whether defined in Swift or Objective-C.
You cannot use extensions to override existing methods or properties on Objective-C types.

Closures

Objective-C blocks are automatically imported as Swift closures. For example, here is an Objective-C block variable:

Objective-C

void (^completionBlock)(NSData
*, NSError
*) = ^(NSData
*data,
NSError *error)
{/* ... */}


And here’s what it looks like in Swift:

Swift

let completionBlock: (NSData,
NSError) -> Void = {data,
error in /* ... */}


Swift closures and Objective-C blocks are compatible, so you can pass Swift closures to Objective-C methods that expect blocks. Swift closures and functions have the same type, so you can even pass the name of a Swift function.
Closures have similar capture semantics as blocks but differ in one key way: Variables are mutable rather than copied. In other words, the behavior of
__block
in Objective-C is the default behavior for variables in Swift.

Object Comparison

There are two distinct types of comparison when you compare two objects in Swift. The first,
equality (
==
), compares the contents of the objects. The second,identity (
===
), determines whether or not the constants or variables refer to the same object instance.
Swift and Objective-C objects are typically compared in Swift using the
==
and
===
operators. Swift provides a default implementation of the
==
operator for objects that derive from the
NSObject
class. In the implementation of this operator, Swift invokes the
isEqual:
method defined on the
NSObject
class. The
NSObject
class only performs an identity comparison, so you should implement your own
isEqual:
method in classes that derive from the
NSObject
class. Because you can pass Swift objects (including ones not derived from
NSObject
) to Objective-C APIs, you should implement the
isEqual:
method for these classes if you want the Objective-C APIs to compare the contents of the objects rather than their identities.
As part of implementing equality for your class, be sure to implement the
hash
property according to the rules in
Object comparison. Further, if you want to use
your class as keys in a dictionary, also conform to the
Hashable
protocol and implement the
hashValue
property.

Swift Type Compatibility

When you define a Swift class that inherits from
NSObject
or any other Objective-C class, the class is automatically compatible with Objective-C. All of the steps in this section have already been done for you by the Swift compiler. If you never import a Swift class in Objective-C code, you don’t need
to worry about type compatibility in this case as well. Otherwise, if your Swift class does not derive from an Objective-C class and you want to use it from Objective-C code, you can use the
@objc
attribute described below.
The
@objc
attribute makes your Swift API available in Objective-C and the Objective-C runtime. In other words, you can use the
@objc
attribute before any Swift method, property, or class that you want to use from Objective-C code. If your class inherits from an Objective-C class, the compiler inserts the attribute for you. The compiler also adds the
attribute to every method and property in a class that is itself marked with the
@objc
attribute. When you use the
@IBOutlet
,
@IBAction
, or
@NSManaged
attribute, the
@objc
attribute is added as well. This attribute is also useful when you’re working with Objective-C classes that use selectors to implement the target-action design pattern—for example,
NSTimer
or
UIButton
.
When you use a Swift API from Objective-C, the compiler typically performs a direct translation. For example, the Swift API
func playSong(name: String)
is imported as
- (void)playSong:(NSString *)name
in Objective-C. However, there is one exception: When you use a Swift initializer in Objective-C, the compiler adds the text “initWith” to the beginning of the method and properly capitalizes the first character in the
original initializer. For example, this Swift initializer
init (songName: String, artist: String)
is imported as
- (instancetype)initWithSongName:(NSString *)songName artist:(NSString *)artist
in Objective-C.
Swift also provides a variant of the
@objc
attribute that allows you to specify name for your symbol in Objective-C. For example, if the name of your Swift class contains a character that isn’t supported by Objective-C,
you can provide an alternative name to use in Objective-C. If you provide an Objective-C name for a Swift function, use Objective-C selector syntax. Remember to add a colon (
:
) wherever a parameter follows a selector piece.

Swift

@objc(Squirrel)

class Белка {

@objc(initWithName:)

init (имя:
String) { /*...*/ }

@objc(hideNuts:inTree:)

func прячьОрехи(Int,
вДереве: Дерево) {
/*...*/ }

}


When you use the
@objc(<#name#>)
attribute on a Swift class, the class is made available in Objective-C without any namespacing. As a result, this attribute can also be useful when you migrate an archivable Objective-C
class to Swift. Because archived objects store the name of their class in the archive, you should use the
@objc(<#name#>)
attribute to specify the same name as your Objective-C class so that older archives can be unarchived by your new Swift class.

Objective-C Selectors

An Objective-C selector is a type that refers to the name of an Objective-C method. In Swift, Objective-C selectors are represented by the
Selector
structure. You can construct a selector with a string literal, such as
let mySelector: Selector = "tappedButton:"
. Because string literals can be automatically converted to selectors, you can pass a string literal to any method that accepts a selector.

Swift

import UIKit

class MyViewController:
UIViewController {

let myButton =
UIButton(frame:
CGRect(x: 0,
y: 0, width:
100, height: 50))



init(nibName
nibNameOrNil: String!,
bundle nibBundleOrNil: NSBundle!) {

super.init(nibName:
nibName, bundle:
nibBundle)

myButton.targetForAction("tappedButton:",
withSender: self)

}



func tappedButton(sender:
UIButton!) {

println("tapped button")

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: