百度App Objective-C/Swift 组件化混编之路(二)- 工程化

发表于 3年以前  | 总阅读数:782 次

前文《百度App Objective-C/Swift 组件化混编之路》已经介绍了百度App 引入 Swift 的影响面评估以及落地的实施步骤,本文主要以依赖管理工具为支撑,介绍百度App 如何实现组件内的 Objective-C/Swift 混编、单测、二进制发布和集成,以及组件间的依赖和引用。

百度App 自研的依赖管理工具 EasyBox 工具链已经把混编作为功能子集,如果你感兴趣,可以阅读百度App 技术公众号往期文章《百度App iOS工程化实践: EasyBox破冰之旅》。掌握 Xcode 编译、链接选项等相关知识点,有助于理解混编的实现过程。

一. 组件Target类型 和 Module化

为解决大规模并行开发问题,百度App 将工程进行了组件化拆分,并实现组件的二进制化,一个组件即为一个独立的功能单元和编译单元,具有两种形态,源码形态和二进制形态,开发过程中可以按需进行组件的源码/二进制切换。所以我们要解决这两种形态下的组件内混编和组件间混合调用问题。

在介绍混编之前,我们先来了解两个重要的概念:组件 Target 类型和 Module。

1.1 组件 Target 类型

EasyBox 工具链会为源码形态的组件生成一个 Xcode 子工程和对应的 Target,Target 可以是以下类型中的一种:

  • dynamic_library:动态库,Xcode 7 之前扩展名为 .dylib, Xcode 7 后是 .tbd ;目前官方环境并不允许为 iOS 平台添加这种类型。
  • static_library:静态库,扩展名 .a
  • static_framework:静态库,扩展名 .framework
  • dynamic_framework:动态库,扩展名 .framework

.a 与 .framework 的区别是:Framework 是分层目录,它将共享资源(例如动态共享库,nib 文件,图像文件,本地化字符串,头文件和参考文档)封装在一个程序包中。动态库与静态库的区别是:系统根据需要将动态库加载到内存中,可以被多个应用程序同时访问,并在所有可能的应用程序之间共享资源的一份副本。静态库则是链接到某个应用程序的二进制中。

这些 Target 可能还存在一个或多个伴生 Target :

  • bundle
  • octest_bundle
  • unit_test_bundle
  • ui_test_bundle

What's the Xcode target?

https://developer.apple.com/library/archive/featuredarticles/XcodeConcepts/Concept-Targets.html

对于伴生 Target,与 Swift 混编相关的只有单测;而对于主 Target,按照 Target 的文件组织形式可以分两类:

  • Library(扩展名为 .a)
  • Framework(扩展名为 .framework)

当 Target 中只有 Objective-C 源码(.h、.m)时,无论哪种 Target,源文件之间都可以通过 import 头文件的方式进行引用,但 Swift 语言是强制以 module 形式 引用的,所以在 Swfit 中需要将 Target 的产物转换为一个独立的 module,供其他 module 依赖并引用。所以要实现 Swift 混编,每个组件对应的主 Target (源码或二进制)都必须以一个 module 的形式存在。下面介绍如何实现 Target 内的 module 混编、以及 Target 之间的 module 依赖。

1.2 Module 化

1.2.1 基本概念

  • module:是一个编译单元,或构建产物,对一个软件库的结构化替代封装,供链接器使用(更多介绍请查阅 Clang-Module:https://clang.llvm.org/docs/Modules.html#introduction)
  • umbrella header:module 对外公开的根头文件,包含了这个 module 中所有其他公开头文件的引用。以 Foundation 框架的根头文件 <span style="font-size: 16px;color: rgb(99, 99, 99);"><Foundation/Foundation.h>为例:

对编译器来讲,每次编译过程一个 module 只会加载一次,避免多次引入并加载相同的头文件带来的编译耗时问题。所以 module 化后编译效率更高。

  • modulemap:描述 module 和 module header 间的关系,描述现有 header 如何映射到 module 的逻辑结构。modulemap 结构如下:
framework module SwiftOCMixture {
  umbrella header "SwiftOCMixture.h"

  export *
  module * { export * }
}


module SwiftOCMixture.Swift {
    header "SwiftOCMixture-Swift.h"
    requires objc
}

ModuleMap采用模块映射语言,但是到现在( 2020 年 Q3 为止)该语法依然不够稳定,所以建议:编写 modulemap 时需要尽可能使用少的关键字实现 module 功能,比如 framework、umbrella、header、extern、use。

建议 modulemap 内声明一个umbrella header,便于快速引用对应的头文件,但必须将所有公开的头文件填充到 umbrella header 文件内。否则将得到一个警告:

<module-includes>
Umbrella header for module 'XXX' does not include header 'absolute path to a public header'

不包含 umbrella header 的 module ,modulemap 中不必添加 <span style="font-size: 14px;">module * { export * } 包含 umbrella header 的 framework,不用配置任何(包括 MODULEMAP_FILE )即可自动 module 化

1.2.2 module 相关的 build setting 参数

上古时期,程序员通过 Makefile 来控制程序的编译链接过程。现如今在 IDE 的封装下,复杂度大大降低,只需要通过 IDE 来控制关键变量和自定义变量,在 Xcode 中,这个控制变量被称为 build setting,build setting 和 Module 化相关的变量主要有这些:- 对module自身的描述:

  • DEFINES_MODULE:YES/NO,module 化需要设置为 YES

  • MODULEMAP_FILE:指向 module.modulemap 路径

  • HEADER_SEARCH_PATHS:modulemap 内定义的 Objective-C 头文件,必须在 HEADER_SEARCH_PATHS 内能搜索到

  • PRODUCT_MODULE_NAME:module 名称,默认和 Target name 相同

  • 对外部module的引用:

  • FRAMEWORK_SEARCH_PATHS:依赖的 Framework 搜索路径

  • OTHER_CFLAGS:编译选项,可配置依赖的其他 modulemap 文件路径 -fmodule-map-file=${modulemap_path}

  • HEADER_SEARCH_PATHS:头文件搜索路径,可用于配置源码中引用的其他 Library 的头文件

  • OTHER_LDFLAGS:依赖其他二进制的编译依赖选项

  • SWIFT_INCLUDE_PATHS:swiftmodule 搜索路径,可用于配置依赖的其他 swiftmodule

  • OTHER_SWIFT_FLAGS:Swift 编译选项,可配置依赖的其他 modulemap 文件路径 -Xcc -fmodule-map-file=${modulemap_path}

本文的后续部分也会用到 build setting 中的其他关键变量。

1.2.3 非 framework 的 module 处理

包含 Swift 源码的非 framework 的 module,建议在 buildphase 的 script 里处理编译后的两个事情:

  • 编译生成的 interface header,拷贝作为公开头文件,供其他 Target 访问编译生成的 Swiftmodule,配置追加到 modulemap 文件中

至此,我们已经了解了单个组件的 module 化过程。

二. 组件内混编

根据官方说明,Target 内支持 Objective-C 和 Swift 语言的混编,无外乎解决两个问题:

  • Objective-C 可以引用 Swift 的类和方法
  • Swift 可以引用 Objective-C 的类和方法

下面我们针对 Framework 和 Library(非 Framework 静态库)两种类型,分别介绍下组件内的混编实现。

2.1 Framework

针对 Framework 类型的 Target 内混编,我们要做的就是什么都不做

简单吧,对于全新生成的有 umbrella header 的 Framework 默认就是 Module化 的,不需要做任何操作即可实现 Target 内混编。对于没有umbrella header的Framework,需要参照 如何实现 Module化 进行 Module 化改造。

  • Objective-C 引用 Swift 在头文件内添加引入 Swift 的 Interface 头文件即可,可以访问 Swift 中以 @objc public@objc open 修饰的类和方法,或者 class 修饰为 @objcMembers public

    #import <xxx/${ModuleName}-Swift.h>

因为 Xcode 在编译时已经对 framework 进行 Module 化处理,并自动生成该 Interface 头文件,编译成功时拷贝 Headers 文件夹内

  • Swift 引用 Objective-C 直接使用对应的类和方法

2.2 Library

针对 Library 类型的 Target 内混编,我们首先依然需要参照如何实现 Module 化改造。

  • Objective-C 引用 Swift 与 Framework 的引用方式一致,在头文件内添加引入 Swift 的 Interface 头文件即可,可以访问 Swift 中以 @objc 修饰的类和方法,或者 class 修饰为 @objcMembers
  • Swift 引用 Objective-C 有显式和隐式两种方式 1、通过显式配置桥接文件 BridingHeader,在桥接文件内 import 对 Swift 类公开的头文件,用于 Swift 访问 Objective-C 头文件 (Importing Objective-C into Swift:https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_objective-c_into_swift)

不足:无法开启跨 Swift 版本兼容的功能

  • OTHER_SWIFT_FLAGS 的标记:-import-underlying-module 该构件标记由 Xcode 隐式创建下层 Module,并隐式引入当前 Module 内所有的 Objective-C 的公开头文件,Swift 可以直接访问。该标记需要配合 USER_HEADER_SEARCH_PATHS 或者 HEADER_SEARCH_PATHS 来搜索当前 module 所需的公开头文件
OTHER_SWIFT_FLAGS = $(inherited) -import-underlying-module

不足:因为隐式创建下层 module,也会将 Swift 的类和方法包含到 Swift 的 Interface 头文件中,需要在 Swift 的类和方法之前添加 <span style="font-size: 14px;">@objc open,经测试发现,这样会造成 module 将近一秒延迟(即修改 Swift 的部分接口后 Interface 文件不立即变更)。

三. 组件间依赖

组件间依赖调用的核心依然是 Module 化,否则 Swift 无法调用其他组件,下面介绍组件间依赖调用相关的 Build Settings 参数。

单测也是组件间依赖的一种,单测的 Target 依赖其他需要测试的组件,并且该组件以源码形态集成

集成单测,除了配置组件间依赖的 Build Settings ,还需要注意两个要点

  • 第一,需要链接对应的静态库到目标 testbundle
  • 第二,如果当前单测是 Objective-C 源码,而依赖的库文件包含 Swift 相关的库或 Target,必须在单测的 Target 内添加空的 Swift 占位源文件(空文件真的可以,后缀为 .swift),否则链接时会报错。

3.1 依赖 Framework 组件

如果依赖组件的Target类型是Framework,So Easy,因为Framework已经是一个module了(包含umbrella header),直接配置BuildSettings:

  • FRAMEWORK_SEARCH_PATHS: 依赖的Framework搜索路径,在对应的路径下查找<xxx.framework文件
  • OTHER_LDFLAGS:当依赖的组件是源码时,可以有效将依赖的组件顺序编译,根据Xcode 10.2的升级说明(https://developer.apple.com/documentation/xcode-release-notes/xcode-10_2-release-notes)
// 当依赖组件是二进制时,可以不用设置该项
OTHER_LDFLAGS = $(inherited) -framework xxxA -framework xxxB ...

3.2 依赖Library组件

当依赖组件的Target类型是Library,配置稍微复杂一点:

3.2.1 当前组件包含Objective-C源码

  • OTHER_CFLAGS:配置当前Target依赖的其他Module
OTHER_CFLAGS = $(inherited) -fmodule-map-file="${path_dir}/xxxA/module.modulemap" -fmodule-map-file="${path_dir}/xxxB/module.modulemap" ...
  • OTHER_LDFLAGS:同 3.1 依赖 Framework 组件
OTHER_LDFLAGS = $(inherited) -l"xxxA" -l"xxxB" ...
  • HEADER_SEARCH_PATHS:配置当前 Target 的头文件搜索路径,包含依赖的其他 Module 内配置的头文件搜索路径
HEADER_SEARCH_PATHS = $(inherited) "${xxxA_public_header_dir}" "${xxxB_public_header_dir}" ...

3.2.2 当前组件包含 Swift 源码

  • OTHER_SWIFT_FLAGS;配置当前 Target 依赖的其他 Module
OTHER_CFLAGS = $(inherited) -Xcc -fmodule-map-file="${path_dir}/xxxA/module.modulemap" -Xcc -fmodule-map-file="${path_dir}/xxxB/module.modulemap" ...

3.2.3 依赖 swiftmodule

当依赖的 Library 中包含 Swift 源码,那么该源码编译后将生成 swiftmodule,或依赖 Library 二进制中包含 swiftmodule,那么当前组件需要配置:

  • SWIFT_INCLUDE_PATHS:依赖组件 swiftmodule 的搜索路径,需要配置该路径,目录下包含 *.swiftmodule
SWIFT_INCLUDE_PATHS = $(inherited) "${xxxA_swift_module_dir}" "${xxxB_swift_module_dir}" ...

3.2.4 编译顺序控制

当依赖的组件是 Library,并且包含 Swift 的源码,需将当前 Target 的 Scheme 编译条件配置为非并行编译 uncheck Parallelize Build(如下图所示),达到控制编译顺序的目的,避免因为依赖组件还未生成的 *-Swift.h 文件(依赖组件编译成功后生成),造成当前组件源码的编译错误。

四. 混编组件二进制打包

为了提升产品线的编译速度,业界内很多产品线均做了组件二进制化,即将组件源码编译为多种架构的二进制,并合并架构后以二进制的方式引入工程,避免了大量源码的重复编译,提升编译效率,对于 Swift 的组件来说,如何做二进制化?

4.1 module 化

参考 1.2 Module 化要点

4.2 兼容性

虽然 ABI 稳定了,但是根据 Swift 的设计,各自 Swift 编译器打出的二进制并不能在其他版本使用,需要使用到跨 Swift 版本调用的 interface 文件(在编译产物 swiftmodule 文件夹中),设置 BUILD_LIBRARY_FOR_DISTRIBUTION = YES 即可生成,但该标记与bridging 冲突,即在混编的 Library 且使用 bridging header 的工程中不可用;如果真要使用 Library 又想 Swift 二进制跨 Swift 版本兼容,参考 2.2 介绍的 -import-underlying-module

4.3 SWIFT_OBJC_INTERFACE_HEADER 文件合并

对于 Framework ,Swift 源码编译产生的 Objective-C Interface 文件会被自动拷贝到公开头文件夹,只需要合并多架构 Interface 头文件即可;但对于 Library 则需要先手动移动头文件再合并 Interface 头文件,建议在 BuildPhase 添加 Script Phase 在编译完成后拷贝操作:

// 仅供参考
COMPATIBILITY_HEADER_PATH="${公开头文件目录}/${PRODUCT_MODULE_NAME}-Swift.h"
ditto "${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h" "${COMPATIBILITY_HEADER_PATH}"

不同架构的 *-Swift.h 文件的合并方式:

  1. <span style="font-size: 14px;">#ifdef 架构 的方式进行(当各架构提供的接口没有区别的情况下,可直接使用模拟器架构)
  2. 合并为 XCFramework 的形式

4.4 swiftmodule文件合并

对于包含 Swift 源码的产物中将包含 swiftmodule 文件夹,直接合并两个 swiftmodule 目录即可,不同架构以不同的文件名呈现

对于开启 BUILD_LIBRARY_FOR_DISTRIBUTION 的 module 来说,swiftmodule 文件夹内包含 *.interface 即为跨 Swift 版本兼容文件

4.5 合并二进制

使用 lipo 命令进行二进制架构的常规合并,这里不做赘述

4.6 二进制包

如下图:模拟器架构 Framework 形态的 *.swiftmodule(.a的 *.swiftmodule与之类似),其中 x86_64-apple-ios-simulator.swiftinterface是跨 Swift 版本调用的 interface 文件

4.7 小知识:swiftmodule 的传递依赖性

已知:有组件 A 依赖组件 B,组件 B 依赖组件 C 在 Objective-C 中,B 对外暴露的头文件中引用了 C 的公开头文件,我们叫组件 B 传递依赖 C,结果就是编译组件 A 时必须同时能找到组件 B 和组件 C 的头文件,否则编译失败。

然而 Swift 并没有公开头文件一说,只要组件 B import C,导致 swiftmodule 中也明确标记了 import C,当组件 A import B 时,也同时 import C ,如果组件 A 找不到组件 C 的 module,那组件 A 将编译失败。

五. 总结

对于百度App 的开发者来说,不用去关心混编的是如何实现的,只需要跟正常开发一样,组件内引用所需的头文件(#import <ModuleXX/xx.h>)或module(@import ModuleXX),组件间在声明依赖后亦可直接引用头文件或 module ,EasyBox 工具链会根据源码文件或配置进行module 化和 Xcode Build setting 相关的处理,以下情况将判定为需要 module 化:

  • 存在 .swift 的源码文件的组件
  • 存在 .swiftmodule 或 *-Swift.h 文件的二进制组件
  • 宿主工程的 Boxfile 中显式配置 module 化
  • 组件的 boxspec 描述中声明 modulemap 文件

对于混编组件的二进制打包,开发者们也不用去关心如何处理编译产物,诸如 *-Swift.h、二进制架构、*.swiftmodule*.interface等,EasyBox 工具链打包命令 box package 会全权处理,降低开发者们的配置难度和协同成本。

六. 常见问题

6.1 Swift 组件内调用 Objective-C,只能调用 Objective-C 的公开头文件,就不能调用私有头文件吗?

  • 如果组件以源码的方式被集成,是可行的

  • Framework 中将私有头文件声明为一个私有 module(modulemap内声明),由组件内的 Swift 源码 import 该私有 module 即可

  • Library 中使用 bridging header

  • 如果组件是以二进制方式被集成,则不可以

  • 集成 Framework 二进制,由于 Swiftmodule 的传递依赖的这个特性,这种调用方式将导致其他组件依赖这个组件的二进制时,无法找到对应的私有 module,导致编译失败

  • 集成 Library 二进制,由于编译二进制时无法同时开启 Bridging Header 和 BUILD_LIBRARY_FOR_DISTRIBUTION,开启 Bridging Header 后该二进制将无法在不同的 Swift 版本下被集成

6.2 到底使用 Framework 还是 Library?

建议直接全部使用 Framework ,因为 Framework 针对 Swift 混编支持非常简单

对于最低支持版本在 iOS8 及以下的 App,由于 Apple 限制 ipa 中二进制包大小为 80M,为了缩小二进制体积,一般都采用内置动态库,如果动态库也建议使用 Framework,而非动态库的 Library

6.3 App 链接一个 Swift 二进制时报错?

当一个组件或产物需要链接其他 Swift 的产物时,比如 App、单测、动态库等,需要告诉 Xcode 开启 Swift 链接功能,开启方法就是添加一个 Swift 文件,否则报错。

七. 参考

  • 官方文档

    https://swift.org

  • What are Frameworks?

    https://developer.apple.com/library/archive/documentation/MacOSX/ Conceptual/BPFrameworks/Concepts/WhatAreFrameworks.html

  • Clang Module

    http://clang.llvm.org/docs/Modules.html

  • Importing Objective-c Into Swift

    https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_objective-c_into_swift

  • Xcode Release Notes

    https://developer.apple.com/documentation/xcode_release_notes

  • Xcode Build Settings

    https://xcodebuildsettings.com/#category-core-build-system

本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/s0RZrMQQKVdxLS4Ni8yO6g

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237231次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8065次阅读
 目录