Comments (6)
Swift 相关
from aswifttour.
Swift 混编 & 组件化
- Flutter有局限,拥抱Swift!优酷Mac迁移Swift实践
- iOS 混编 模块化/组件化 经验指北
- 网易漫画 Swift 混编实践
- 蜂鸟商家版 iOS 组件化 / 模块化实践总结
- CTMediator的Swift应用
- 从探索到落地,手淘引入 Swift “历险记”
- Swift和Objective-C混编在有赞移动的实践
- 手淘 App 如何落地 Swift ?一边探索实践,一边“打怪升级”
- 有赞移动 iOS 组件化(模块化)架构设计实践
- iOS 灵活的 模块化/组件化 工具与规范 Lotusoot 解说
2021 最新
- 百度App Objective-C/Swift 组件化混编之路(一)
- 百度App Objective-C/Swift 组件化混编之路(二)- 工程化
- 百度App Objective-C/Swift 组件化混编之路(三)- 实践篇
- 从预编译的角度理解Swift与Objective-C及混编机制 - 美团技术团队
- A站 的 Swift 实践 —— 上篇
- A站 的 Swift 实践 —— 下篇
- 京东 APP 订单业务 Swift 优化总结
- Swift环境及编译优化调研 - 京东零售技术
- 从Mach-O角度谈谈Swift和OC的存储差异 - 58 技术
- 京东App Swift 混编及组件化落地
from aswifttour.
Presentation(2019.11.18)
- 静态链接问题
- 动态链接问题
- ABI Stability 和
Always Embed Swift Standard Library
选项 - Swift 和 ObjC 混编
- OC -> Swift 迁移指南
- 模块层级划分
- 调试问题
- MCD 上 CTHotel 打包出错的问题:找不到
<CTHotelFramework/CTHotelFramework-Swift.h>
- 静态库之间的循环依赖问题
--------- 11.29 更新-----------
- 单元测试
--------- 12.12 更新-----------
- Swift 模块之间的调用
- MCD 上单独打包??
- Swift 为什么没有头文件?
xxx.swiftmodule
文件和Import Path
设置选项xx.swiftinterface
文件- 设置子工程依赖(最终方案)
--------- 2020.01.10 更新-----------
- 解决 Swift 之间模块调用时重复导入 ObjC 头文件的问题
- 具体背景:Swift 模块 A 和 B 中都导入了 ObjC 模块 X 中的
xx
类,同时模块 B 中导入了模块 A,并且调用了模块 A 中定义的函数aa
,该函数中其中有一个参数类型正是来自模块 X 的xx
类,此时,Xcode 却报错,找不到模块 A 中定义的函数aa
- 具体背景:Swift 模块 A 和 B 中都导入了 ObjC 模块 X 中的
- 单元测试模块中引用 Swift 模块时,但是没有自动补全和代码高亮
解决方案:将模块 A 中定义的 modulemap 移到一个 ObjC 模块中去,同时把所有 Swift 模块中导入桥接的 ObjC 头文件统一收到那一个模块中的 modulemap 中去(当然,理论上来讲,最好是每个 ObjC 模块都有自己对应的 modulemap 来导出自己的头文件)
from aswifttour.
Swift Documentation
目录
-
Data Modeling
- Choosing Between Structures and Classes
- Adopting Common Protocols
-
Data Flow and Control Flow
- Maintaining State in Your Apps
- Preventing Timing Problems When Using Closures
-
Language Interoperability
- Objective-C and C Code Customization
- Migrating Your Objective-C Code to Swift
- Cocoa Design Patterns
- Handling Dynamically Typed Methods and Objects in Swift
- Using Objective-C Runtime Features in Swift
- Imported C and Objective-C APIs
Data Modeling
- Choosing Between Structures and Classes
- Adopting Common Protocols
Data Flow and Control Flow
- Maintaining State in Your Apps
- Preventing Timing Problems When Using Closures
Language Interoperability
1. Objective-C and C Code Customization
1.1 Customizing Objective-C APIs
在 Objective-C 中,我们通常是通过指向对象的指针来引用对象的,这个指针的值可能是 NULL,在 ObjC 中是 nil。
但是在 Swift 中,所有的值,包括对象实例,都是 non-null 的。我们可以用一个 optional 类型来表示一个值可能是空的,同时,我们用 nil 来表示一个空值。
我们在 Swift 中引入 OC 类的声明时,可以通过给 OC 的头文件中的声明添加 nullability annotations 来说明那些参数可能是 NULL 或者 nil。
如果没有给 OC 代码添加 nullability annotations 的话,Swift 中导入的函数参数、返回值和属性就变成了 implicitly wrapped optional。
@interface MyList : NSObject
- (MyListItem *)itemWithName:(NSString *)name;
- (NSString *)nameForItem:(MyListItem *)item;
@property (copy) NSArray<MyListItem *> *allItems;
@end
||
\/
class MyList: NSObject {
func item(withName name: String!) -> MyListItem!
func name(for item: MyListItem!) -> String!
var allItems: [MyListItem]!
}
三种 nullablility 标记:
nonnull
— Imported as nonoptionals, whether annotated directly or by inclusion in an annotated regionnullable
— Imported as optionals- Without a nullability annotation or with a
null_resettable
annotation — Imported as implicitly unwrapped optionals
另外,nullable
和 nonnull
实际上分别是 _Nullable
和 _Nonnull
的简化形式,绝大多数场景下,两种形式都可以。但是如果是复杂的指针类型,比如 id *
,那就必须要使用 _Nullable
和 _Nonnull
了。
NS_ASSUME_NONNULL_BEGIN
和 NS_ASSUME_NONNULL_END
:
NS_ASSUME_NONNULL_BEGIN
@interface MyList : NSObject
- (nullable MyListItem *)itemWithName:(NSString *)name;
- (nullable NSString *)nameForItem:(MyListItem *)item;
@property (copy) NSArray<MyListItem *> *allItems;
@end
NS_ASSUME_NONNULL_END
使用宏 NS_SWIFT_NAME
给 OC 类取一个给 Swift 用的别名。
NS_SWIFT_NAME(Sandwich.Preferences)
@interface SandwichPreferences : NSObject
@property BOOL includesCrust NS_SWIFT_NAME(isCrusty);
@end
@interface Sandwich : NSObject
@end
||
\/
var preferences = Sandwich.Preferences()
preferences.isCrusty = true
枚举:
typedef NS_ENUM(NSInteger, SandwichBreadType) {
brioche, pumpernickel, pretzel, focaccia
} NS_SWIFT_NAME(SandwichPreferences.BreadType);
@interface Color : NSObject
- (void)getRed:(nullable CGFloat *)red
green:(nullable CGFloat *)green
blue:(nullable CGFloat *)blue
alpha:(nullable CGFloat *)alpha NS_REFINED_FOR_SWIFT;
@end
加了 NS_REFINED_FOR_SWIFT
之后,上面的代码被转成了:
func __getRed(_ red: UnsafeMutablePointer<CGFloat>?, green: UnsafeMutablePointer<CGFloat>?, blue: UnsafeMutablePointer<CGFloat>?, alpha: UnsafeMutablePointer<CGFloat>?)
然后再在 Swift 中重新定义一个新方法,并在这个新方法中调用原来的方法:
extension Color {
var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 0.0
__getRed(red: &r, green: &g, blue: &b, alpha: &a)
return (red: r, green: g, blue: b, alpha: a)
}
}
NS_ENUM
for simple enumerations
NS_CLOSED_ENUM
for simple enumerations that can never gain new cases
NS_OPTIONS
for enumerations whose cases can be grouped into sets of options
NS_TYPED_ENUM
for enumerations with a raw value type that you specify
NS_TYPED_EXTENSIBLE_ENUM
for enumerations that you expect might gain more cases
除了 NS_ENUM
会被转成 enum
类型之外,其他的几种宏都被转成了 struct
类型。
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
||
\/
enum UITableViewCellStyle: Int {
case `default`
case value1
case value2
case subtitle
}
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
||
\/
public struct UIViewAutoresizing: OptionSet {
public init(rawValue: UInt)
public static var flexibleLeftMargin: UIViewAutoresizing { get }
public static var flexibleWidth: UIViewAutoresizing { get }
public static var flexibleRightMargin: UIViewAutoresizing { get }
public static var flexibleTopMargin: UIViewAutoresizing { get }
public static var flexibleHeight: UIViewAutoresizing { get }
public static var flexibleBottomMargin: UIViewAutoresizing { get }
}
typedef long FavoriteColor NS_TYPED_EXTENSIBLE_ENUM;
FavoriteColor const FavoriteColorBlue;
||
\/
struct FavoriteColor: RawRepresentable, Equatable, Hashable {
typealias RawValue = Int
init(_ rawValue: RawValue)
init(rawValue: RawValue)
var rawValue: RawValue { get }
static var blue: FavoriteColor { get }
}
@available
和 #available
- Making Objective-C APIs Unavailable in Swift
NS_SWIFT_UNAVAILABLE
和 NS_UNAVAILABLE
1.2 Customizing C APIs
- Customizing Your C Code for Swift
C 语言中的结构体只有成员变量,没有方法、getter 和 setter,给 C 函数或者常量加了 CF_SWIFT_NAME
之后,可以将其关联到一个结构体类型,这样就可以转成一个 Swift 结构体的接口。
#ifndef Color_h
#define Color_h
#import <Foundation/Foundation.h>
struct Color {
float r;
float g;
float b;
float alpha;
};
typedef struct Color Color;
Color ColorCreateWithCMYK(float c, float m, float y, float k) CF_SWIFT_NAME(Color.init(c:m:y:k:)); // 构造器
float ColorGetHue(Color color) CF_SWIFT_NAME(getter:Color.hue(self:)); // 实例属性 getter
void ColorSetHue(Color color, float hue) CF_SWIFT_NAME(setter:Color.hue(self:newValue:)); // 实例属性 setter
Color ColorDarkenColor(Color color, float amount) CF_SWIFT_NAME(Color.darken(self:amount:)); // 实例方法
extern const Color ColorBondiBlue CF_SWIFT_NAME(Color.bondiBlue); // 常量
Color ColorGetCalibrationColor(void) CF_SWIFT_NAME(getter:Color.calibration()); // 类属性 getter
Color ColorSetCalibrationColor(Color color) CF_SWIFT_NAME(setter:Color.calibration(newValue:)); // 类属性 setter
#endif /* Color_h */
Swift 调用效果:
let _ = Color.init(r: 1, g: 1, b: 1, alpha: 1)
let color = Color.init(c: 2, m: 4, y: 5, k: 5)
let darkenColor = color.darken(amount: 1)
darkenColor.hue = 1
print(darkenColor.hue)
let bondiBlue = Color.bondiBlue
let _ = Color.calibration
Color.calibration = bondiBlue
2. Migrating Your Objective-C Code to Swift
-
Clean Up Your Code(准备工作)
- Add nullability annotations
- Make sure your code follows modern coding practices
-
Migrate Your Code(代码迁移 ObjC -> Swift)
- You can’t subclass Swift classes in Objective-C
- Class prefixes are optional in Swift
- Make your Swift class a descendant of an Objective-C class
@objc(name)
- See "1. Objective-C and C Code Customization"
- Denote instance
-
and class+
methods with func and class func, respectively - Declare simple macros as global constants, and translate complex macros into functions
-
Troubleshooting Tips and Reminders(注意点)
- 不能在 ObjC 中继承 Swift 类
- 在成功迁移一个 ObjC 类到 Swift 之后,需要删掉原来的 ObjC 文件,以避免出现符号重复的错误
- 如果要让一个 Swift 类能够在 ObjC 中使用,它必须继承一个 ObjC 的类
3. Cocoa Design Patterns
3.1 Using Key-Value Observing in Swift
- You can only use key-value observing with classes that inherit from NSObject.
3.2 Using Delegates to Customize Object Behavior
3.3 Managing a Shared Resource Using a Singleton
static let sharedInstance = Singleton()
static let sharedInstance: Singleton = {
let instance = Singleton()
// setup code
return instance
}()
3.4 About Imported Cocoa Error Parameters
- (BOOL)removeItemAtURL:(NSURL *)URL
error:(NSError **)error;
在 Swift 中被转成了:
func removeItem(at: URL) throws
3.5 Handling Cocoa Errors in Swift
https://developer.apple.com/documentation/swift/cocoa_design_patterns/handling_cocoa_errors_in_swift
- Catch Errors
- Throw Errors
- Throw and Catch Errors from Custom Error Domains
- Handle Exceptions in Objective-C Only
- There’s no safe way to recover from Objective-C exceptions in Swift. To handle Objective-C exceptions, write Objective-C code that catches exceptions before they reach any Swift code
4. Handling Dynamically Typed Methods and Objects in Swift
id
(Objc) == Any
(Swift)
https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html
5. Using Objective-C Runtime Features in Swift
https://developer.apple.com/documentation/swift/using_objective-c_runtime_features_in_swift
https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID563
- Use Selectors to Arrange Calls to Objective-C Methods
- Use Key Paths to Dynamically Access Objective-C Properties
6. Imported C and Objective-C APIs
6.1 Swift and Objective-C in the Same Project
6.1.1 Importing Objective-C into Swift
A. Import Code Within an App Target
在 ObjC 项目中新建 Swift 文件时或者在 Swift 项目中新建 ObjC 文件时都会自动帮你新建一个 Objective-C bridging header file,我们可以将需要暴露给 Swift 代码调用的 ObjC 的头文件在这个文件中导入。
B. Import Code Within a Framework Target
- 将 Build Settings 中的 Defines Module 选项设置为
YES
- 将需要暴露给 Swift 的 ObjC 的头文件在 Objective-C umbrella header 中导入
6.1.2 Importing Swift into Objective-C
编译器会自动给你的 Swift 代码生成一个 ProductModuleName-Swift.h
的头文件,暴露给 ObjC 使用。
A. Import Code Within an App Target
在 ObjC 代码里可以直接通过 #import "ProductModuleName-Swift.h"
来导入这个由编译器自动生成的 Swift 声明。
默认情况下,编译器生成的 ProductModuleName-Swift.h 文件只包含带有 public
或者 open
修饰符的 Swift 接口对应的 OC 接口。
如果你的 target 中有 Objective-C bridging header file 的话,那就还会为 internal
修饰符的 Swift 接口生成对应的 OC 接口。
标记了 private
或者 fileprivate
修饰符的接口声明不会出现在 ProductModuleName-Swift.h 中,也不会暴露给 ObjC Runtime,除非它们同时被被显式声明了 @IBAction
,@IBOutlet
或者 @objc
。
B. Import Code Within a Framework Target
- 将 Build Settings 中的 Defines Module 选项设置为
YES
- 在要引用 Swift 代码的 ObjC 文件中导入编译器生成的头文件
#import <ProductName/ProductModuleName-Swift.h>
C. Include Swift Classes in Objective-C Headers Using Forward Declarations
跟 ObjC 中类似,如果避免头文件循环引用,可以使用 @class
或者 @protocol
的方式来引用 Swift 类或者协议。
// MyObjcClass.h
@class MySwiftClass;
@protocol MySwiftProtocol;
@interface MyObjcClass : NSObject
- (MySwiftClass *)returnSwiftClassInstance;
- (id <MySwiftProtocol>)returnInstanceAdoptingSwiftProtocol;
// ...
@end
6.2 Cocoa Frameworks
6.2.1 Working with Foundation Types
Prefer Swift Value Types to Bridged Objective-C Reference Types
When Swift code imports Objective-C APIs, the importer replaces Foundation reference types with their corresponding value types.
Swift value type | Objective-C reference type |
---|---|
AffineTransform | NSAffineTransform |
Array | NSArray |
Calendar | NSCalendar |
Data | NSData |
... | ... |
如果确实需要在 Swift 中仍然使用引用类型,可以使用 as
关键字:
let dataValue = Data(base64Encoded: myString)
let dataReference = dataValue as NSData?
Note Renamed Reference Types
还有一些不能被桥接成 Swift 值类型的 Foundation 类型,Swift 层会将这些 class、protocol,还有相关的 enumeration 和 constant 重新命名,它们的 NS
前缀会被去掉。
比如,NSJSONReadingOptions
枚举类型就被转成了 JSONSerialization.ReadingOptions
。
不过,以下两种情况除外:
- Classes specific to Objective-C or inherently tied to the Objective-C runtime, like NSObject, NSAutoreleasePool, NSException, and NSProxy
- Platform-specific classes, like NSBackgroundActivity, NSUserNotification, and NSXPCConnection
6.2.2 Working with Core Foundation Types
什么叫 Toll-Free Bridging?
Use Memory Managed Objects
Ref
后缀被移除CFTypeRef
类型变成了AnyObject
- Core Foundation objects returned from annotated APIs are automatically memory-managed in Swift—you don't need to invoke the CFRetain, CFRelease, or CFAutorelease functions yourself.
- 针对自定义 C 函数或者 Objective-C 方法返回的 Core Foundation 对象
- 标记 CF_RETURNS_RETAINED 或者 CF_RETURNS_NOT_RETAINED
- 标记 CF_IMPLICIT_BRIDGING_ENABLED 或者 CF_IMPLICIT_BRIDGING_DISABLED
Convert Unmanaged Objects to Memory-Managed Objects
返回值为 Core Foundation 对象但是没有标记内存管理方式的函数或者方法,Swift 会将返回的对象包装成一个 Unmanaged<Instance>
类型。
例如,下面的 C 函数:
CFStringRef StringByAddingTwoStrings(CFStringRef s1, CFStringRef s2)
会被转成 Swift 代码:
func StringByAddingTwoStrings(_: CFString!, _: CFString!) -> Unmanaged<CFString>! {
// ...
}
Unmanaged<Instance>
提供了两个方法将 unmanaged object 转成一个 memory-managed object:
—takeUnretainedValue()
takeRetainedValue()
例如:
let memoryManagedResult = StringByAddingTwoStrings(str1, str2).takeUnretainedValue()
// memoryManagedResult is a memory managed CFString
6.3 Objective-C APIs
6.3.1 Using Imported Lightweight Generics in Swift
ObjC 轻量泛型的转换。
@property NSArray<NSDate *> *dates;
@property NSCache<NSObject *, id<NSDiscardableContent>> *cachedData;
@property NSDictionary <NSString *, NSArray<NSLocale *> *> *supportedLocales;
在 Swift 中被转成了:
var dates: [Date]
var cachedData: NSCache<NSObject, NSDiscardableContent>
var supportedLocales: [String: [Locale]]
@interface List<T: id<NSCopying>> : NSObject
- (List<T> *)listByAppendingItemsInList:(List<T> *)otherList;
@end
@interface ListContainer : NSObject
- (List<NSValue *> *)listOfValues;
@end
@interface ListContainer (ObjectList)
- (List *)listOfObjects;
@end
在 Swift 中被转成了:
class List<T: NSCopying> : NSObject {
func listByAppendingItemsInList(otherList: List<T>) -> List<T>
}
class ListContainer : NSObject {
func listOfValues() -> List<NSValue>
}
extension ListContainer {
func listOfObjects() -> List<NSCopying>
}
6.3.2 Using Imported Protocol-Qualified Classes in Swift
protocol composition types
@property UIViewController<UITableViewDataSource, UITableViewDelegate> * myController;
转成 Swift 之后:
var myController: UIViewController & UITableViewDataSource & UITableViewDelegate
Objective-C protocol-qualified metaclass
- (void)doSomethingForClass:(Class<NSCoding>)codingClass;
转成 Swift 之后:
func doSomething(for codingClass: NSCoding.Type)
6.4 C APIs
6.4.1 Using Imported C Structs and Unions in Swift
Structures with Default Values
struct Color {
float r, g, b;
};
typedef struct Color Color;
public struct Color {
var r: Float
var g: Float
var b: Float
init()
init(r: Float, g: Float, b: Float)
}
Unions
虽然在 Swift 中,将 C 的 Union 类型转成了 Structure,但是实际上表现还是一样的。
union SchroedingersCat {
bool isAlive;
bool isDead;
};
struct SchroedingersCat {
var isAlive: Bool { get set }
var isDead: Bool { get set }
init(isAlive: Bool)
init(isDead: Bool)
init()
}
var mittens = SchroedingersCat(isAlive: false)
print(mittens.isAlive, mittens.isDead)
// Prints "false false"
mittens.isAlive = true
print(mittens.isDead)
// Prints "true"
Bit Fields
Swift imports bit fields that are declared in structures, like those found in Foundation’s NSDecimal type, as computed properties. When accessing a computed property corresponding to a bit field, Swift automatically converts the value to and from compatible Swift types.
Unnamed Structure and Union Fields
struct Cake {
union {
int layers;
double height;
};
struct {
bool icing;
bool sprinkles;
} toppings;
};
var simpleCake = Cake()
simpleCake.layers = 5
print(simpleCake.toppings.icing)
// Prints "false"
let cake = Cake(
.init(layers: 2),
toppings: .init(icing: true, sprinkles: false)
)
print("The cake has \(cake.layers) layers.")
// Prints "The cake has 2 layers."
print("Does it have sprinkles?", cake.toppings.sprinkles ? "Yes." : "No.")
// Prints "Does it have sprinkles? No."
6.4.2 Using Imported C Functions in Swift
一般的 C 函数可以转成 Swift 中的全局函数。比如,
int product(int multiplier, int multiplicand);
int quotient(int dividend, int divisor, int *remainder);
struct Point2D createPoint2D(float x, float y);
float distance(struct Point2D from, struct Point2D to);
func product(_ multiplier: Int32, _ multiplicand: Int32) -> Int32
func quotient(_ dividend: Int32, _ divisor: Int32, _ remainder: UnsafeMutablePointer<Int32>) -> Int32
func createPoint2D(_ x: Float, _ y: Float) -> Point2D
func distance(_ from: Point2D, _ to: Point2D) -> Float
Use a CVaListPointer to Call Variadic Functions
In Swift, you can call C variadic functions, such as vasprintf(_:_:_:)
, using the Swift getVaList(_:)
or withVaList(_:_:)
functions.
func swiftprintf(format: String, arguments: CVarArg...) -> String? {
return withVaList(arguments) { va_list in
var buffer: UnsafeMutablePointer<Int8>? = nil
return format.withCString { cString in
guard vasprintf(&buffer, cString, va_list) != 0 else {
return nil
}
return String(validatingUTF8: buffer!)
}
}
}
print(swiftprintf(format: "√2 ≅ %g", arguments: sqrt(2.0))!)
// Prints "√2 ≅ 1.41421"
注:Swift 中只能导入有 va_list
参数的函数,而不能导入有 ...
参数的函数。
Call Functions with Pointer Parameters
- Swift 将 C 中的指针类型转成 Swift 中的 Pointer 类型
- 函数指针被转成了带有
@convention(c)
关键字的闭包,比如,int (*)(void)
被转成了@convention(c) () -> Int32
OpaquePointer
For return types, variables, and arguments, the following mappings apply:
C Syntax | Swift Syntax |
---|---|
const Type * | UnsafePointer |
Type * | UnsafeMutablePointer |
For class types, the following mappings apply:
C Syntax | Swift Syntax |
---|---|
Type * const * | UnsafePointer |
Type * __strong * | UnsafeMutablePointer |
Type ** | AutoreleasingUnsafeMutablePointer |
For pointers to untyped, raw memory, the following mappings apply:
C Syntax | Swift Syntax |
---|---|
const void * | UnsafeRawPointer |
void * | UnsafeMutableRawPointer |
6.4.3 Using Imported C Macros in Swift
ObjC 中普通的宏可以转成 Swift 中的全局常量:
#define FADE_ANIMATION_DURATION 0.35
#define VERSION_STRING "2.2.10.0a"
#define MAX_RESOLUTION 1268
#define HALF_RESOLUTION (MAX_RESOLUTION / 2)
#define IS_HIGH_RES (MAX_RESOLUTION > 1024)
let FADE_ANIMATION_DURATION = 0.35
let VERSION_STRING = "2.2.10.0a"
let MAX_RESOLUTION = 1268
let HALF_RESOLUTION = 634
let IS_HIGH_RES = true
另外,如果你在 Objective-C 中定义了一些包含复杂逻辑的宏,建议手动转成 Swift 中的函数或者泛型。
from aswifttour.
Swift 静态库之间的相互调用问题(组件化)
1. Swift 没有头文件,那么在跨模块调用的场景下,要单独编译一个静态库怎么办?
这个时候如果直接 import CTHotelFramework
,就会报错 No such module 'CTHotelFramework'
。
解决办法是创建 Swift 静态库时需要导出一堆 .swiftmodule
文件。
参考:
- 怎么创建静态库 https://medium.com/better-programming/create-swift-5-static-library-f1c7a1be3e45
- 一个 Swift 模块包含哪些东西?http://andelf.github.io/blog/2014/06/19/modules-for-swift/
其他:
- 怎么查看 swiftmodule 文件?
- 可以用 MachOView 查看 swiftmodule 文件
- 静态库和动态库的区别 https://www.jianshu.com/p/42891fb90304
- 关于 Library 和 Framework https://hechen.xyz/post/library_framework/
- 官方文档
- 详细介绍了 Framework,包括 Framework 和一般的 library 的区别: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html
- https://developer.apple.com/library/archive/technotes/tn2435/_index.html
2. import 混编的模块之后提示Implicit import of bridging header 'CTHotelFramework-Bridging-Header.h' via module 'CTHotelFramework' is deprecated and will be removed in a later version of Swift
13:53:02 /Users/cbuilder/jenkins_slaves/slave_216/workspace/IOS_BUNDLE/wirelesscode/app-8.18.0/rel/8.18/IOS_2/CTHotelViewModel/CTHotelViewModel/Request/HotelSOTPCachePolicy.swift:10:8: warning: implicit import of bridging header 'CTHotelFramework-Bridging-Header.h' via module 'CTHotelFramework' is deprecated and will be removed in a later version of Swift
13:53:02 import CTHotelFramework
13:53:02 ^
13:53:02 /Users/cbuilder/jenkins_slaves/slave_216/workspace/IOS_BUNDLE/wirelesscode/app-8.18.0/rel/8.18/IOS_2/CTHotelViewModel/CTHotelViewModel/Request/HotelSOTPCachePolicy.swift:10:8: error: failed to import bridging header '/Users/xianglongchen/Desktop/CtripGitRepo/IOS_2/CTHotelFramework/CTHotelFramework/CTHotelFramework-Bridging-Header.h'
13:53:02 import CTHotelFramework
13:53:02 ^
参考:
解决办法之一,就是在当前 target(也就是 CTHotelViewModel)的 bridging header 中显式导入 CTHotelFramework-Bridging-Header.h
3. 但是 MCD 上打 CTHotelViewModel 的静态库仍然报错了,接口声明重复了
/Users/xianglongchen/Desktop/CtripGitRepo/IOS_2/CTHotelFramework/CTHotelFramework/CTHotelFramework-Bridging-Header.h:7:2: note: in file included from /Users/xianglongchen/Desktop/CtripGitRepo/IOS_2/CTHotelFramework/CTHotelFramework/CTHotelFramework-Bridging-Header.h:7:
10:02:59 # 1 "/Users/xianglongchen/Desktop/CtripGitRepo/IOS_2/CTHotelFramework/../CTBaseFramework/CTBaseFramework/CTBaseBusiness/CTBaseBusiness/CTService/CTABTestResultModel.h" 1
10:02:59 ^
10:02:59 /Users/xianglongchen/Desktop/CtripGitRepo/IOS_2/CTHotelFramework/../CTBaseFramework/CTBaseFramework/CTBaseBusiness/CTBaseBusiness/CTService/CTABTestResultModel.h:31:40: error: property has a previous declaration
10:02:59 @property(nonatomic,strong) NSString * expResult;
10:02:59 ^
10:02:59 /Users/cbuilder/jenkins_slaves/slave_196/workspace/IOS_BUNDLE/wirelesscode/app-8.18.0/rel/8.18/IOS_2/CTHotelViewModel/../CTBaseFramework/CTBaseFramework/CTBaseBusiness/CTBaseBusiness/CTService/CTABTestResultModel.h:31:40: note: property declared here
10:02:59 @property(nonatomic,strong) NSString * expResult;
####4. Swift 5.1 编译的接口在 Swift 5.1.2 上不能用(Module compiled with Swift 5.1 cannot be imported by the Swift 5.1.2 compiler
)
You need to set the
Build Libraries for Distribution
option toYes
in your framework's build settings, otherwise the swift compiler doesn't generate the neccessary.swiftinterface
files which are the key to future compilers being able to load your old library.
但是又报了另外一个错误:error: using bridging headers with framework targets is unsupported
。
https://stackoverflow.com/questions/24875745/xcode-6-beta-4-using-bridging-headers-with-framework-targets-is-unsupported 给的解释是,根据官方文档 Import Code Within a Framework Target:
To use the Objective-C declarations in files in the same framework target as your Swift code, you’ll need to import those files into the Objective-C umbrella header—the master header for your framework. Import your Objective-C files by configuring the umbrella header:
Under Build Settings, in Packaging, make sure the Defines Module setting for the framework target is set to Yes.
In the umbrella header, import every Objective-C header you want to expose to Swift.
解决方案如下:
- Remove your bridging header file.
- Remove references to the bridging header file in the build settings for the framework
- Add the necessary headers to your umbrella file ([ProductName].h)
- Make the included files public in the framework's "Headers" section of its "Build Phases".
- Clean and rebuild.
但是 modulemap 文件是什么呢?怎么加呢?
- modulemap 一个库的所有头文件的结构化描述
- 怎么添加?
- 按照上面的步骤创建
module.modulemap
文件,语法参考 LLVM 官方文档 - 设置 Import Path
- 按照上面的步骤创建
参考:
- https://medium.com/allatoneplace/challenges-building-a-swift-framework-d882867c97f9
- https://clang.llvm.org/docs/Modules.html#header-declaration
- https://hechen.xyz/post/swift-and-modules/
- Understanding Objective-C Modules
- 一个 Swift 模块包含哪些东西?http://andelf.github.io/blog/2014/06/19/modules-for-swift/
加好之后再编译一下 CTHotelFramwork,最后我们可以看到 .swiftmodule
目录下多了几个 xxx.swiftinterface 文件。
延伸:
- Module Stability
小结:
- 问题 2、3、4 本质上是同一个问题,也就是 llvm Module 系统的问题。
- CTHotelFramwork 中虽然只有 Swift 代码,只是通过 bridging header 引用了 ObjC 的代码,但是其实它还是一个 Swift-ObjC 混编的静态库。
- 一个 Swift 模块包含哪些东西?http://andelf.github.io/blog/2014/06/19/modules-for-swift/
5. 编译时生成不同的 CPU 架构对应的 xx.swiftinterface
在 CMD+R/CMD+B 编译时执行 xcodebuild
脚本再编译一次?
6. 更完美的方案
将 CTHotelFramework 同时添加为 CTHotelViewModel 的子工程,这样就可以将 CTHotelFramework 设置为它的 dependencies 之一了,但是不要添加 linked library,在主工程里面 link CTHotelFramework 就行了。
这样就不再需要每次修改 CTHotelFramework 并编译之后再重新 copy swiftmodule 和 -swift 文件到代码目录下了,实现了真正的编译依赖。
from aswifttour.
在 ObjC 项目中使用 Swift 静态库时出现了调试问题
$ po itemId
Cannot create Swift scratch context (couldn't load the Swift stdlib).Shared Swift state for CTRIP_WIRELESS could not be initialized. The REPL and expressions are unavailable.
参考:
- https://developer.apple.com/library/archive/qa/qa1947/_index.html
- https://stackoverflow.com/questions/3221504/xcode-debugger-sometimes-doesnt-display-variable-values
- https://forums.swift.org/t/debugging-sourcekit-lsp-using-lldb/28328/5
from aswifttour.
Related Issues (15)
- Swift Package Manager HOT 1
- 最佳实践(Best Practices) HOT 1
- Debugging HOT 1
- Talks, posts and questions HOT 3
- JSON 解析
- OC和siwft混编,脚本生成XXX-swift.h,脚本报错
- SwiftUI HOT 1
- 值类型(struct) VS. 引用类型(class) HOT 2
- WWDC(Swift 相关) HOT 1
- Swift 5.x HOT 3
- Swift 编译 HOT 1
- Swift 性能 HOT 2
- 语言特性 HOT 10
- Swift Memory Layout 和 Runtime HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from aswifttour.