Swift blog

提议读一不折不扣swift
blog来了解OC与swift不同以及swift版本变动细节

引言

C++保留了一如既往有些过程式语言的表征,因而它们可定义不属其他类似的全局变量和函数。但是,C++毕竟是同样栽面向对象的顺序设计语言,为了支持函数的重载,C++对全局函数的处理方式与C有强烈的不同。
extern
“C”的重大作用就是为着能够科学贯彻C++代码调用其他C语言代码。加上extern
“C”后,会指示编译器这一部分代码按C语言的进行编译,而休是C++的。由于C++支持函数重载,因此编译器编译函数的进程中见面拿函数的参数类型也加到编译后的代码中,而不光是函数称呼;而C语言并无支持函数重载,因此编译C语言代码的函数时无会见带动齐函数的参数类型,一般的包括函数叫。

比如说你用C 开发了一个DLL 库,为了能为C
++语言也会调用你的DLL输出(Export)的函数,你要用extern
“C”来强制编译器不要涂改你的函数叫做。

OC id -> Swift Any

亮点,不需手动装箱
让OC API更加灵活

OC Swift2 Swift3
id AnyObject Any
NSArray * [AnyObject] [Any]
NSDictionary * [NSObject:AnyObject] [AnyHashable:Any]
NSSet * Set<NSObject> Set<AnyHashable>

内需专注的而是swift2不再供隐式转换,因此NS桥接的内需而展示转换

揭秘extern “C”

Overriding methods and conforming to protocols

取名符合OC的业内,id->Any

// Swift 2
class Foo: NSObject, NSCopying {
    override func isEqual(_ x: AnyObject?) -> Bool { ... }
    func copyWithZone(_ zone: NSZone?) -> AnyObject { ... }
}

// Swift 3
class Foo: NSObject, NSCopying {
    override func isEqual(_ x: Any?) -> Bool { ... }
    func copy(with zone: NSZone?) -> Any { ... }
}

由标准头文件说于

#ifndef __INCvxWorksh  /*防止该头文件被重复引用*/
#define __INCvxWorksh

#ifdef __cplusplus    //__cplusplus是cpp中自定义的一个宏
extern "C" {          //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
#endif

    /**** some declaration or so *****/  

#ifdef __cplusplus
}
#endif

#endif /* __INCvxWorksh */

Untyped Collections

隐式桥接不存在了,需要动用as,swift3导入的cocoa接受了Any/AnyHashable,因此于集合也可采取[AnyHashable:Any]

// Swift 2
struct State {
    var name: String
    var abbreviation: String
    var population: Int

    var asPropertyList: [NSObject: AnyObject] {
        var result: [NSObject: AnyObject] = [:]//也可以使用NSDictionary
        // Implicit conversions turn String into NSString here…
        result["name"] = self.name
        result["abbreviation"] = self.abbreviation
        // …and Int into NSNumber here.
        result["population"] = self.population
        return result
    }
}

let california = State(name: "California",
                       abbreviation: "CA",
                       population: 39_000_000)
NSNotification(name: "foo", object: nil,
               userInfo: california.asPropertyList)

// Swift 3
struct State {
    var name: String
    var abbreviation: String
    var population: Int

    // Change the dictionary type to [AnyHashable: Any] here...
    var asPropertyList: [AnyHashable: Any] {
        var result: [AnyHashable: Any] = [:]
        // No implicit conversions necessary, since String and Int are subtypes
        // of Any and AnyHashable
        result["name"] = self.name
        result["abbreviation"] = self.abbreviation
        result["population"] = self.population
        return result
    }
}
let california = State(name: "California",
                       abbreviation: "CA",
                       population: 39_000_000)
// ...and you can still use it with Cocoa API here
Notification(name: "foo", object: nil,
             userInfo: california.asPropertyList)

extern “C”的含义

extern “C”
包含重复意义,从字面上即可获取:首先,被它修饰的靶子是“extern”的;其次,被其修饰的目标是“C”的。
被extern “C”限定的函数或变量是extern类型的;
1、extern关键字
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的重要性字,该要字告诉编译器,其声明的函数和变量可以当比如模块或任何模块中使用。
平凡,在模块的腔文件中对遵循模块提供给任何模块引用的函数和全局变量以重点字extern声明。例如,如果模块B欲引用该模块A中定义之全局变量和函数时止需要蕴涵模块A的腔文件即可。这样,模块B中调用模块A中之函数时,在编译阶段,模块B虽然搜索不顶拖欠函数,但是连无见面报错;它会以链接阶段惨遭于模块A编译生成的对象代码中找到这个函数。
与extern对应之要害字是static,被其修饰的全局变量和函数只能在本模块被采取。因此,一个函数或变量只恐受据模块使用时,其无容许被extern
“C”修饰。

2、被extern “C”修饰的变量和函数是遵循C语言方式编译和链接的
第一看望C++中对类似C的函数是怎样编译的。
作一如既往种植面向对象的言语,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后每当符号库中的讳和C语言的不等。例如,假设有函数的原型为:
void foo( int x, int y );
欠函数被C编译器编译后当符号库中的讳也_foo,而C++编译器则会有像_foo_int_int之类的名(不同之编译器可能坏成的名字不同,但是还下了千篇一律之机制,生成的新名字叫做“mangled
name”)。
**
_foo_int_int这样的名字包含了函数叫作、函数参数数量和类型信息,C++就是凭这种体制来实现函数重载的。**
例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y
)编译生成的号是无相同的,后者也_foo_int_float。
同地,C++中之变量除支持部分变量外,还支持类成员变量和全局变量。用户所编写程序的类似成员变量可能与全局变量同名,我们以”.”来区分。而实质上,编译器在拓展编译时,与函数的处理一般,也为接近吃的变量取了一个独一无二的名,这个名字以及用户程序中同名的全局变量名字不同。

3、举例说明
(1)未加extern “C”声明时的接连方式
借用而于C++中,模块A的条文件如下:

// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
int foo( int x, int y );
#endif

//在模块B中引用该函数:
// 模块B实现文件 moduleB.cpp
#include "moduleA.h"
foo(2,3);

实际上,在连年路,链接器会从模块A生成的对象文件moduleA.obj中找_foo_int_int这样的记号!

(2)加extern “C”声明后的编译和链接方式
加extern “C”声明后,模块A的腔文件化:

// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
extern "C" int foo( int x, int y );
#endif

每当模块B的落实公文中还调用foo( 2,3 ),其结果是:
<1>A编译生成foo的靶子代码时,没有对准那名进行超常规处理,采用了C语言的法;
<2>链接器在呢模块B的靶子代码寻找foo(2,3)调用时,寻找的凡未经修改的符名_foo。

只要当模块A中函数声明了foo为extern “C”类型,而模块B中寓的是extern int
foo(int x, int y),则模块B找不顶模块A中之函数;反之亦然。

extern “C”这个宣称的真目的是以兑现C++与C及另语言的交集编程

The AnyHashable Type

Any类型可以具有另外项目,但是Dictionary、Set需要Hasble,AnyHashble是swift3提出的hashable的超类,任何一个可hash的种且落实了Anyhashble协议,比如String、Int

下场合

  • C++代码调用C语言代码、在C++的条文件被使用
    于C++中援C语言中之函数和变量,在蕴藏C语言头文件(假设为cExample.h)时,需进行下列处理:

extern "C"
{
#include "cExample.h"
}

若是于C语言的腔文件被,对其表函数只能指定为extern类型,C语言中不支持extern
“C”声明,在.c文件被寓了extern “C”时见面起编译语法错误。

/* c语言头文件:cExample.h */
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);     //注:写成extern "C" int add(int , int ); 也可以
#endif

/* c语言实现文件:cExample.c */
#include "cExample.h"
int add( int x, int y )
{
 return x + y;
}

// c++实现文件,调用add:cppFile.cpp
extern "C"
{
 #include "cExample.h"        //注:此处不妥,如果这样编译通不过,换成 extern "C" int add(int , int ); 可以通过
}

int main(int argc, char* argv[])
{
 add(2,3);
 return 0;
}

苟C++调用一个C语言编写的.DLL时,当包括.DLL的条文件或者声明接口函数时,应加extern
“C”{}。

  • 每当C中引用C++语言中之函数和变量时,C++的条文件要添加extern
    “C”,但是在C语言中无可知一直引用声明了extern
    “C”的该头文件,应该单独将C文件中将C++中定义之extern
    “C”函数声明为extern类型

//C++头文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern "C" int add( int x, int y );
#endif

//C++实现文件 cppExample.cpp
#include "cppExample.h"
int add( int x, int y )
{
 return x + y;
}

/* C实现文件 cFile.c
/* 这样会编译出错:#include "cExample.h" */
extern int add( int x, int y );
int main( int argc, char* argv[] )
{
 add( 2, 3 );
 return 0;
}

转载请注明作者Jason Ding及其出处
Github主页(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
简书主页(http://www.jianshu.com/users/2bd9b48f6ea8/latest\_articles)

Explicit Conversion for Unbridged Contexts

当有些限的场子swift不能够一直桥接C和OC的计划,比如id*,这个时段api将会显示UnsafePointer<AnyObject>,此时内需as转换,再&

// ObjC
@interface Foo

- (void)updateString:(NSString **)string;
- (void)updateObject:(id *)obj;

@end

// Swift
func interactWith(foo: Foo) -> (String, Any) {
    var string = "string" as NSString // explicit conversion
    foo.updateString(&string) // parameter imports as UnsafeMutablePointer<NSString>
    let finishedString = string as String

    var object = "string" as AnyObject
    foo.updateObject(&object) // parameter imports as UnsafeMutablePointer<AnyObject>
    let finishedObject = object as Any

    return (finishedString, finishedObject)
}

此外,OC的合计是看似协议,不可知就此结构体、枚举或另轻量级的通用型遵守OC的商事

AnyObject Member Lookup

Any没有AnyObject的寻行为艺术,因此不克动态的向Any发送信息,但是AnyObject可以,此时内需更换

// Swift 2
func foo(x: NSArray) {
    // Invokes -description by magic AnyObject lookup
    print(x[0].description)
}

// Swift 3
func foo(x: NSArray) {
    // Result of subscript is now Any, needs to be coerced to get method lookup
    print((x[0] as AnyObject).description)//也可以转换到你期望的指定类型as!NSOjbect
}

Swift Value Types in Objective-C

Any可以享有另外的结构体、枚举、元组或者其他的门类,OC的id在swift3等价于any,在swift2中需要手动封箱或者转入类,swift3则无欲

// Swift 2
struct CreditCard { number: UInt64, expiration: NSDate }

let PaymentMade = "PaymentMade"

// We can't attach CreditCard directly to the notification, since it
// isn't a class, and doesn't bridge.
// Wrap it in a Box class.
class Box<T> {
    let value: T
    init(value: T) { self.value = value }
}

let paymentNotification =
    NSNotification(name: PaymentMade,
                   object: Box(value: CreditCard(number: 1234_0000_0000_0000,
                                                 expiration: NSDate())))


// Swift 3
let PaymentMade = Notification.Name("PaymentMade")

// We can associate the CreditCard value directly with the Notification
let paymentNotification =
    Notification(name: PaymentMade,
                 object: CreditCard(number: 1234_0000_0000_0000,
                                    expiration: Date()))

欲注意的是swift3中于常见的组织体类型将会晤桥接作为透明对象要未是cocoa对象,Int、UInt、Double、Bool会桥接为NSNumber,Int8,UInt16虽然仅仅桥接为透明对象。如果遇了unrecognized selector sent to _SwiftValue题目,它代表OC尝试唤醒一个方以swift
值类型上,此时我们要手动管理

swift Any持有任一品种包括Optional,尽管OC api要求凡nonull
id,Optional在匪解包的情况下为可以看成参数传递,会造成runtime
error而不是编译错误。swift3.0.1+Xoce8.1解决了上述问题,为了避免兼容问题,不要因让透明对象为前景底swift可能会见桥接到固定类型

Working with JSON in Swift

JSONSerialization Foundation framework

Extracting Values from JSON

JSONSerialization类方法jsonObject返回Any类型并且扔来十分而data不克分析

import Foundation

let data: Data // received from a network request, for example
let json = try? JSONSerialization.jsonObject(with: data, options: [])

json顶层对象一般为字典或者频繁组,我们好利用as?+if进行判定转换

// Example JSON with object root:
/*
    {
        "someKey": 42.0,
        "anotherKey": {
            "someNestedKey": true
        }
    }
*/
if let dictionary = jsonWithObjectRoot as? [String: Any] {
    if let number = dictionary["someKey"] as? Double {
        // access individual value in dictionary
    }

    for (key, value) in dictionary {
        // access all key / value pairs in dictionary
    }

    if let nestedDictionary = dictionary["anotherKey"] as? [String: Any] {
        // access nested dictionary values by key
    }
}

// Example JSON with array root:
/*
    [
        "hello", 3, true
    ]
*/
if let array = jsonWithArrayRoot as? [Any] {
    if let firstObject = array.first {
        // access individual object in array
    }

    for object in array {
        // access all objects in array
    }

    for case let string as String in array {
        // access only string values in array
    }
}

Creating Model Objects from Values Extracted from JSON

一经有只饭店的model

import Foundation

struct Restaurant {
    enum Meal: String {
        case breakfast, lunch, dinner
    }

    let name: String
    let location: (latitude: Double, longitude: Double)
    let meals: Set<Meal>
}

来自sever的JSON数据

{
    "name": "Caffè Macs",
    "coordinates": {
        "lat": 37.330576,
        "lng": -122.029739
    },
    "meals": ["breakfast", "lunch", "dinner"]
}

Writing an Optional JSON Initializer

extension Restaurant {
    init?(json: [String: Any]) {
        guard let name = json["name"] as? String,
            let coordinatesJSON = json["coordinates"] as? [String: Double],
            let latitude = coordinatesJSON["lat"],
            let longitude = coordinatesJSON["lng"],
            let mealsJSON = json["meals"] as? [String]
        else {
            return nil
        }

        var meals: Set<Meal> = []
        for string in mealsJSON {
            guard let meal = Meal(rawValue: string) else {
                return nil
            }

            meals.insert(meal)
        }

        self.name = name
        self.coordinates = (latitude, longitude)
        self.meals = meals
    }
}

Writing a JSON Initializer with Error Handling

enum SerializationError: Error {
    case missing(String)
    case invalid(String, Any)
}

extension Restaurant {
    init(json: [String: Any]) throws {
        // Extract name
        guard let name = json["name"] as? String else {
            throw SerializationError.missing("name")
        }

        // Extract and validate coordinates
        guard let coordinatesJSON = json["coordinates"] as? [String: Double],
            let latitude = coordinatesJSON["lat"],
            let longitude = coordinatesJSON["lng"]
        else {
            throw SerializationError.missing("coordinates")
        }

        let coordinates = (latitude, longitude)
        guard case (-90...90, -180...180) = coordinates else {
            throw SerializationError.invalid("coordinates", coordinates)
        }

        // Extract and validate meals
        guard let mealsJSON = json["meals"] as? [String] else {
            throw SerializationError.missing("meals")
        }

        var meals: Set<Meal> = []
        for string in mealsJSON {
            guard let meal = Meal(rawValue: string) else {
                throw SerializationError.invalid("meals", string)
            }

            meals.insert(meal)
        }

        // Initialize properties
        self.name = name
        self.coordinates = coordinates
        self.meals = meals
    }
}

Interactive Playgrounds

至于此可以去看自己之简文playground正确利用姿势

Literals in Playgrounds

Xcode7.1开始支持字面量,主要用来颜色、图片、文件

Strings in Swift 2

至于此可以去看自己之简文您实在懂swift
string吗?

Increasing Performance by Reducing Dynamic Dispatch

swift允许再次写超类的道及性质,这就是需要以运作时刻连的拜访并且实施间接调用。这个技术叫做动态派发,这项技艺增加了语言表达的复杂和大度之runtime消耗以间接的下上。下面介绍三种植方式消除动态派发:final、private、WholeModule
Optimization

央圈下的代码

class ParticleModel {
    var point = ( 0.0, 0.0 )
    var velocity = 100.0

    func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
        point = newPoint
        velocity = newVelocity
    }

    func update(newP: (Double, Double), newV: Double) {
        updatePoint(newP, newVelocity: newV)
    }
}

var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
    p.update((i * sin(i), i), newV:i*1000)
}

就是如面写的,编译器发出动态派发调用

  1. 调用p 的 update
  2. 调用p 的 updatePoint
  3. 抱p 的 point长祖属性
  4. 获取p 的 速率

此使用动态派发的因由在ParticleModel的子类可能由此可计算属性重写point、velocity,也再写update、updatePonit

动态派发调用的贯彻是由此查找method
table然后实行间接调用。着相对于直接调用的快必然是迟迟的。

Use final when you know that a declaration does not need to be overridden

final关键字会范围class、method、property不受重写。能够平安之教编译器取消动态派发。point、velocity、updatePoint不开展动态派发,直接访问,update进行动态派发

class ParticleModel {
    final var point = ( x: 0.0, y: 0.0 )
    final var velocity = 100.0

    final func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
        point = newPoint
        velocity = newVelocity
    }

    func update(newP: (Double, Double), newV: Double) {
        updatePoint(newP, newVelocity: newV)
    }
}

final修饰类的早晚,表明无能够让子类化,因此指明函数、属性都是final

final class ParticleModel {
    var point = ( x: 0.0, y: 0.0 )
    var velocity = 100.0
    // ...
}

Infer final on declarations referenced in one file by applying the private keyword.

行使private关键字,限制以手上文件内,如果手上文件内没针对该class的重写,那么编译器就见面想其不适用动态派发

class ParticleModel {
    private var point = ( x: 0.0, y: 0.0 )
    private var velocity = 100.0

    private func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
        point = newPoint
        velocity = newVelocity
    }

    func update(newP: (Double, Double), newV: Double) {
        updatePoint(newP, newVelocity: newV)
    }
}

及final一样,private也得修饰class

Use Whole Module Optimization to infer final on internal declarations.

internal默认的访问控制权限表明只有能当模块可见。swift编译文件是模块独立的,无法确定internal声明在不同的文件是否给再写了。但是倘若整个模块的优化是被的,所有的模块同时编译、能够允许编译器将internal推断其的可见性

public class ParticleModel {
    var point = ( x: 0.0, y: 0.0 )
    var velocity = 100.0

    func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
        point = newPoint
        velocity = newVelocity
    }

    public func update(newP: (Double, Double), newV: Double) {
        updatePoint(newP, newVelocity: newV)
    }
}

var p = ParticleModel()
for i in stride(from: 0.0, through: times, by: 1.0) {
    p.update((i * sin(i), i), newV:i*1000)
}

Nullability and Objective-C

其实对应的即使是不过选值

  • !表示非空
  • ?可空

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图