swiftSwift编程首页推荐

Swift与OC详谈

2016-11-15  本文已影响363人  earthX

最近瞄上了Swift,随着3.0版本的发布,1.x,2.x的部分API发生了一些算是较大的变化,语法越来越简便,实质内容还是保持不变,断断续续的接触了Swift几个月,总算是基本掌握了这门语言。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于Cocoa 和 Cocoa Touch 框架演变而来。

Swift给我的感觉就像一个天资聪慧的灵童,彷佛什么都懂,既有现有语言优良的传统(面向对象),又继承了其兄OC另辟蹊径的的方式(扩展,代理,面向协议),而苹果为了更快的推广这门语言,不惜放弃专制,居然开源了,虽然这很不苹果,但谁都能看得出来这都是OC留下的祸根啊。Swift可以跨平台运行,未来能不能在windows上玩一把就不得而知了。这里就不扯OC和Swift到底谁好谁坏,如:PHP是世界上最好的语言。

PS:在学习的过程中,收获颇丰,借助老关的帮助,对比OC和Swift同步进阶。不得不说老关的书作为初级,进阶和工具书都是极好的,在此推荐一下:传送门:Swift(关东升著)

一、基础篇

.......................................

类型 Swift Object-C 备注
整型 Int8 Int32 UInt8 UInt64 NSInteger 32位平台上 Int = Int32 (64 Int = Int64)
浮点型 Float Double NSFloat,NSDouble 32位浮点数,64位浮点数
布尔型 True, False YES,NO 严格true为真,false为假
字符型 Character NSString -
字符串 String NSString -
元组 Tuple () - (“1001”,“张三”,"18岁")
集合 Array,Dictionary,Set NSArray,NSDictionary,NSSet -
枚举 Enum Enum -
结构体 Struct Struct -

1.可选类型

swift要求所有的数据类型声明的变量或常量都是不能为nil的,所以必须对其进行初始化,初始化如果不确定其值的时候,可以采用可选类型(俗称的(?)和(!)), 在不能保证可选类型值是否为空之前最好不要拆包,否则可能编译报错。他们两个区别主要是在可选类型的拆包,拆包是将可选类型转变成普通类型。拆包分为显示拆包和隐性拆包。
例如 var n1 : Int? = 10 输出内容为:Optional(10) 说明变量n1不是普通类型

var n1 : Int? = 10
print(n1! + 10)
var n2 : Int! = 10
print(n1 + 10)

2.Swift3.0字符串用法改动

import Foundation

//---------字符串定义-----------

//字符串变量
var str1 = "hello"

//字符串常量
let str2 = "swift3.0"

//声明为nil,
var str3:String?  //可选类型

//空字符串
let str4 = String()
let str5 = ""     //提倡用这样的字面量语法声明,类型可不指定,swift自动识别

//字符
var char1:Character = "m"
var p_str1 = ""

//字符串拼接
p_str1 = str1 + str2
print(p_str1)
p_str1 = String(format:"%@~%@",str1,str2)
print(p_str1 as String);//hello~swift3.0
p_str1 = String(format:"%@~%@-%d",str1,str2,456)

print(p_str1);

//这种拼接方式方便地组合多种类型
p_str1 = "\(str1)\(str2)\(456)"
print(p_str1);//helloswift3.0456
p_str1 = "\(str1)\(str2)\(char1)"

print(p_str1);

//在字符串后面添加
p_str1.append(char1)
print(p_str1);
p_str1 += str2

print(p_str1);

//与数组通过指定字符拼接
var strArray = ["hello", "swift", "3.0"]
p_str1 = strArray.joined(separator: "-")//数组通过指定字符拼接

print("数组通过指定字符拼接\(p_str1)");


//拆分为指定的字符
strArray = p_str1.components(separatedBy: "-")
print("拆分为数组\(strArray)");

print("p_str1字符串为:\(p_str1)");


//枚举字符
for ch in p_str1.characters{
    
    print(ch)
    
    switch ch {
    case "0":
        print("有该字符")
    default:
        break
    }
}


print(p_str1)

//字符串长度
print("p_str1长度为:\(p_str1.characters.count)");

//首字母
char1 = p_str1[p_str1.startIndex]
print("首字母\(char1)");

//末字母
char1 = p_str1[p_str1.index(before: p_str1.endIndex)]
print("末字母\(char1)")

//第二个字母
char1 = p_str1[p_str1.index(after: p_str1.startIndex)]
print("第二个字母\(char1)")

//索引4的字母
char1 = p_str1[p_str1.index(p_str1.startIndex, offsetBy: 4)]
print("索引4的字母\(char1)")


//字符串截取
let i = p_str1.index(p_str1.startIndex, offsetBy: 4)
let j = p_str1.index(p_str1.startIndex, offsetBy: 8)

print(i,j);

var subStr = p_str1.substring(to: i)
print("截取到4\(subStr)")

subStr = p_str1.substring(from: i)
print("从索引4开始截取\(subStr)")

subStr = p_str1.substring(with: i..<j)
print("从索引4开始到8(不截取索引为8的字符)结束截取\(subStr)")

//通过扩展来简化一下

extension String{
    
    subscript (range: Range <Int>) -> String{
    
        get{
            
            let startIndex = self.index(self.startIndex, offsetBy:range.lowerBound)
            let endIndex = self.index(self.startIndex,offsetBy:range.upperBound)
            return self[Range(startIndex..<endIndex)]
            
        }
        
        set{
        
            let startIndex = self.index(self.startIndex,offsetBy:range.lowerBound)
            let endIndex = self.index(self.startIndex,offsetBy:range.upperBound)
            let strRange = Range(startIndex..<endIndex)
            
            self.replaceSubrange(strRange, with: newValue)
            
        }
        
    }
    
}

//通过扩展截取字符串
subStr = p_str1[0..<5]



print(p_str1)
//通过指定字符串截取子串
let range1 = p_str1.range(of: "o-")
let range2 = p_str1.range(of: ".")

subStr = p_str1.substring(from: (range1?.upperBound)!)
print("从o-开始截取字符串:\(subStr)")

subStr = p_str1.substring(with: (range1?.upperBound)!..<(range2?.lowerBound)!)
print("从o-开始到.结束截取字符串:\(subStr)")


print(subStr)
//插入指定字符串
subStr.insert("!", at: subStr.startIndex)
print(subStr);

subStr.insert("!", at: subStr.endIndex)
print(subStr);

subStr.insert(contentsOf:"YY".characters, at: subStr.index(after: subStr.startIndex))
print(subStr);

subStr.insert(contentsOf:"MM".characters, at: subStr.index(before: subStr.endIndex))
print(subStr)

//打印字符串的索引
let x = p_str1.index(p_str1.startIndex, offsetBy: 4)
print(x)

print(subStr)
//删除指定字符串
subStr.remove(at: x)
print("删除索引为4的字符后得到的字符串\(subStr)")

subStr.remove(at: subStr.startIndex)
print("删除索引为0的字符后得到的字符串\(subStr)")

subStr.remove(at: subStr.index(after: subStr.startIndex))
print("删除索引为0的字符后得到的字符串\(subStr)")

subStr.remove(at: subStr.index(before: subStr.endIndex))
print("删除最后一个索引的字符后得到的字符串\(subStr)")


//删除范围字符串
let ran1 = subStr.index(subStr.endIndex, offsetBy: -3)..<subStr.endIndex
subStr.removeSubrange(ran1)
print("删除从索引为倒数第3之后所有的字符串后得到的字符串\(subStr)")

let ran2 = subStr.index(subStr.startIndex, offsetBy: 4)..<subStr.endIndex
subStr.removeSubrange(ran2)
print("删除从索引为4之后所有的字符串后得到的字符串\(subStr)")


subStr = "http://baidu.com"
let ran3 = subStr.range(of: ":")
let ran4 = subStr.range(of: ".")

subStr.removeSubrange((ran3?.upperBound)!..<(ran4?.lowerBound)!)
print("删除从:开始到.之间的字符串后得到的字符串\(subStr)");

//字符串替换
subStr.replaceSubrange(ran3!, with: "wert")
print("ran3被wert替换之后的字符串\(subStr)");


//全部返回为布尔类型  Bool类型
//判断字符串是否有前、后缀
p_str1.hasPrefix("3.0")
p_str1.hasSuffix("3.0")

//判断字符串是否为空
p_str1.isEmpty
str3?.isEmpty
str4.isEmpty
str5.isEmpty


//--- 大小写转换 -----------
subStr = "ashdfasdjf"

print("subStr.capitalized(首字母大写)方法:\(subStr.capitalized)")
print("subStr.uppercased()大写方法:\(subStr.uppercased())")
print("subStr.lowercased()小写方法:\(subStr.lowercased())")

//--- 字符串比较 -----------
subStr = "2.6.2"
p_str1 = "2.6.1"
str1 = "2.7"
str3 = "2.7.0"
subStr > p_str1
subStr > str1
str1 == str3
str1 > str3!
str1 < str3!

//--- 字符串转换 -----------
Int(str1)
Double(str1)
Double(str3!)
Bool(str1)
str1 = "true"
Bool(str1)

3.控制语句

for i in 1...arr.count{
      //循环体
}

swift专门提供了遍历集合的方法for-in

let numbers = [1,2,3,4,5,6,7,8,9,10]
for (index,element) in numbers.enumerate(){
      print("Item \(index) : \(element)")     //index和item都有了
}

swift中的for循环提供了类似NSRange方式的跨步查找方式

for index in stride(from: 1, through: 5, by: 2) { print(index)}
//switch中使用元祖类型
var student = (id:"1002",name:"李四",age:32, ChineseScore:80, EnglishScore:89)
var desc: String
switch student {
case (_, _, _, 90...100, 90...100) where age > 20:  //where语句
    desc = "优"
case (_, _, _, 80...90, 80...90):
    desc = "良"
case (_, _, _, 60...80, 60...80):
    desc = "中"
case (_, _, _, 60...80, 90...100), (_,_, _, 90...100, 60...80):
    desc = "偏科"
case (_, _, _, 0...80, 90...100), (_,_, _, 90...100, 0...80):
    desc = "严重偏科"
default:
    desc = "无"
}
print("说明:\(desc)")

4.集合,函数,闭包

//Array
//swift中没有提供二维数据,只有一维数组
var arr1 = [String]()
var arr2 = [Int]()
var arr3 = [AnyObject]()
var arr4 = ["1","2","3"]
arr1 = ["1","2","3"]
arr2 = [1,2,3]
arr3 = [1 as AnyObject,"2" as AnyObject,3 as AnyObject,4.4 as AnyObject]

arr1.first //第一个元素
arr1.last  //最后一个元素
arr1.contains("9")//是否存在该元素
arr2.contains(3)
arr1.count
let bool1 = arr1 == arr4 //两数组是否相等
//增删改查
arr1 += arr1
arr1.insert("9", at: 4)
arr1.append("10")
arr1.remove(at: 6)
arr1[0] = "123"
let bool2 = arr1 == arr4 //两数组是否相等

for item in arr1 {
    print(item)
}
for (i,item) in arr1.enumerated() {
    if item == "2" {
        print(item)
        break
    }
}
//Dictionary
var dic1 = [String:Any]()
var dic2 = [Int:Int]()
let dic3 = [1:"4",2:"5",3:"6"]
let dic4 = ["2":"相同key值在合并的时候会替换","w":"5","e":dic3] as [String : Any]
dic1 = ["1":"qw","2":45,"3":UIView(),"4":dic3]
dic2 = [1:4,2:5,3:6]
print(dic1)
//遍历
for value in dic1.values {
    if let val = value as? UIView {
        val.frame = CGRect(x: 10, y: 150, width: 250, height: 250)
    }
}
print(dic1)
for key in dic1.keys {
    print(key)
}
for (key,value) in dic1 {
    print("key:\(key) -- value:\(value)")
}
//增删改查
dic1["增加"] = "增加的内容"
print(dic1)
dic1.removeValue(forKey: "4")
print(dic1)
dic1["2"] = 678
print(dic1)
(dic1["3"] as! UIView).frame = CGRect(x: 100, y: 100, width: 200, height: 200)
print(dic1)
//查询
if let value = dic1["2"] {
    print("value =>\(value)" )
}else{
    print("value => 没有" )
}
if let value = dic1["2"] as? String {
    print("value =>\(value)" )
}else{
    print("value => 没有" )
}
if let value = dic1["22"] {
    print("value =>\(value)" )
}else{
    print("value => 没有" )
}
if let value = dic1["22"] as? String {
    print("value =>\(value)" )
}else{
    print("value => 没有" )
}
//----重载运算符 更方便实现两个字典合并为一个字典
func += <key, value> ( left: inout Dictionary<key, value>, right: Dictionary<key, value>) {
    for (k, v) in right {
        left.updateValue(v, forKey: k)
    }
}
dic1 += dic4
print(dic1)
dic1 = dic4
print(dic1)
dic1.isEmpty
dic1.removeAll()
dic1.isEmpty
//函数
 func <#name#>(<#parameters#>) -> <#return type#> {
        <#function body#>
    }

5.其他

class Rectangle{
  var width : Double
  var height: Double

  init(width: Double,height:Double){
    self.width = width
    self.height = height
  }

 convenience init(length: Double){
    self.init(W: length, H: height)
  }

}
//单参数类型
func isEquals <T> (a : T ,b : T) -> Bool{
    return (a == b)
}

//多参数类型
func isEquals <T , U> (a : T ,b: U) -> Bool{
   return (a == b)
}


二、进阶篇

.......................................

1.值类型/引用类型

2.属性和变量(propertry,variable)

在OC中使用属性一般都使用如下格式:

@property (strong,nonatomic) NSString *string;

而在Swift中的属性一般都使用如下格式:

class Shape { var name = "shape"}

在oc中可以直接与变量打交道,swift中则行不通,在Swift的世界里,我们只与property打交道。在Swift中,属性分为存储属性和计算属性。简单来说,就是存储属性能够保存值,而计算属性只提供getter与setter,利用存储属性来生成自己的值。计算属性更像方法,而不是传统意义的属性。但是这样一个特性存在,使得我们更容易组织我们的代码。

构造变量
@interface Model

+ (int) value;
+ (void) setValue:(int)val;

@end

@implementation Modelstatic 

  int value;  
+ (int) value{ 

@synchronized(self) { 
  return value; 
} 

+ (void) setValue:(int)val{ 
  @synchronized(self) { 
    value = val; 
    } 
}
@end

// Foo.h@interface Foo {}
+ (NSDictionary *) dictionary;
// Foo.m oc单例
+ (NSDictionary *) dictionary{ 
  static NSDictionary* fooDict = nil; 
  static dispatch_once_t oncePredicate; 
  dispatch_once(&oncePredicate, ^{ 
  // create dict 
  }); 
  return fooDict;
}
struct SomeStructure { 
  static var storedTypeProperty = "Some value." 
  static var computedTypeProperty: Int { 
    return 1 
  } 
  class var overrideableComputedTypeProperty: Int { 
    return 107 
  }
}

//swift单例
class singletonClass { 
  static let sharedInstance = singletonClass() 
  private init() {}
  // 这就阻止其他对象使用这个类的默认的'()'初始化方法
}

计算属性
存储属性

待补充

属性观察者
class Employee{
  var no : Int = 0
  var name : String = "test"{
    willSet(newName){
      print("员工name新值 : \(newName)")
    }
    didSet(oldName){
      print("员工name旧值 : \(oldName)")
    }
  }
}

3.扩展(Extension)/协议(Protocol)

extension 类型名{
    //添加新功能
}
protocol leader{
  mutating func method()  //定义抽象方法
}
class member : leader {
  func method()  // 实现具体实例方法
} 
struct member_str : leader{
  mutating func method()    //实现变异方法
}

在Objective-C中我们这么声明Protocol:

@protocol SampleProtocol <NSObject>
- (void)someMethod;
@end

而在Swift中:

protocol SampleProtocol { func someMethod() }

在Swift遵循协议:

class AnotherClass: SomeSuperClass, SampleProtocol{ func someMethod() { } }

4.内存管理

swift和oc的内存管理都是采用了自动引用计数(ARC),很早之前的oc还是采用比较古老的手动引用计数(MRC),swfit在这方面有着基因级别的优势。ARC就是程序员不关心对象释放的问题,编译器在编译时在合适的位置插入对象内存释放代码。引用计数具体细节这里就不详细说明了。
但是采用ARC会导致强引用循环,即当两个对象的存储属性互相引用对方的时候,一个对象释放的前提是对方先释放,另一个对象释放的前提也是对方先释放,就会导致死锁的状态,从而导致内存泄露,打破强引用循环一般采用弱引用或者无主引用,使用weak和unowned 或可选类型(Optional)

//Object-C
weak self

//swift
weak var dept: Department?

5.错误处理

NSError *anyError;
BOOL success = [receivedData writeToURL:someLocalFileURL options:0 error:&anyError];
if (!success) {
NSLog(@”Write failed with error: %@”, anyError);
// present error to user
}
try {
     //成功处理语句
}catch{
     //错误处理语句
}

6. Id和AnyObject 以及Optional

在Swift中,没有id类型,Swift用一个名字叫AnyObject的protocol来代表任意类型的对象。

id myObject = [[UITableViewCell alloc]init];
var myObject: AnyObject = UITableViewCell()

我们知道id的类型直到运行时才能被确定,如果我们向一个对象发送一条不能响应的消息,就会导致crash。我们可以利用Swift的语法特性来防止这样的错误:

myObject.method?()

如果myObject没有这个方法,就不会执行,类似检查delegate是否有实现代理方法。在Swift中,在AnyObject上获取的property都是optional的。

7.函数式编程/block闭包

+(void)类方法
-(void)实例方法
+(UIColor*)blackColor    //类方法
-(void)addSubview:(UIView *)view   //实例方法 
class func blackColor() -> UIColor   //类方法
func addSubview(view: UIView)      //实例方法
//OC
NSArray *oldArray = @[@1,@2,@3,@4,@5,@6,@7,@8,@9,@10]; 
NSMutableArray *newArray; 
for (NSNumber* number in oldArray) { 
  if ([number compare:@4] == NSOrderedDescending ) { 
  [newArray addObject:number]; 
  } 
}

//swift函数式编程
let oldArray = [1,2,3,4,5,6,7,8,9,10]
let newArray = oldArray.filter({$0 > 4})

在Swift中,函数的最重要的改进就是函数作为一等公民(first-class),和对象一样可以作为参数进行传递,可以作为返回值,函数式编程也成为了Swift支持的编程范式。

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success) { 
  NSLog(@"Error: %@", error.domain);
}

//do-try-catch
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("/path/to/file")
do { 
  try fileManager.removeItemAtURL(URL)
} catch let error as NSError { 
  print("Error: \(error.domain)")
}

8.KVO

三、实战篇

很想写点什么实战类型的东西,但是坦诚来说,笔者并没有太多的实战经验,在这里仅总结一些前人的经验以供参考,后续可以继续补充。这里CSDN上的一篇写的很好了 可以参考一下 => 传送门

1.Swift与OC的常见区别
//OC:
typedef int MyInt 
//Swift:
typealias MyInt = int
2.控件类
@implementation SubUIView
- (id) initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self != nil) {
        // ...
    }
    return self;
}
@end
class SubUIView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        // ...
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
@implementation SubUIViewController
- (id) init
{
    self = [super init];
    if (self != nil) {
        // ...
    }
    return self;
}
@end
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)

3.Auto Layout
4.选择器

使用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中,不需要使用指令或字符串来分配方法。

        let button = UIButton(type: UIButtonType.system)
        button.setTitle("OK", for: UIControlState())
        
        let buttonWidth:CGFloat = 60
        let buttonHeight:CGFloat = 20
        let buttonTopView:CGFloat = 240
        
        button.frame = CGRect(x: (screen.size.width - buttonWidth)/2 , y: buttonTopView, width: buttonWidth, height: buttonHeight)
        
        button.addTarget(self, action: #selector(ViewController.onClick(_:)), for: UIControlEvents.touchUpInside)
        
        self.view.addSubview(button)

四、总结

.............................

事实上Swift的世界相比OC的世界还有很多新鲜的东西等待我们去发现和总结,Swift带来的多范式编程也将给我们编程的架构和代码的组织带来更来的思考。而Swift也是一个不断变化,不断革新的语言。相信未来的发展和稳定性会更让我们惊喜。

上一篇 下一篇

猜你喜欢

热点阅读