GithubHelp home page GithubHelp logo

Swift-ObjC 混编 about aswifttour HOT 6 OPEN

shannonchenchn avatar shannonchenchn commented on August 22, 2024
Swift-ObjC 混编

from aswifttour.

Comments (6)

ShannonChenCHN avatar ShannonChenCHN commented on August 22, 2024

Swift 相关

from aswifttour.

ShannonChenCHN avatar ShannonChenCHN commented on August 22, 2024

Swift 混编 & 组件化

2021 最新

from aswifttour.

ShannonChenCHN avatar ShannonChenCHN commented on August 22, 2024

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 中定义的 modulemap 移到一个 ObjC 模块中去,同时把所有 Swift 模块中导入桥接的 ObjC 头文件统一收到那一个模块中的 modulemap 中去(当然,理论上来讲,最好是每个 ObjC 模块都有自己对应的 modulemap 来导出自己的头文件)

from aswifttour.

ShannonChenCHN avatar ShannonChenCHN commented on August 22, 2024

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

  1. Designating Nullability in 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 region
  • nullable — Imported as optionals
  • Without a nullability annotation or with a null_resettable annotation — Imported as implicitly unwrapped optionals

另外,nullablenonnull 实际上分别是 _Nullable_Nonnull 的简化形式,绝大多数场景下,两种形式都可以。但是如果是复杂的指针类型,比如 id *,那就必须要使用 _Nullable_Nonnull 了。

NS_ASSUME_NONNULL_BEGINNS_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
  1. Renaming Objective-C APIs for Swift

使用宏 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);
  1. Improving Objective-C API Declarations for Swift
@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)
    }
}
  1. Grouping Related Objective-C Constants

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 }
}
  1. Marking API Availability in Objective-C

@available#available

  1. Making Objective-C APIs Unavailable in Swift

NS_SWIFT_UNAVAILABLENS_UNAVAILABLE

1.2 Customizing C APIs

  1. 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 的类
  • 关于@objc@objcMembers@nonobjc

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.

ShannonChenCHN avatar ShannonChenCHN commented on August 22, 2024

Swift 静态库之间的相互调用问题(组件化)

1. Swift 没有头文件,那么在跨模块调用的场景下,要单独编译一个静态库怎么办?

这个时候如果直接 import CTHotelFramework,就会报错 No such module 'CTHotelFramework'

解决办法是创建 Swift 静态库时需要导出一堆 .swiftmodule 文件。

参考

其他

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

https://stackoverflow.com/questions/58654714/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 to Yes 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.

解决方案如下:

  1. Remove your bridging header file.
  2. Remove references to the bridging header file in the build settings for the framework
  3. Add the necessary headers to your umbrella file ([ProductName].h)
  4. Make the included files public in the framework's "Headers" section of its "Build Phases".
  5. Clean and rebuild.

但是 modulemap 文件是什么呢?怎么加呢?

  • modulemap 一个库的所有头文件的结构化描述
  • 怎么添加?
    • 按照上面的步骤创建 module.modulemap 文件,语法参考 LLVM 官方文档
    • 设置 Import Path

参考

加好之后再编译一下 CTHotelFramwork,最后我们可以看到 .swiftmodule 目录下多了几个 xxx.swiftinterface 文件。

延伸

小结

  • 问题 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.

ShannonChenCHN avatar ShannonChenCHN commented on August 22, 2024

在 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.

debugging_variables

参考:

from aswifttour.

Related Issues (15)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.