從OC到Swift (一)

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)赋咽、prefixsuffix等截取子串吨娜,子串類型不是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)容
    - startIndexendIndex屬性袱瓮、index方法
    - String缤骨、Array都遵守了這個(gè)協(xié)議
  • RangeReplaceableCollection 協(xié)議包含的部分內(nèi)容
    - appendinsert尺借、remove方法
    - String绊起、Array都遵守了這個(gè)協(xié)議
  • DictionarySet 也有實(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

  • StringNSString之間可以隨時(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(原因:StringNSString可以互相橋接萧落,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ù))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末解阅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泌霍,更是在濱河造成了極大的恐慌货抄,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朱转,死亡現(xiàn)場(chǎng)離奇詭異蟹地,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)藤为,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門怪与,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人缅疟,你說(shuō)我怎么就攤上這事分别。” “怎么了存淫?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵耘斩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我桅咆,道長(zhǎng)括授,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮荚虚,結(jié)果婚禮上薛夜,老公的妹妹穿的比我還像新娘。我一直安慰自己版述,他們只是感情好梯澜,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著渴析,像睡著了一般腊徙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上檬某,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音螟蝙,去河邊找鬼恢恼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胰默,可吹牛的內(nèi)容都是我干的场斑。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼牵署,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼漏隐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起奴迅,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤青责,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后取具,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脖隶,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年暇检,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了产阱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡块仆,死狀恐怖构蹬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悔据,我是刑警寧澤庄敛,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蜜暑,受9級(jí)特大地震影響铐姚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一隐绵、第九天 我趴在偏房一處隱蔽的房頂上張望之众。 院中可真熱鬧,春花似錦依许、人聲如沸棺禾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)膘婶。三九已至,卻和暖如春蛀醉,著一層夾襖步出監(jiān)牢的瞬間悬襟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工拯刁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脊岳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓垛玻,卻偏偏與公主長(zhǎng)得像割捅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子帚桩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容