oc的入口:main.m
文件中
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
swift的入口:AppDelegate.swift
文件中頂部@main
標(biāo)記,表示
- 編譯器自動(dòng)生成入口代碼(
main
函數(shù)代碼),自動(dòng)設(shè)置AppDelegate
為APP的代理 - 也可以刪掉
@main
载慈,自定義入口代碼丰包,新建一個(gè)main.swift
文件(需要自定義application需求時(shí)可以自己寫入口代碼),main.swift
文件代碼如下:
import Foundation
import UIKit
class MJApplication: UIApplication {
}
UIApplicationMain(CommandLine.argc,
CommandLine.unsafeArgv,
NSStringFromClass(MJApplication.self),
NSStringFromClass(AppDelegate.self))
由于很多常用的第三方框架還是oc版本饶唤,沒有swift版本肄程,但是項(xiàng)目里可能需要調(diào)用那些框架的功能,所以swift語(yǔ)言的工程還需要用到oc版本的三方叶骨,或者公司項(xiàng)目需要用到swift和oc混合開發(fā)時(shí)茫多,就涉及到了Swift調(diào)用OC或者OC調(diào)用Swift
Swift調(diào)用OC
- 手動(dòng)新建一個(gè)橋接頭文件,文件名格式默認(rèn)為:{targetName}-Bridging-Header.h忽刽,然后在Build Settings設(shè)置路徑,如下
- 或者在swift工程里新建一個(gè)oc的文件時(shí)會(huì)自動(dòng)彈出彈框詢問(wèn)是否自動(dòng)創(chuàng)建橋接文件天揖,如下
想用oc的哪些頭文件,在橋接頭文件中導(dǎo)入頭文件即可跪帝,例#import "Person.h"
,則在swift工程中今膊,Person
類就暴漏給swift使用了
Person.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
int sum(int a,int b);
@interface Person : NSObject
@property (nonatomic,assign) NSInteger age;
@property (nonatomic,copy) NSString *name;
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
+ (instancetype) personWithAge:(NSInteger)age name:(NSString *)name;
- (void)run;
+ (void)run;
- (void)eat:(NSString *)food other:(NSString *)other;
+ (void)eat:(NSString *)food other:(NSString *)other;
@end
NS_ASSUME_NONNULL_END
Person.m
#import "Person.h"
@implementation Person
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name{
if (self = [super init]) {
self.age = age;
self.name = name;
}
return self;
}
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name{
return [[self alloc] initWithAge:age name:name];
}
+ (void)run{NSLog(@"Person + run");}
- (void)run{NSLog(@"%zd %@-run",_age,_name);}
+ (void)eat:(NSString *)food other:(NSString *)other{
NSLog(@"Person + eat %@ %@",food,other);
}
- (void)eat:(NSString *)food other:(NSString *)other{
NSLog(@"%zd %@ - eat %@ %@",_age,_name,food,other);
}
@end
int sum(int a,int b){return a+b;}
swift文件中Swift代碼
let p = Person(age: 10, name: "jack")
p.age = 18
p.name = "Rose"
p.run() //18 Rose - run
p.eat("Apple", other: "water")//18 Rose - eat Apple water
Person.run() //Person + run
Person.eat("Pizza", other: "Banana")//Person + eat Pizza Banana
print(sum(10, 20)) //30
OC調(diào)用Swift
- Xcode已經(jīng)默認(rèn)生成一個(gè)用于OC調(diào)用Swift的頭文件,文件名格式是:{targetName}-Swift.h
這里需要注意
的是:工程名如果帶有中劃線-
時(shí)伞剑,buildsettings的路徑變成了下劃線_
,import引用時(shí)斑唬,寫下劃線的文件名即可;工程名如果帶有下劃線_
時(shí)黎泣,buildsettings的路徑也是下劃線恕刘,引用時(shí)會(huì)報(bào)錯(cuò)找不到文件(不知道為啥,難道是項(xiàng)目名稱命名不允許用下劃線抒倚?褐着??)所以為了避免問(wèn)題吧托呕,項(xiàng)目名稱還是乖乖用大小駝峰吧:亍!项郊!哈哈哈馅扣,文件內(nèi)部放的是swift要暴露給oc的代碼,那么也不是所有的swift代碼都要暴露給oc着降,要暴漏給oc的代碼要繼承NSObject - Swift暴露給OC的類最終繼承自
NSObject
(why:因?yàn)閛c有runtime差油,runtime是要求類有isa
指針,isa
指針肯定是繼承NSObject
才有的任洞,所以最終要繼承NSObject
) - 使用
@objc
修飾需要暴露給OC的成員 - 使用
@objcMembers
修飾類
- 代表默認(rèn)所有成員都會(huì)暴露給oc(包括擴(kuò)展中定義的成員)
- 最終是否成功暴露厌殉,還需要考慮成員自身的訪問(wèn)級(jí)別
例子如下:
swift文件:
class Car: NSObject {
var price:Double
var band:String
@objc init(price:Double,band:String) {
self.price = price
self.band = band
}
func run(){
print(price,band,"run")
}
static func run(){
print("car run")
}
}
extension Car{
@objc func test(){
print(price,band,"test")
}
}
oc文件:
Car *car = [[Car alloc] initWithPrice:1.55 band:@"bannana"];
[car run]; // 報(bào)錯(cuò):No visible @interface for 'Car' declares the selector 'run'
[Car run]; //報(bào)錯(cuò):No known class method for selector 'run'
[car test];
被@objc修飾的屬性或者方法才能被調(diào)用
也可寫成如下:
swift文件:
@objcMembers class Car: NSObject {
var price:Double
var band:String
init(price:Double,band:String) {
self.price = price
self.band = band
}
func run(){
print(price,band,"run")
}
static func run(){
print("car run")
}
}
extension Car{
func test(){
print(price,band,"test")
}
}
oc文件:
Car *car = [[Car alloc] initWithPrice:1.55 band:@"bannana"];
[car run];
[Car run];
[car test];
- Xcode會(huì)根據(jù)Swift代碼生成對(duì)應(yīng)的OC聲明食绿,寫入 {targetName}-Swift.h文件
- 可以通過(guò)
@objc
重命名Swift暴露給OC的符號(hào)名(類名、屬性名公罕、函數(shù)名等)
swift文件:
@objc(XXCar)
@objcMembers class Car: NSObject {
var price:Double
@objc(name)
var band:String
init(price:Double,band:String) {
self.price = price
self.band = band
}
@objc(drive)
func run(){
print(price,band,"run")
}
static func run(){
print("car run")
}
}
extension Car{
@objc(exec)
func test(){
print(price,band,"test")
}
}
oc文件:
XXCar *car = [[XXCar alloc] initWithPrice:1.55 band:@"bannana"];
car.name = @"banban";
[car drive];
[car exec];
[XXCar run];
選擇器(Selector)
- Swift中依然可以使用選擇器,使用
#selector(name)
定義一個(gè)選擇器 - 必須是被
@objcMembers
或@objc
修飾的方法才可以定義選擇器耀销。
selector(選擇器)是依賴于runtime的楼眷,oc里才有runtime,純swift里是不存在runtime的
@objcMembers class XXPerson: NSObject {
func test1(v1:Int){
print("test1")
}
func test2(v1:Int,v2:Int) {
print("test2(v1:v2:)")
}
func test2(_ v1:Double,_ v2:Double){
print("test2(_ :_ :)")
}
func run(){
perform(#selector(test1))
perform(#selector(test1(v1:)))
perform(#selector(test2(v1:v2:)))
perform(#selector(test2(_:_:)))
perform(#selector(test2 as (Double,Double) -> Void))
}
}
p.run()底層是怎么調(diào)用的熊尉?
oc文件:
Person.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic,assign) NSInteger age;
@property (nonatomic,copy) NSString *name;
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
- (void)run;
@end
Person.m
#import "Person.h"
#import "ludan-Swift.h"
@implementation Person
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name{
if (self = [super init]) {
self.age = age;
self.name = name;
}
return self;
}
- (void)run{NSLog(@"%zd %@-run",_age,_name);}
@end
swift文件:
var p:Person = Person(age: 10, name: "jack")
p.run()
從匯編來(lái)看罐柳,走runtime的機(jī)制,objc_msgSend
反過(guò)來(lái)狰住,OC調(diào)用Swift底層又是如何調(diào)用张吉?
swift寫的類繼承自NSObject類,暴漏給OC調(diào)用催植,同樣走runtime那套機(jī)制肮蛹,objc_msgSend
car.run()底層是怎么調(diào)用的?
第一種情況:
@objcMembers class Car:NSObject{
var price:Double
var band:String
init(price:Double,band:String) {
self.price = price
self.band = band
}
func run(){
print(price,band,"run")
}
static func run(){
print("Car run")
}
}
extension Car{
func test() {
print(price,band,"test")
}
}
var car:Car = Car(price: 10.5, band: "Audi")
car.run()
在swift文件里邊調(diào)用创南,沒有走oc的runtime機(jī)制伦忠,如果想要走objc_msgSend,則在run方法前邊加上dynamic
關(guān)鍵字修飾
第二種情況(不繼承自NSObject稿辙,并且沒有@objcMembers
修飾):
class Car{
var price:Double
var band:String
init(price:Double,band:String) {
self.price = price
self.band = band
}
func run(){
print(price,band,"run")
}
static func run(){
print("Car run")
}
}
extension Car{
func test() {
print(price,band,"test")
}
}
var car:Car = Car(price: 10.5, band: "Audi")
car.run()
純swift類方法調(diào)用是走虛表v-Table那一套機(jī)制
字符串
- Swift的字符串類型
String
昆码,跟OC的NSString
,在API設(shè)計(jì)上還是有較大差異
//空字符串
var emptyStr1 = ""
var emptyStr2 = String()
var str:String = "1"
//拼接邻储,
str.append("_2")
//重載運(yùn)算符 +
str = str + "_3"
//重載運(yùn)算符 +=
str += "_4"
//\()插值
str = "\(str)_5"
//長(zhǎng)度
print(str.count)
var str = "1_2"
print(str.count,str.startIndex,str.endIndex)
//打印結(jié)果:3 Index(_rawBits: 1) Index(_rawBits: 196609)
//1_2_
str.insert("_", at: str.endIndex)
//1_2_3_4
str.insert(contentsOf: "3_4", at: str.endIndex)
//1666_2_3_4
str.insert(contentsOf: "666", at: str.index(after: str.startIndex))
//1666_2_3_8884
str.insert(contentsOf: "888", at: str.index(before: str.endIndex))
//1666hello_2_3_8884
str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))
//666hello_2_3_8884
str.remove(at: str.firstIndex(of: "1")!)
//hello_2_3_8884
str.removeAll{ $0 == "6"}
var range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex)
//hello_2_3_4
str.removeSubrange(range)
-
String
可以通過(guò)下標(biāo)赋咽、prefix
、suffix
等截取子串吨娜,子串類型不是String
脓匿,而是Substring
var str = "1_2_3_4_5"
var substr1 = str.prefix(3)
print(substr1)
var substr2 = str.suffix(3)
print(substr2)
var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3)
var substr3 = str[range]
print(substr3)
//最初的String,1_2_3_4_5
print(substr3.base)
//Substring -> String
var str2 = String(substr3)
-
Substring
和他的base
萌壳,共享字符串?dāng)?shù)據(jù) -
Substring
轉(zhuǎn)為String
時(shí)亦镶,會(huì)重新分配新的內(nèi)存存儲(chǔ)字符串?dāng)?shù)據(jù)
String與Character
for c in "jack" {//c是Character類型
print(c)
}
var str = "jack"
//c是Character類型
var c = str[str.startIndex]
String相關(guān)的協(xié)議
-
BidirectionalCollection
協(xié)議包含的部分內(nèi)容
-startIndex
、endIndex
屬性袱瓮、index
方法
-String
缤骨、Array
都遵守了這個(gè)協(xié)議 -
RangeReplaceableCollection
協(xié)議包含的部分內(nèi)容
-append
、insert
尺借、remove
方法
-String
绊起、Array
都遵守了這個(gè)協(xié)議 -
Dictionary
、Set
也有實(shí)現(xiàn)上述協(xié)議中聲明的一些方法燎斩,只是并沒有遵守上述協(xié)議
多行String
- 用
”“”
開頭虱歪,用“”“
結(jié)尾 - 如果要顯示3個(gè)引號(hào)蜂绎,至少轉(zhuǎn)義1個(gè)引號(hào)
let str = """
Escaping the first quote \"""
Escaping two quote \"\""
Escaping all three quote \"\"\"
"""
- 縮進(jìn)以結(jié)尾的3引號(hào)為對(duì)齊線
let strA = """
1
3 2
4
"""
print(strA)
//打印結(jié)果:
1
3 2
4
String與NSString
-
String
與NSString
之間可以隨時(shí)隨地橋接轉(zhuǎn)換(中間是有調(diào)函數(shù)bridge的,不是編譯器直接轉(zhuǎn)的)
- 如果覺得String
的API過(guò)于復(fù)雜難用笋鄙,可以考慮將String
轉(zhuǎn)為NSString
var str1:String = "jack"
var str2:NSString = "rose"
var str3 = str1 as NSString
var str4 = str2 as String
var str5 = str3.substring(with: NSRange(location: 0, length: 2))
print(str5)
- 比較字符串內(nèi)容是否等價(jià)
-String
使用 == 運(yùn)算符
-NSString
使用isEqual
方法师枣,也可以使用 == 運(yùn)算符(本質(zhì)還是調(diào)用了isEqual
方法) -
String
不能轉(zhuǎn)為NSMutableString
;但是NSMutableString
可以轉(zhuǎn)為String
(原因:String
與NSString
可以互相橋接萧落,String
不能橋接轉(zhuǎn)換成NSMutableString
践美,NSMutableString
是繼承自NSString
的,NSString
可以轉(zhuǎn)換成String
找岖,所以NSMutableString
可以轉(zhuǎn)換成String
)(注意:這里指的不能轉(zhuǎn)是不能直接用as
橋轉(zhuǎn))
Swift陨倡、OC橋接轉(zhuǎn)換表
swift中,類繼承自NSObject和沒有繼承自NSObject的內(nèi)存區(qū)別:
class Person{
var age = 10
var weight = 20
}
var p = Person()
p的內(nèi)存結(jié)構(gòu)為许布,第一個(gè)八個(gè)字節(jié)是metadata兴革,第二個(gè)八個(gè)字節(jié)是refcount,第三個(gè)八個(gè)字節(jié)是存放的age蜜唾,第四個(gè)八個(gè)字節(jié)存放的是weight
class Person:NSObject{
var age = 10
var weight = 20
}
var p = Person()
p的內(nèi)存結(jié)構(gòu)為第一個(gè)八個(gè)字節(jié)存放的是isa指針杂曲,第二個(gè)八個(gè)字節(jié)是age,第三個(gè)八個(gè)字節(jié)是weight灵妨,第四個(gè)八個(gè)字節(jié)是湊數(shù)(因?yàn)槎芽臻g分配的內(nèi)存長(zhǎng)度需要是16的倍數(shù))