├── .DS_Store ├── .gitignore ├── Animation ├── .DS_Store ├── Core Graphics学习笔记.md ├── CoreAnimation笔记.md ├── Images │ ├── animation_cubicgraph.png │ ├── bounceanimation_animationcurve.png │ ├── ca_architecture.png │ └── customtimingfunction.png ├── iOS中的CGAffineTransform.md ├── iOS动画技巧.md └── 《iOS Core Animation Advanced Techniques》笔记.md ├── App's lifecycle in iOS.md ├── AttributedString in iOS.md ├── Background Execution in iOS.md ├── Block-Closure in iOS function缩进.md ├── Cache ├── CoreData in iOS.md ├── FileSystem in iOS.md ├── NSCache笔记.md ├── Persistence in iOS.md └── SQLite vs Realm in iOS.md ├── Class initializer in Objective C and Swift.md ├── Closure ├── Block in Objective-C温故.md ├── Objective-C block 深入了解.md └── 碾压面试之再提Block.md ├── Codable.md ├── CodingGuideline ├── Objective-C 注释文档笔记.md ├── 《Coding Guidelines for Cocoa》笔记.md └── 《Google Objective-C Style Guide》笔记 .md ├── Copy in Objective-C.md ├── Crash ├── .DS_Store ├── iOS Crash分析.md └── 对iOS中崩溃防护的思考.md ├── Customize Presentation in iOS.md ├── Daily Questions └── Compiler error- "initializer element is not a compile-time constant".md ├── Functional-Reactive-Programing ├── Functional Programing in Swift.md ├── Reactive Programing.md ├── images │ ├── operator-flatmap.png │ ├── operator-scan.png │ └── operator-skipwhile@2x.png └── 《RxSwift, Reactive Programing with Swift》笔记.md ├── Gemfile in iOS.md ├── IGListKit实践笔记.md ├── Images ├── .DS_Store ├── AFNetworking_architecture.jpg ├── CA认证服务器公钥过程.png ├── CombinePipeline-1.png ├── Libraries │ ├── .DS_Store │ ├── pod_app_link_dynamicpod.png │ ├── pod_app_link_staticpod.jpg │ ├── pod_app_otherlinkflag.png │ ├── pod_dynamiclibrary.png │ ├── pod_dynamiclibrary_umbrellaheader.png │ └── pod_staticpods.png ├── OC_MessageForwarding.png ├── PromiseKit_classes.png ├── PromiseKit_demo_1.png ├── PromiseKit_demo_2.png ├── PromiseKit_demo_3.png ├── PromiseKit_demo_4.png ├── ReactiveCocoaPipeline-2.png ├── Socket_architecture.png ├── Socket_communication_process.png ├── Timer_vs_CADisplayLink.png ├── animation_begintime.png ├── app-state_dark.png ├── application_feedback_loop.png ├── auto_Lint.sh ├── autoreleasepool_linkedlist.jpg ├── autoreleasepool_pop.jpg ├── autoreleasepool_push.jpg ├── block_copy__block.png ├── block_forwarding.png ├── border.png ├── bounds_coordinatesystem.png ├── bounds_originchange.png ├── bounds_originchange_display.png ├── coreanimation_shadow_path.png ├── custom-presentation-and-animator-objects.png ├── customcollectionviewlayout_coreprocess.png ├── downsample.png ├── downsampling-1.png ├── dyld3.png ├── dynamic_link.png ├── forwarding_multipleinheritance.gif ├── hitch_ratio.png ├── hitch_time.png ├── https-http.png ├── https_Man_in_the_middle_attack.png ├── https_certificate.png ├── https_charles.png ├── https加密过程.png ├── http响应报文.png ├── http组成.png ├── http首部.jpeg ├── iOSLearning.webp ├── iOS_build_link.jpg ├── imagebuffer_databuffer_framebuffer.png ├── initializersExample02.png ├── initializersExample03.png ├── instance_class_metaclass.png ├── ios-background-remotenotification.png ├── ios-background-transfer-service.png ├── ios-backgroundfetch-vs-remotenotification.png ├── ios-backgroundtasks-structure.png ├── ios-multitask-backgroundtask-ios6.png ├── ios-multitask-backgroundtask-ios7.png ├── ios7-backgroundfetch.png ├── ios_frame_drop.png ├── ios_sandbox.png ├── ios_screen_display_cpu_gpu.png ├── ios_vsync_runloop.png ├── jd_cpustuck_gpustuck.gif ├── kvc_getter.png ├── kvc_setter.png ├── layer_border_background.png ├── oc_initialization_process.gif ├── oc_isa.png ├── runtime_associatedobject.png ├── runtime_messaging1.gif ├── size-class 3.png ├── size_class_open.png ├── sizeclass-vary_for_trait.jpeg ├── static_link.png ├── stuck_benchmark.png ├── tcpip.png ├── transitioning-context-object.png ├── tree.jpg ├── tree_array.jpg ├── uigesturerecognizer_state.png ├── uikit_btn.png └── xcode_embed.png ├── Implementing a Custom Gesture Recognizer.md ├── Infer学习总结.md ├── Interview ├── iOS设计题之findFiles.md ├── iOS设计题之设计消息发送模块.md ├── iOS面试之Objective C runtime系列.md ├── iOS面试之weak原理.md ├── iOS面试之将多个异步任务串行起来.md └── 一名iOS高级工程师可能需要具备什么能力.md ├── KVC in iOS.md ├── KVO Crash in iOS.md ├── KVO in iOS.md ├── KeyChain in iOS.md ├── LLDB debug 技巧 in iOS.md ├── Layout ├── Autolayout笔记.md ├── SizeClass in iOS.md ├── anchor in iOS.md ├── bounds in iOS.md └── layoutSubviews执行时机.md ├── NSMutableString in iOS.md ├── Network ├── Socks协议笔记.md ├── iOS网络编程笔记.md └── 图解HTTP 笔记.md ├── Objective c dealloc该怎么写.md ├── Objective-C笔记.md ├── Opensource ├── .DS_Store ├── CustomNotificationCenter │ └── Create Custom NotificationCenter in Swift.md ├── DragControl │ ├── .DS_Store │ ├── drag_applemap.gif │ └── iOS拖拽组件实现.md ├── EditMenu │ ├── EditMenuInteraction.swift │ ├── EditMenuInteractionDummy.h │ └── EditMenuInteractionDummy.m └── Slide │ ├── .DS_Store │ ├── SlideView.swift │ ├── iOS横滑组件实现.md │ ├── slider_pageenable.gif │ ├── slider_pageenable_1.gif │ ├── slider_pageoffset.png │ └── slider_secretscrollview.gif ├── Pending ├── Daily Questions.md └── General list in iOS.md ├── Push in iOS.md ├── RAC学习 ├── Operator in ReactiveCocoa.md ├── RAC学习笔记3.md ├── ReactiveCocoa使用技巧.md ├── ReactiveCocoa入门.md ├── ReactiveCocoa底层源码学习.md └── ReactiveCocoa笔记-实践篇.md ├── README.md ├── RunloopNote.md ├── Runtime笔记 ├── OC Runtime笔记.md └── iOS思考之self vs super.md ├── Swift笔记 ├── .DS_Store ├── Compatibility in Swift.md ├── Embed Swift Standard Library in Xcode.md ├── Exclusive Access to Memory in Swift.md ├── Frozen enums in Swift5.md ├── JSON与Model互转 in Swift.md ├── Key-Path in Swift.md ├── Property Wrapper笔记.md ├── Protocol in Swift.md ├── Some and Any in Swift.md ├── Swift JSON-Model库调研.md ├── Swift Package Manager(Swift PM)笔记.md ├── Swift-OC混编.md ├── Swift笔记.md ├── What is lazy collections feature in Swift.md ├── What is type erasure in Swift.md ├── What's new in Swift 5.md ├── Whatisnew.md ├── as-is in Swift.md ├── iOS混编项目Tips.md └── rethrow in Swift.md ├── Target, Project, Workspace, Scheme in Xcode.md ├── Timer in iOS.md ├── UIAppearance in iOS.md ├── UIImageView in iOS.md ├── UIKit笔记.md ├── UIScrollViewDelegate回调方法执行时机备忘.md ├── UITableView in iOS.md ├── UNNotification in iOS.md ├── UnitTesting in iOS.md ├── View Controller Programming Guide for iOS.md ├── WWDC-Image and Graphics Best Practices 笔记.md ├── Xcode Practice ├── Assets.md └── AttachProcess.md ├── architecture ├── .DS_Store ├── AOP(Aspect Oriented Programming)学习笔记.md ├── App's lifecycle in iOS.md ├── Clean Architecture学习笔记.md ├── Images │ └── App_responsibility.png ├── MVP.md ├── MVVM学习笔记.md ├── VIPER.md ├── iOS Architecture Note.md ├── iOS中跨页面数据同步方案.md ├── 《App Architecture》Note.md ├── 《谈谈 MVX 中的 Model》笔记.md ├── 关于架构的一些读书笔记.md ├── 单向数据流(Unidirectional data flow).md ├── 移动端复杂页面架构设计.md └── 读《iOS应用架构谈 本地持久化方案及动态部署》笔记.md ├── dSYM in iOS.md ├── for-in in Objective-C-Swift.md ├── iOS EditMenuInteracton组件.md ├── iOS Test Note.md ├── iOS之自定义UICollectionViewLayout.md ├── iOS事件响应简单综述.md ├── iOS包体积优化笔记.md ├── iOS开发高手课笔记 ├── 《iOS开发高手课》笔记-App启动速度如何优化与监控.md ├── 《iOS开发高手课》笔记-列表.md └── 《iOS开发高手课》笔记之04 - 项目大了人员多了,架构怎么设计更合理?.md ├── iOS思考之如何在遍历集合过程中删除元素.md ├── iOS电池电量优化.md ├── iOS笔记.md ├── iOS编译过程.md ├── iOS难点攻关.md ├── iPhone设备信息参考.md ├── 《Effective Objective-C 2.0》笔记.md ├── 内存 ├── Autoreleasepool in iOS.md ├── Memory Leak in iOS.md ├── OOM in iOS.md └── 《WWDC 2024-- Analyze heap memory》学习笔记.md ├── 动态库静态库 ├── Library, Framework in iOS--Part1.md └── Library, Framework in iOS--Part2.md ├── 原生Log框架 in iOS.md ├── 命令备忘.md ├── 多线程 ├── Synchronization Tools in iOS.md ├── iOS多线程编程.md ├── iOS思考之控制并发任务数.md └── 面试题之多读单写.md ├── 性能卡顿启动速度优化 ├── Pre-main优化.md ├── iOS卡顿优化笔记.md ├── 《iOS开发高手课》笔记-App启动速度如何优化与监控.md └── 移动端应用监控(APM)探索.md ├── 技术成长.md ├── 正则表达式 in iOS.md ├── 源码阅读 ├── AFNetworking源码笔记.md ├── Masonry源码阅读笔记.md ├── SDWebImage源码阅读笔记.md └── YYDispatchQueuePool源码笔记.md ├── 组件化学习.md ├── 跨平台技术调研.md └── 轮询、SSE、WebSocket区别.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | -------------------------------------------------------------------------------- /Animation/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Animation/.DS_Store -------------------------------------------------------------------------------- /Animation/Images/animation_cubicgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Animation/Images/animation_cubicgraph.png -------------------------------------------------------------------------------- /Animation/Images/bounceanimation_animationcurve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Animation/Images/bounceanimation_animationcurve.png -------------------------------------------------------------------------------- /Animation/Images/ca_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Animation/Images/ca_architecture.png -------------------------------------------------------------------------------- /Animation/Images/customtimingfunction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Animation/Images/customtimingfunction.png -------------------------------------------------------------------------------- /Animation/iOS动画技巧.md: -------------------------------------------------------------------------------- 1 | # iOS动画技巧 2 | 3 | ### 如何实现一个动画间隔n时间重复执行 4 | 5 | 默认情况下,一个重复执行的CoreAnimation动画,执行完一次,会立即执行下一次。如何做到两次执行之间间隔一定时间呢? 6 | 7 | 简单想了下,想到1种方案 8 | 9 | - 通过监听单词动画的结束,重复创建添加动画 10 | 11 | 该方案可行,但问题是可能会出现两部分代码:动画本身的代码和监听、重复添加的代码,这多少会让动画显得不太高内聚 12 | 13 | 另一种更高内聚的实现是使用AnimationGroup,比如如下代码表示实际动画执行时长是0.5s,间隔1s中执行一次 14 | 15 | ``` 16 | let group = CAAnimationGroup() 17 | group.duration = 1.5 18 | group.repeatCount = Float(Int.max) 19 | // 真正的动画内容 20 | let animation = CAKeyframeAnimation(keyPath: "transform.translation.y") 21 | // balabala 22 | animation.duration = 0.5 23 | group.animations = [animation] 24 | layer.add(group, forKey: "key") 25 | ``` -------------------------------------------------------------------------------- /App's lifecycle in iOS.md: -------------------------------------------------------------------------------- 1 | # App's lifecycle in iOS 2 | 3 | > 写本文时,最新iOS系统版本为15 4 | 5 | App的生命周期中可能会有一些事件,比如点击App图标进入App时、按Home(上推HomeIndicator虚拟键)退出后台、使用APP 6 | 过程中有电话进入、点击App的通知时等,这时App可能需要处理一些事情,比如暂停播放中的音乐 7 | 8 | 本文便来介绍如何接收、处理这些事件 9 | 10 | - iOS 13之后新增了基于场景(scene)的生命周期管理方式 11 | - iOS12及更早App则只能用基于App的声明周期管理方式(App-Based Life-Cycle) 12 | 13 | ## Respond to App-Based Life-Cycle Events 14 | 15 | 该种生命周期管理方式的核心是`UIAppDelegate`对象,它会接收各种事件,并需要在其中进行响应任务处理 16 | 17 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/app-state_dark.png?raw=true) 18 | 19 | ### State 20 | 21 | - NotRunning 22 | - Inactive 23 | - 一个短暂的临时状态,比如App处在Active时有电话进入、系统弹窗等 24 | - 该状态下App是不能接收和处理用户的各种操作的 25 | - 该状态的意义在于,进入该状态时可能需要做些数据存储、资源释放等工作 26 | - Active 27 | - App可以接收处理用户操作 28 | - Active和Inactive可以说都属于Foreground状态(虽然官方没有明确列出Foreground状态) 29 | - Background 30 | - 通常是用户按下Home触发 31 | - Background状态是为后台任务准备的一个状态 32 | - 如果没有后台任务要执行,那在Background状态短暂停留就会进入Suspended状态 33 | - 如果有任务就执行,但官方不建议执行耗时太久的任务。除了必须持续执行的,比如导航 34 | - Suspended 35 | - 此处状态下App不执行任何代码 36 | - 当设备内存紧张时,App可能在这个状态下被系统干掉,变为NotRunning 37 | 38 | ### State Transitions 39 | 大部分状态之间的转换容易理解,也有几个转换让人费解 40 | 41 | - NotRunning -> Background 42 | - 官方说法:If your app requested specific events, the system might also launch your app in the background to handle those events. 43 | - Suspended -> Background 44 | - The system may also launch an app directly into the background state, or move a suspended app into the background, and give it time to perform important tasks. 45 | - Background -> NotRunning 46 | - Yes, iOS can and will kill an application in the background if it requires resources. Not to mention the app can crash on it's own, or the device can be restarted. from [this](https://developer.apple.com/forums/thread/696275) 47 | - NotRunning -> Suspended 48 | - dont know 49 | 50 | ### Responding to App Life-Cycle Events 51 | 52 | 有以下几个回调方法构成 53 | 54 | ``` 55 | //Tells the delegate that the app has become active. 56 | func applicationDidBecomeActive(UIApplication) 57 | 58 | // Tells the delegate that the app is about to become inactive. 59 | func applicationWillResignActive(UIApplication) 60 | 61 | // Tells the delegate that the app is now in the background. 62 | func applicationDidEnterBackground(UIApplication) 63 | 64 | // Tells the delegate that the app is about to enter the foreground. 65 | func applicationWillEnterForeground(UIApplication) 66 | 67 | // Tells the delegate when the app is about to terminate. 68 | func applicationWillTerminate(UIApplication) 69 | ``` 70 | 71 | 同时以上事件也有对应的通知,可以在App中任意位置接收、处理这些通知 72 | 73 | ``` 74 | class let didBecomeActiveNotification: NSNotification.Name 75 | 76 | class let didEnterBackgroundNotification: NSNotification.Name 77 | 78 | class let willEnterForegroundNotification: NSNotification.Name 79 | 80 | class let willResignActiveNotification: NSNotification.Name 81 | 82 | class let willTerminateNotification: NSNotification.Name 83 | ``` 84 | 85 | ### applicationWillTerminate 86 | - 首先该方法执行时表示,App即将被系统干掉,回收内存 87 | - 该时机下,有大约5s的时间处理事情 88 | - 一个典型调用时机是,如果没有后台任务时,用户强制杀死App时会执行 89 | 90 | ## Respond to Scene-Based Life-Cycle Events 91 | 92 | - 自iOS 13开始,Apple推出了multiple window(scene)的技术;简言之就是一个App进程可以有多个Window了;当然,目前该技术只支持在iPad系统上 93 | - 代码层面上与iOS 13之前的工程相比,最直观的区别就是多了一个SceneDelegate的东西(实质上不仅如此) 94 | 95 | > 本人没做过iPad应用的开发,只是通过一个tutorial简单的了解了一下 96 | 97 | ## 参考 98 | - [Managing Your App's Life Cycle](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle) 99 | - [Adopting Scenes in iPadOS](https://www.raywenderlich.com/5814609-adopting-scenes-in-ipados#toc-anchor-007) 100 | - [Architecting Your App for Multiple Windows](https://developer.apple.com/videos/play/wwdc2019/258/) -------------------------------------------------------------------------------- /AttributedString in iOS.md: -------------------------------------------------------------------------------- 1 | # AttributedString in iOS 2 | 3 | ## Changing an Attributed String 4 | 5 | ## 疑问 6 | 1. 什么是attachment、attachment characters 7 | 2. 官方的guide中为什么主要在讲Application Kit中的API呢?iOS部分为何没有具体讲? 8 | 9 | ## 参考 10 | - [Attributed String Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/AttributedStrings/AttributedStrings.html#//apple_ref/doc/uid/10000036i) -------------------------------------------------------------------------------- /Block-Closure in iOS function缩进.md: -------------------------------------------------------------------------------- 1 | # Block/Closure in iOS function缩进 2 | 3 | 当iOS代码的Function/Method中有多个Block或Closure时,怎样缩进决定了代码的美观和可维护性 4 | 5 | 参考业界认可度比较高的代码规范(谷歌的Objective C规范和RayWenderlich的Swift代码规范),将不同情况列举如下 6 | 7 | ## Objective C 8 | 9 | ``` 10 | ``` 11 | -------------------------------------------------------------------------------- /Cache/CoreData in iOS.md: -------------------------------------------------------------------------------- 1 | # CoreData in iOS 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | 记得之前写过Core Data的笔记,后来找不到了,很久不用Core Data了,很多知识点都忘了,这里做一下记录 6 | 7 | > 尽管基础知识点已经记不清了,但数据库的一些知识如基本的SQL语句、主键、级联操作、触发器还印象深刻。这都得归功于上大学时数据库这门课学的还可以 8 | 9 | ## 参考 10 | - [初识Core Data(2)](http://yulingtianxia.com/blog/2014/05/02/chu-shi-core-data-2/) -------------------------------------------------------------------------------- /Cache/FileSystem in iOS.md: -------------------------------------------------------------------------------- 1 | # FileSystem in iOS 2 | 3 | An iOS Application sandbox directory 4 | 5 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/ios_sandbox.png?raw=true) 6 | 7 | - Bundle 8 | - app resources, such as images 9 | - signed during app installation, so readonly 10 | - not backed up by iTunes or iClound, but iTunes does perform an initial sync of any apps purchased from the App Store 11 | - Data Container 12 | - can store user, app data, writable 13 | - Documents 14 | - use to store user-generated content 15 | - user can access and share files 16 | - backed up by iTunes, iCould 17 | - Documents/Inbox 18 | - access files that your app was asked to open by outside entities 19 | - can read, delete contens, can not edit 20 | - Library 21 | - not user data, app's data 22 | - Application Support, Caches subdirectories, can create custom directory 23 | - backed up by iTunes, iCloud 24 | - Caches, system may delete contents to free up disk space 25 | - tmp 26 | - for temp contents, may be purged 27 | - not backed up by iTunes, iCloud 28 | - iCloud Container 29 | - almost readonly, can only edit by system framework 30 | 31 | ### 参考 32 | 33 | - [File System Programming Guide](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html) 34 | -------------------------------------------------------------------------------- /Cache/NSCache笔记.md: -------------------------------------------------------------------------------- 1 | # NSCache笔记 2 | 3 | 支持临时在**内存**中缓存对象 4 | 5 | - 也是按照`key-value`对的形式存取数据、删除数据 6 | - 内部也会根据设置和系统内存使用情况自动移除陈旧对象 7 | - 区别于`NSDictionary`,不会将key进行copy 8 | - 可以设置最大对象数和最大存储size,但内部不会一定按照这个设置清除陈旧对象 9 | - api线程安全 10 | 11 | ## 实践 12 | 13 | ### SDWebImage 3.x 14 | SDWebImage的3.x版本中,缓存对象`SDImageCache`,内部实际用的就是`NSCache`来做的内存缓存 15 | 16 | 只不过其内部又加入了基于文件的磁盘存储逻辑 17 | 18 | 具体的做法是继承了`NSCache`,初始化时同时监听了低内存警告的通知,当发生低内存警告时,会自动清理缓存中的图片 19 | 20 | ### SDWebImage新版 21 | SDWebImage新版本中,`SDImageCache`进一步完善逻辑,分成`SDMemoryCache`和`SDDiskCache`两个实例 22 | 23 | 同时`SDMemoryCache`中还加入了一个新feature,如果`SDImageCacheConfig`中的`shouldUseWeakMemoryCache`是true的话,`SDMemoryCache`内部会维护一个`NSMapTable`的容器,除了在`NSCache`中缓存图片外,`NSMapTable`中还有一个弱引用指向图片。 24 | 25 | 当一些情况下`NSCache`中的数据被清除时,再次使用时,如果按照常规操作可能要去磁盘中取或者网络下载,但有可能图片本身还没有被释放,此时就可以使用弱引用直接拿到图片了 26 | 27 | # 参考 28 | - [iOS缓存 NSCache详解及SDWebImage缓存策略源码分析你要知道的NSCache都在这里](https://cloud.tencent.com/developer/article/1089338) -------------------------------------------------------------------------------- /Cache/Persistence in iOS.md: -------------------------------------------------------------------------------- 1 | # Persistence in iOS 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | 汇总一下iOS中(Swift、Objective C)的持久化的方案 6 | 7 | 或者说将如下的专业名词进行分类、结束清楚 8 | 9 | > nscoding、archive、serialization、userdefault、plist、core data(sqlite、realm)、codable 10 | 11 | ## Archive 12 | 13 | OC和Swift下都支持 14 | 15 | ### Features 16 | - 支持数据与byte stream(NSData)之间来回转换;两个过程分别叫做archive、unarchive 17 | - 支持存储Object Graph,即对象间引用关系;且不会存储重复对象 18 | - 支持自定义存储规则,如某些对象 or 属性可以不存储 19 | - 支持OC Object、scalar、structure等多种类型 20 | - 可以持久化到文件中 21 | 22 | ### How to use 23 | - 核心类有两个:NSCoder、NSCoding 24 | - NSCoder负责整个archive 和 unachive过程 25 | - 如果希望对象类型支持,需要遵循NSCoding协议 26 | - NSCoder类似一个抽象类,常用的具体子类有NSKeyedArchiver、NSKeyedUnArchiver 27 | 28 | > 相比NSCoding,NSSecureCoding更加安全,更推荐使用 29 | 30 | ## Serialization & Deserialization 31 | 32 | Serialization & Deserialization为序列化、反序列化的意思 33 | 34 | > In computing, serialization (US and Oxford spelling) or serialisation (UK spelling) is the process of translating a data structure or object state into a format that can be stored (for example, in a file or memory data buffer) or transmitted (for example, over a computer network) and reconstructed later (possibly in a different computer environment). -- from wikipedia 35 | 36 | Objective C和Swift中对序列化的支持是不一样的 37 | 38 | ### Serialization in Objective C 39 | |涉及类|功能|备注| 40 | |:-:|:-:|:-:| 41 | | NSJSONSerialization |支持JSON类型|| 42 | | NSPropertyListSerialization |仅支持Property List数据类型与xml数据之间的序列化|Property List是iOS中对一些数据类型的一个统称,可看参考中资料;PropertyList数据转化后的xml文件即为所说的plist文件| 43 | 44 | - 以上类仅支持系统内置的类与对应数据类型的转换,如NSDictionary转为JSON数据,NSDictionary数据转为plist文件的数据 45 | - 如果要让自定义对相关支持与不同类型数据的转换,需要额外的工作,比如借助YYModel将JSON转为NSDictionary再转为自定义对象 46 | 47 | ### Serialization in Swift 48 | 49 | 上面OC中的类都支持,除此之外,Swift中引入一套新的进行序列化的机制----Codable 50 | 51 | - 功能更强大,比如可以使得JSON和自定义对象之间直接来回转换,在OC中必须要借助中间的数据类型如NSDictionary 52 | - 自定义功能更多,如转换规则、错误规则 53 | - 对与JSON、Property List数据之间转换提供了基于Codable的支持 54 | 55 | ## UserDefaults 56 | 57 | - 用于存储轻量级的、需要持久化的键值对数据 58 | - 仅支持存取Property List类型,即自定义对象是不支持存储到NSUserDefaults中的 59 | 60 | ## Database 61 | Core Data、SQLite、Realm都是数据库 62 | 63 | ## QA 64 | 65 | ## References 66 | - [Archives and Serializations Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Archiving/Archiving.html) 67 | - [Property List 使用注意事项](https://juejin.cn/post/6844903992166711304) 68 | - [NSSecure​Coding](https://nshipster.com/nssecurecoding/) -------------------------------------------------------------------------------- /Cache/SQLite vs Realm in iOS.md: -------------------------------------------------------------------------------- 1 | # Database in iOS 2 | 3 | ## SQLite 4 | > 上手体验一下SQLite 5 | 6 | SQLite是C语言实现的开源的、支持跨平台的轻量级数据库引擎,在移动设备、移动应用中应用广泛 7 | 8 | > SQLite is not directly comparable to client/server SQL database engines such as MySQL, Oracle, PostgreSQL, or SQL Server since SQLite is trying to solve a different problem.SQLite strives to provide local data storage for individual applications and devices. SQLite emphasizes economy, efficiency, reliability, independence, and simplicity. 9 | 10 | 11 | ### 适合场景 12 | 13 | ### 问题 14 | ### 与CoreData的关系 15 | - CoreData基于SQLite,增加了一些方便的API 16 | 17 | ## Realm 18 | 19 | ## 参考 20 | - [SQLite](https://www.sqlite.org/index.html) 21 | - [SQLite With Swift Tutorial: Getting Started](https://www.raywenderlich.com/6620276-sqlite-with-swift-tutorial-getting-started) -------------------------------------------------------------------------------- /CodingGuideline/Objective-C 注释文档笔记.md: -------------------------------------------------------------------------------- 1 | # Objective-C 注释文档笔记 2 | 3 | > 本文写作于2020年10月27日,代码环境为Xcode12,iOS14系统,Objective-C语言 4 | 5 | ## 类注释 6 | ``` 7 | /** 8 | 描述,如何使用该类 9 | 10 | 详细描述,使用该类是具体几种形式 11 | 12 | - 描述1 13 | 14 | - 描述2 15 | @warning 警告,使用时不要做xxxx 16 | */ 17 | @interface ViewController : UIViewController 18 | @end 19 | ``` 20 | 21 | ![](https://user-images.githubusercontent.com/5978164/97268266-8f02fa80-1866-11eb-90a3-603341206c46.png) 22 | 23 | ## 方法注释 24 | 25 | ``` 26 | /// 判断xxx是否满足条件 27 | /// 28 | /// 详细描述一下该方法使用注意事项 29 | /// @param num 参数num 30 | /// @param obj 参数obj 31 | /// @return YES:满足;NO:不满足 32 | /// @warning 警告,注意不要xxx 33 | - (BOOL)isQualifiedByA:(NSUInteger)num andB:(NSObject *)obj; 34 | ``` 35 | 36 | ![](https://user-images.githubusercontent.com/5978164/97269718-ebffb000-1868-11eb-8709-d3ed4fb07c75.png) 37 | 38 | ## Deprecated使用 39 | 40 | 41 | ## 参考 42 | - [Objective-C Documentation](https://nshipster.com/objective-c-documentation/) 43 | - [AFHTTPSessionManager.h](https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPSessionManager.h) -------------------------------------------------------------------------------- /CodingGuideline/《Coding Guidelines for Cocoa》笔记.md: -------------------------------------------------------------------------------- 1 | # 《Coding Guidelines for Cocoa》笔记 2 | 3 | ## 一般原则 4 | 5 | ### Clarity(清晰) 6 | 7 | |code|commentary| 8 | |:-:|:-:| 9 | |`insertObject:atIndex:`|good| 10 | |`insert:at:`|not clear| 11 | 12 | 通常,不用简写 13 | 14 | |code|commentary| 15 | |:-:|:-:| 16 | |`setBackgroundColor`|good| 17 | |`setBkgdColor`|not clear| 18 | 19 | 但也有一些简写是经过历史检验,已经约定俗成了的 20 | 21 | [Acceptable Abbreviations and Acronyms](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html#//apple_ref/doc/uid/20001285-BCIHCGAE) 22 | 23 | 定义api时,避免歧义 24 | 25 | |code|commentary| 26 | |:-:|:-:| 27 | |`sendPort`|歧义,是想表示发送端口数据还是表示获取发送方的端口信息呢| 28 | |`displayName`|同上| 29 | 30 | ### Consistency(一致) 31 | 32 | 有一些方法、属性名贯穿多个不同的类,使用同一个名称,会让编程更清晰 33 | 34 | |code|commentary| 35 | |:-:|:-:| 36 | |- (NSInteger)tag|Defined in NSView, NSCell, NSControl.| 37 | 38 | ### No Self Reference 39 | 40 | |code|commentary| 41 | |:-:|:-:| 42 | |NSString|good| 43 | |NSStringObject|self reference| 44 | 45 | 但也有例外,比如Mask常量和通知名称 46 | 47 | |code|commentary| 48 | |:-:|:-:| 49 | | NSUnderlineByWordMask |good| 50 | | NSTableViewColumnDidMoveNotification |good| 51 | 52 | ## 前缀 53 | 54 | - 因为OC没有命名空间的概念,所以为了避免与三方库或系统库发生冲突,需要使用前缀 55 | - 当给类、协议、函数、常量、typdef命名时,需要用到前缀,这些数据结构可能产生冲突 56 | - 但当命名类中的方法、属性时,则不需要 57 | 58 | ## 书写约定 59 | 60 | - 通常,方法(Method)的命名以小写字母开始,中间每个单词的开始使用大写,即小驼峰写法 61 | - 也有例外,比如方法名的开头是以常见的缩写开始的--如`TIFFRepresentation ` 62 | - 对类、协议、函数、常量、typdef的命名,除了使用前缀,要使用大驼峰写法 63 | 64 | ## 类与协议名 65 | 66 | - 大多数协议中的方法跟某一类没有具体关系,这时候协议命名时可能会在最后加上`ing`,比如`NSLocking` 67 | - 也有与某个类关联很大的协议,此时可以用类名作为协议名,比如`NSObject`协议 68 | 69 | ## 方法命名规范(Method) 70 | 71 | ### 一般规则 72 | 73 | - 小驼峰,不需要前缀 74 | - 两种情况下可以不用小驼峰 75 | - 以经典缩写开头,比如PDFXXXX、TIFFXXXX 76 | - 一些我们类内部的私有方法,可以加前缀;但不要以下划线开始,以防与系统私有方法冲突 77 | - 若方法返回一个属性,直接写属性名称即可,无需加get之类 78 | - 方法每个入参之前都应该有关键词 79 | - 入参数之间不要用and连接 80 | - 方法中独立的行为可以用and连接,如`- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;` 81 | 82 | ### Accessor Methods 83 | 84 | - 不要试图用过去分词形式,将动词改为形容词形式 85 | 86 | |code|| 87 | |:-:|:-:| 88 | |- (BOOL)acceptsGlyphInfo;|right| 89 | |- (BOOL)glyphInfoAccepted;|wrong| 90 | - get的使用仅限于一个方法获取多个参数的形式,即通过指针来获取额外返回值时 91 | - `- (void)getLineDash:(float *)pattern count:(int *)count phase:(float *)phase;` 92 | 93 | ### Delegate Methods 94 | 95 | 该小节为代理方法的命名规范,该规范也适用于数据源对象(datasource) 96 | 97 | - 要以发送代理方法的对象的类名开头,小写字母开头,且没有前缀 98 | - `- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;` 99 | - 一般是在类名后面加冒号,第一个参数就是被代理的对象;但当只有一个参数,且参数就是被代理的对象时,就不是在类名后面加冒号了,而是如下 100 | - `- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;` 101 | - 关于上面的一条,也有例外,比如发送了某个通知,触发了代理方法,这种情况下唯一的参数就是通知对象 102 | - `- (void)windowDidChangeScreen:(NSNotification *)notification;` 103 | - did、will、should在代理方法中也很常用 104 | - `- (void)browserDidScroll:(NSBrowser *)sender;` 105 | - `- (BOOL)windowShouldClose:(id)sender;` 106 | 107 | ### Method Arguments 108 | 109 | 下面这些参数名称和方法名经常搭配使用 110 | 111 | ``` 112 | ...action:(SEL)aSelector 113 | ...alignment:(int)mode 114 | ...atIndex:(int)index 115 | ...content:(NSRect)aRect 116 | ...doubleValue:(double)aDouble 117 | ...floatValue:(float)aFloat 118 | ...font:(NSFont *)fontObj 119 | ...frame:(NSRect)frameRect 120 | ...intValue:(int)anInt 121 | ...keyEquivalent:(NSString *)charCode 122 | ...length:(int)numBytes 123 | ...point:(NSPoint)aPoint 124 | ...stringValue:(NSString *)aString 125 | ...tag:(int)anInt 126 | ...target:(id)anObject 127 | ...title:(NSString *)aString 128 | ``` 129 | ## Tips and Techniques for Framework Developers 130 | 131 | 未完待续 132 | 133 | 134 | ## 参考 135 | - [Coding Guidelines for Cocoa](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingBasics.html#//apple_ref/doc/uid/20001281-BBCHBFAH) -------------------------------------------------------------------------------- /Copy in Objective-C.md: -------------------------------------------------------------------------------- 1 | # Copy in Objective-C 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | 一般说到拷贝指的是对象类型的拷贝而非`scalar`类型(如NSInteger、BOOL) 6 | 7 | 因为`scalar`类型赋值时使用的是`值传递`,本身就是一种拷贝 8 | 9 | 而对象的变量在赋值时是`引用传递`,真正的对象在内存里只有一份,所以当需要有多份的时候就涉及到拷贝了 10 | 11 | ## Copy要实现功能 12 | 13 | 官方如下要求 14 | > The exact meaning of “copy” can vary from class to class, but a copy must be a functionally independent object with values identical to the original at the time the copy was made. A copy produced with NSCopying is implicitly retained by the sender, who is responsible for releasing it. 15 | 16 | 翻译一下,一个对象的拷贝必须 17 | 18 | 1. 在功能上是一个独立对象,拷贝对象的每个属性值必须和原对象对应属性值必须相等 19 | 2. 拷贝后的对象的内存管理权(ownership)交给了使用这个对象的一方来管理。它要负责该对象的内存释放 20 | 21 | 细品一下这两条原则 22 | 23 | 1. 关于第2条,因为目前都是ARC了,所以我们也不用做额外处理。具体内容可以看苹果官方内存管理文章 24 | 2. 第1条很重要 25 | - 对象的各个属性值都相等,这个是肯定的 26 | - `功能上的`独立对象其实是想说,拷贝后的对象不一定必须在内存中新开辟一块地方创建这个对象的拷贝 27 | - 也可以还是原来的对象,只是这个对象所有的属性都不允许被修改 28 | - 换句话说,对对象的拷贝做任何修改不应影响到原对象;或者干脆这个对象的内容就不能被修改 29 | 30 | ## 如何实现Copy 31 | 32 | 有了上面的理解,实现Copy就容易许多,无非就是按照官方的套路走 33 | 34 | - 所有类的基类`NSObject`有一个`-(id) copy`方法 35 | - 该方法内部会调用`NSCopying`协议唯一的方法`- (id)copyWithZone:` 36 | - 需要支持拷贝功能的对象只需要实现`NSCopying`协议的`- (id)copyWithZone:`方法即可 37 | 38 | 实现`- (id)copyWithZone:`时需要注意些什么 39 | 40 | - 如果父类实现了该方法,也要执行以下`[super copyWithZone:]`,以达到父类对属性完成拷贝 41 | 42 | ## Copy vs mutableCopy 43 | 44 | 这两个方法产生的对象的`mutability`(可变性)不同 45 | 46 | ## 深拷贝VS浅拷贝 47 | 48 | 对于对象类型的内容,拷贝时有深、浅拷贝区别(因为对于`scalar`类型数据都是深拷贝,或者说只有深拷贝) 49 | 50 | **完全深拷贝**,拷贝后的对象的每一个属性内容与原对象占用完全不同的一份内存区域 51 | 52 | 平常开发当中完全深拷贝比较少,**多数是浅拷贝** 53 | 54 | 要想实现深拷贝,可以通过`Archive`技术,将对象转为data再转回对象 55 | 56 | ``` 57 | NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: 58 | [NSKeyedArchiver archivedDataWithRootObject:oldArray]]; 59 | 60 | ``` 61 | 62 | ## Copying Collections 63 | 64 | 集合类的拷贝默认(比如直接执行`copy`方法)都是浅拷贝 65 | 66 | `initWithArray:copyItems:`中`copyItems`传YES时,**有可能**实现**第一层对象**的深拷贝 67 | 68 | 1. 传YES时,本质上是在将每个item放入新的集合中时,对每个item执行`copy`方法 69 | 2. 所以说是否能否实现第一层对象的真正深拷贝,取决于每个item的`copyWithZone:`的实现 70 | 3. 而且也只是对第一层对象执行`copy`方法 71 | 4. 如果要完全深拷贝,还是用`Archive`吧 72 | 73 | 74 | ## 参考 75 | - [NSCopying](https://developer.apple.com/documentation/foundation/nscopying?language=objc) 76 | - [Copying Collections](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Collections/Articles/Copying.html#//apple_ref/doc/uid/TP40010162-SW1) 77 | -------------------------------------------------------------------------------- /Crash/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Crash/.DS_Store -------------------------------------------------------------------------------- /Crash/iOS Crash分析.md: -------------------------------------------------------------------------------- 1 | # iOS Crash 2 | 3 | 大致分为三类crash: 4 | 5 | - 上层NSException,可以通过NSExceptionCaughtHandler捕获到 6 | - 通过Unix内核信号捕获到的crash 7 | - 无法捕获到崩溃 8 | 9 | 信号捕获不到的崩溃有: 10 | 11 | - 进入后台时,由于某些原因被系统强杀 12 | - OOM导致系统强杀 13 | - 主线程卡顿时间过长导致系统强杀 14 | 15 | ### 异常编码 16 | 17 | - 0x8badf00d,表示 App 在一定时间内无响应而被 watchdog 杀掉的情况。 18 | - 0xdeadfa11,表示 App 被用户强制退出。 19 | - 0xc00010ff,表示 App 因为运行造成设备温度太高而被杀掉。 20 | 21 | ## Xcode Tool 22 | ### Address Sanitizer 23 | 24 | ## 25 | 26 | ## Q&A 27 | 28 | 1. 系统在后台杀死App的原因是什么? 29 | - 是因为后台任务在规定时间内无法完成导致的吗? 30 | - 如果是后台一些关键数据读写出了问题,很可能导致后序App的更严重体验问题 31 | 32 | ## 参考 33 | - [12 | iOS 崩溃千奇百怪,如何全面监控?](https://time.geekbang.org/column/article/88600) 34 | - [深入iOS系统底层之crash解决方法](https://www.jianshu.com/p/cf0945f9c1f8) 35 | - [深入iOS系统底层系列文章目录](https://www.jianshu.com/p/139f0899335d) 36 | - [深入iOS系统底层之XCODE对汇编的支持介绍](https://cloud.tencent.com/developer/article/1192667) 37 | - [iOS的崩溃捕获方案](http://silentcat.top/2017/11/23/iOS%E7%9A%84%E5%B4%A9%E6%BA%83%E6%8D%95%E8%8E%B7%E6%96%B9%E6%A1%88/) 38 | - [【老司机精选】iOS 符号化:基础与进阶](https://juejin.cn/post/7084044616864890893) 39 | - [Adding identifiable symbol names to a crash report](https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report#Symbolicate-the-crash-report-with-the-command-line) -------------------------------------------------------------------------------- /Crash/对iOS中崩溃防护的思考.md: -------------------------------------------------------------------------------- 1 | # 对iOS中崩溃防护的思考 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | 过往工作经历中基本没碰到过关于崩溃的防护内容,但发现面试中会有类似问题,于是做了一点调研工作。本文简单说以下相关技术点、现状和个人思考 6 | 7 | 本文主要聊一下如下几个问题: 8 | 9 | 1. 都有哪些崩溃可以防护 10 | 2. 国内大多数是咋样防护的 11 | 3. 国外App也这样做吗 12 | 4. 个人看法 13 | 14 | ## 什么是崩溃防护 15 | 16 | 崩溃防护并非舶来品,是地道的国产名词 17 | 18 | 大致意思是,针对不同类型崩溃的特点,对潜在的崩溃进行提前的干预 19 | 20 | - 一方面尽可能避免崩溃的发生,及时避免不了也通过更温和的方式提醒用户,而非直接闪退 21 | - 另一方面,还要记录并上报下本次潜在的崩溃的信息,以供后续修复 22 | 23 | ## 哪些崩溃可以防护 24 | 25 | 常见的崩溃情况如下: 26 | 27 | - unrecognized selector crash 28 | - KVO crash 29 | - NSNotification crash 30 | - NSTimer crash 31 | - Container crash(数组越界,插nil等) 32 | - NSString crash (字符串操作的crash) 33 | - Bad Access crash (野指针) 34 | - UI not on Main Thread Crash (非主线程刷UI) 35 | 36 | ## 国外App是怎么做的 37 | 38 | 我在stackoverflow和medium上搜索“iOS prevent crash”之类的关键词,一条有价值的内容都没搜到 39 | 40 | ## 如何防护 41 | 42 | 看了几篇文章后,发现对于上面的crash,基本的防护措施就两方面: 43 | 44 | - 非内存问题,通过Method Swizzling进行提前干预,避免崩溃 45 | - 内存问题(野指针),创建自定义Zombie对象,延迟真实对象的释放,来监控和规避野指针问题 46 | 47 | ## 个人看法 48 | 49 | 先说结论:不推荐做crash防护 50 | 51 | 上面提到的crash防护措施,本质上是通过技术手段去为开发者编码错误兜底,我们从付出的代价和受益上简单比较一下: 52 | 53 | - 使用Method Swizzling技术或Zombie没有错,但防护方案中依赖了较多的不确定性因素,比如 54 | - 选择创建Zombie对象的时机如何选择,到底在dealloc还是在底层free函数执行时?如果后续某个版本iOS SDK变化了怎么办?(实际上这种情况已经发生了,第一个参考文档中有提到) 55 | - 一些系统类的内部使用了类簇或使用了消息转发等机制,这些技术会与防护方案有冲突 56 | - 到了Swift,没有runtime了,防护方案可能大功能就不能用了 57 | - 第一篇参考文档中有提到,因为方案中在系统调用之前增加了很多逻辑,性能降低明显。且有的防护措施实际应用并没有预想的多,但却因为iOS设备、系统版本较多的原因,维护工作量不小 58 | 59 | 简单来说,不论是像数组越界这种低级的编码错误,还是内存管理理解不到位导致的内存泄漏或野指针错误,都不应该丢给兜底方案,谁的错谁来承担,能力达不到就多学习 60 | 61 | 与其做价值不太大的crash防护,我认为更有价值的内容是: 62 | 63 | - 通过研究iOS/OS X操作系统底层原理,深入了解崩溃、异常、信号的内容(美团的欧阳大哥曾写过一个系列,从汇编开始认识崩溃内容) 64 | - 学习深入解读崩溃文件,了解其中的偏移量、镜像等概念(曾遇到一个国外公司的面试题:如何在只要崩溃文件但没有dSYM文件的情况下了解具体的崩溃信息) 65 | - 研究crash SDK是如何监控和收集crash 66 | 67 | ## 参考 68 | - [再谈 iOS App Crash 防护](https://cloud.tencent.com/developer/article/1641911) 69 | - [iOS Crash防护你看这个就够了 - 上篇](https://juejin.cn/post/6959014275184590855) 70 | - [iOS Crash防护你看这个就够了-下篇](https://juejin.cn/post/6959015601536761893) 71 | - [大白健康系统--iOS APP运行时Crash自动修复系统](https://www.cnblogs.com/Julday/p/13065474.html) 72 | -------------------------------------------------------------------------------- /Daily Questions/Compiler error- "initializer element is not a compile-time constant".md: -------------------------------------------------------------------------------- 1 | # Compiler error: "initializer element is not a compile-time constant" 2 | 3 | ## 问题与解析 4 | 5 | ``` 6 | // 下一句编译不过 7 | // 错误为:initializer element is not a compile-time constant 8 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 9 | 10 | @implementation SomeClass 11 | @end 12 | ``` 13 | 14 | 我们从这个错误提示本身入手: 15 | 16 | - 什么是`initializer element` 17 | - 什么是`compile-time constant` 18 | 19 | 20 | 我们上面这句代码做的事情是初始化一个变量的过程,initializer element就是` [NSMutableDictionary dictionary]` 21 | 22 | `compile-time constant`,我第一反应是不知道什么意思 23 | 24 | ## 参考 25 | 26 | - [Initializer element is not a compile-time constant using C](https://stackoverflow.com/questions/65774807/initializer-element-is-not-a-compile-time-constant-using-c) 27 | - [stack overflow-Compiler error: "initializer element is not a compile-time constant"](https://stackoverflow.com/questions/6143107/compiler-error-initializer-element-is-not-a-compile-time-constant) 28 | - [Constant Expressions](https://public.support.unisys.com/aseries/docs/ClearPath-MCP-20.0/86002268-209/section-000066746.html) 29 | - [What is a compile time constant?](https://coderanch.com/t/454384/java/compile-time-constant) -------------------------------------------------------------------------------- /Functional-Reactive-Programing/Functional Programing in Swift.md: -------------------------------------------------------------------------------- 1 | # Functional Programing in Swift 2 | 3 | ## What is functional programming? 4 | 5 | Functional programming is not a language or syntax - it is a way of thinking about problems. Functional programming was around for decades before we had the monads. Functional programming is a way of thinking about how you tear down problems and then put them back together in structured ways. 6 | 7 | 8 | 9 | ## Features 10 | - function is the first-class citizens 11 | - immutable state and lack of side effects 12 | - high order function 13 | - partial function 14 | - Pure Functions 15 | 16 | ## Imperative Programing 17 | 18 | Imperative programming is a programming paradigm that uses statements to change the program’s state. Much like you would use imperative language while playing with your dog — “Fetch! Lay down! Play dead!” — you use imperative code to tell the app exactly when and how to do things. 19 | 20 | Imperative code is similar to the code that your computer understands. All the CPU does is follow lengthy sequences of simple instructions. The issue is that it gets challenging for humans to write imperative code for complex, asynchronous apps — especially when shared mutable state is involved. 21 | 22 | ## Imperative solution vs Functional solution 23 | 24 | ``` 25 | var ridesOfInterest: [Ride] = [] 26 | for ride in parkRides where ride.waitTime < 20 { 27 | for category in ride.categories where category == .family { 28 | ridesOfInterest.append(ride) 29 | break 30 | } 31 | } 32 | 33 | let sortedRidesOfInterest1 = ridesOfInterest.quickSorted() 34 | print(sortedRidesOfInterest1) 35 | ``` 36 | 37 | ``` 38 | let sortedRidesOfInterest2 = parkRides 39 | .filter { $0.categories.contains(.family) && $0.waitTime < 20 } 40 | .sorted(by: <) 41 | ``` 42 | 43 | ## References 44 | - [An Introduction to Functional Programming in Swift](https://www.raywenderlich.com/9222-an-introduction-to-functional-programming-in-swift#toc-anchor-013) 45 | - [Swift and the Legacy of Functional Programming](https://academy.realm.io/posts/tryswift-rob-napier-swift-legacy-functional-programming/) -------------------------------------------------------------------------------- /Functional-Reactive-Programing/Reactive Programing.md: -------------------------------------------------------------------------------- 1 | # Reactive Programing 2 | 3 | 4 | 5 | ## Q&A 6 | - What is the disadvantages of RP? -------------------------------------------------------------------------------- /Functional-Reactive-Programing/images/operator-flatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Functional-Reactive-Programing/images/operator-flatmap.png -------------------------------------------------------------------------------- /Functional-Reactive-Programing/images/operator-scan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Functional-Reactive-Programing/images/operator-scan.png -------------------------------------------------------------------------------- /Functional-Reactive-Programing/images/operator-skipwhile@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Functional-Reactive-Programing/images/operator-skipwhile@2x.png -------------------------------------------------------------------------------- /Functional-Reactive-Programing/《RxSwift, Reactive Programing with Swift》笔记.md: -------------------------------------------------------------------------------- 1 | # 《RxSwift, Reactive Programing with Swift》笔记 2 | 3 | 读完第一章节(31页),脑子中是满满的疑惑:似懂非懂的感觉,感觉概念不容易理解,可能实践太少了 4 | 5 | 6 | ## Operators 7 | 8 | ### skipWhile 9 | 10 | > There’s a small family of skip operators. Like filter, skipWhile lets you include a predicate to determine what is skipped. However, unlike filter, which filters elements for the life of the subscription, skipWhile only skips up until something is not skipped, and then it lets everything else through from that point on. 11 | 12 | ![]() 13 | 14 | ### flatMap 15 | 16 | ### combineLatest 17 | 18 | This has many concrete applications, such as observing several text fields at once and combining their values, watching the status of multiple sources, and so on. 19 | 20 | ### withLatestFrom 21 | 22 | Simple and straightforward! withLatestFrom(_:) is useful in all situations where you want the current (latest) value emitted from an observable, but only when a particular trigger occurs. 23 | 24 | ## Q&A 25 | 1. 为何一订阅就 26 | 2. shared observable purpose 27 | - 如何验证 -------------------------------------------------------------------------------- /Gemfile in iOS.md: -------------------------------------------------------------------------------- 1 | # Bundler in iOS project浅析 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | ## 前置知识 6 | 7 | gem是一个软件库,提供了像`Cocoapods`、`Fastlane`等常用软件 8 | 9 | Bundler就是gem提供的一个用于**统一gem软件版本**的工具 10 | 11 | ## Bundler工作原理 12 | 13 | 作为iOS开发,对`Podfile`和`Podfile.lock`肯定是有些了解的。其实`Bundler`也是基于同样的思想,而且`Podfile`的思路其实是有借鉴`Bundler`的 14 | 15 | `Bundler`需要两个文件的支持,分别是`Gemfile`和`Gemfile.lock` 16 | - `Gemfile`中用于指定所使用的的gem软件的版本信息和软件下载源 17 | - `Gemfile.lock`则用于锁定当前使用的软件版本信息,需要提交到代码仓库,用于不同开发者不同电脑上使用gem软件对项目工程操作时进行统一版本协同 18 | 19 | ### 使用方法 20 | 21 | iOS项目A,需要使用1.5.0版本的Cocoapods进行库依赖管理,但开发者a电脑上用的1.4.3的Cocoapods,而开发者b电脑上用的是高版本1.8.0的Cocoapods 22 | 23 | 不同版本的Cocoapods在执行依赖安装或更新时,很容易出现冲突情况。最好的版本肯定是大家统一Cocoapods版本。但`Bundler`则给了更好的解决方案 24 | 25 | 我们新创建一个`Gemfile`文件,写入 26 | 27 | ``` 28 | source 'https://rubygems.org' do 29 | gem 'cocoapods', '1.5.0' 30 | end 31 | ``` 32 | 33 | 然后执行`bundle install`,`bundler`会根据`Gemfile`内容创建`Gemfile.lock`文件,此文件要放入git等版本控制中;同时会下载安装`Gemfile`中指定版本的软件 34 | 35 | 最后执行`bundle exec pod install`命令,`bundler`会检查按需下载安装相应版本的`Cocoapods`并进行`pod install`操作 36 | 37 | > 官方建议最好用`bundle exec xx xx`形式,虽然有时候直接`pod install`也可以work,但在不同机器不同环境下直接执行`pod install`是没办法保证`Cocoapods`版本的 38 | 39 | ## 参考 40 | 41 | - [Bundler](https://bundler.io/v2.1/#getting-started) -------------------------------------------------------------------------------- /IGListKit实践笔记.md: -------------------------------------------------------------------------------- 1 | [Instagram/IGListKit实践谈](https://www.jianshu.com/p/44bda1421757) 2 | -------------------------------------------------------------------------------- /Images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/.DS_Store -------------------------------------------------------------------------------- /Images/AFNetworking_architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/AFNetworking_architecture.jpg -------------------------------------------------------------------------------- /Images/CA认证服务器公钥过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/CA认证服务器公钥过程.png -------------------------------------------------------------------------------- /Images/CombinePipeline-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/CombinePipeline-1.png -------------------------------------------------------------------------------- /Images/Libraries/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Libraries/.DS_Store -------------------------------------------------------------------------------- /Images/Libraries/pod_app_link_dynamicpod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Libraries/pod_app_link_dynamicpod.png -------------------------------------------------------------------------------- /Images/Libraries/pod_app_link_staticpod.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Libraries/pod_app_link_staticpod.jpg -------------------------------------------------------------------------------- /Images/Libraries/pod_app_otherlinkflag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Libraries/pod_app_otherlinkflag.png -------------------------------------------------------------------------------- /Images/Libraries/pod_dynamiclibrary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Libraries/pod_dynamiclibrary.png -------------------------------------------------------------------------------- /Images/Libraries/pod_dynamiclibrary_umbrellaheader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Libraries/pod_dynamiclibrary_umbrellaheader.png -------------------------------------------------------------------------------- /Images/Libraries/pod_staticpods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Libraries/pod_staticpods.png -------------------------------------------------------------------------------- /Images/OC_MessageForwarding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/OC_MessageForwarding.png -------------------------------------------------------------------------------- /Images/PromiseKit_classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/PromiseKit_classes.png -------------------------------------------------------------------------------- /Images/PromiseKit_demo_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/PromiseKit_demo_1.png -------------------------------------------------------------------------------- /Images/PromiseKit_demo_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/PromiseKit_demo_2.png -------------------------------------------------------------------------------- /Images/PromiseKit_demo_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/PromiseKit_demo_3.png -------------------------------------------------------------------------------- /Images/PromiseKit_demo_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/PromiseKit_demo_4.png -------------------------------------------------------------------------------- /Images/ReactiveCocoaPipeline-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ReactiveCocoaPipeline-2.png -------------------------------------------------------------------------------- /Images/Socket_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Socket_architecture.png -------------------------------------------------------------------------------- /Images/Socket_communication_process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Socket_communication_process.png -------------------------------------------------------------------------------- /Images/Timer_vs_CADisplayLink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/Timer_vs_CADisplayLink.png -------------------------------------------------------------------------------- /Images/animation_begintime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/animation_begintime.png -------------------------------------------------------------------------------- /Images/app-state_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/app-state_dark.png -------------------------------------------------------------------------------- /Images/application_feedback_loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/application_feedback_loop.png -------------------------------------------------------------------------------- /Images/auto_Lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | COLOR_ERR="\033[1;31m" #出错提示 4 | COLOR_SUCC="\033[0;32m" #成功提示 5 | COLOR_QS="\033[1;37m" #问题颜色 6 | COLOR_AW="\033[0;37m" #答案提示 7 | COLOR_END="\033[1;34m" #颜色结束符 8 | 9 | # 寻找项目的 ProjectName 10 | function searchProjectName () { 11 | find . -maxdepth 1 -name "*.xcodeproj" 12 | } 13 | 14 | function oclintForProject () { 15 | # 预先检测所需的安装包是否存在 16 | if which xcodebuild 2>/dev/null; then 17 | echo 'xcodebuild exist' 18 | else 19 | echo '🤔️ 连 xcodebuild 都没有安装,玩鸡毛啊? 🤔️' 20 | fi 21 | 22 | if which oclint 2>/dev/null; then 23 | echo 'oclint exist' 24 | else 25 | echo '😠 完蛋了你,玩 oclint 却不安装吗,你要闹哪样 😠' 26 | echo '😠 乖乖按照博文:https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.63.md 安装所需环境 😠' 27 | fi 28 | if which xcpretty 2>/dev/null; then 29 | echo 'xcpretty exist' 30 | else 31 | gem install xcpretty 32 | fi 33 | 34 | 35 | # 指定编码 36 | export LANG="zh_CN.UTF-8" 37 | export LC_COLLATE="zh_CN.UTF-8" 38 | export LC_CTYPE="zh_CN.UTF-8" 39 | export LC_MESSAGES="zh_CN.UTF-8" 40 | export LC_MONETARY="zh_CN.UTF-8" 41 | export LC_NUMERIC="zh_CN.UTF-8" 42 | export LC_TIME="zh_CN.UTF-8" 43 | export xcpretty=/usr/local/bin/xcpretty # xcpretty 的安装位置可以在终端用 which xcpretty找到 44 | 45 | searchFunctionName=`searchProjectName` 46 | path=${searchFunctionName} 47 | # 字符串替换函数。//表示全局替换 /表示匹配到的第一个结果替换。 48 | path=${path//.\//} # ./BridgeLabiPhone.xcodeproj -> BridgeLabiPhone.xcodeproj 49 | path=${path//.xcodeproj/} # BridgeLabiPhone.xcodeproj -> BridgeLabiPhone 50 | 51 | myworkspace=$path".xcworkspace" # workspace名字 52 | myscheme=$path # scheme名字 53 | 54 | # 清除上次编译数据 55 | if [ -d ./derivedData ]; then 56 | echo -e $COLOR_SUCC'-----清除上次编译数据derivedData-----'$COLOR_SUCC 57 | rm -rf ./derivedData 58 | fi 59 | 60 | # xcodebuild clean 61 | xcodebuild -scheme $myscheme -workspace $myworkspace clean 62 | 63 | 64 | # # 生成编译数据 65 | xcodebuild -scheme $myscheme -workspace $myworkspace -configuration Debug | xcpretty -r json-compilation-database -o compile_commands.json 66 | 67 | if [ -f ./compile_commands.json ]; then 68 | echo -e $COLOR_SUCC'编译数据生成完毕😄😄😄'$COLOR_SUCC 69 | else 70 | echo -e $COLOR_ERR'编译数据生成失败😭😭😭'$COLOR_ERR 71 | return -1 72 | fi 73 | 74 | # 生成报表 75 | oclint-json-compilation-database -e Pods -- -report-type html -o oclintReport.html \ 76 | -rc LONG_LINE=200 \ 77 | -disable-rule ShortVariableName \ 78 | -disable-rule ObjCAssignIvarOutsideAccessors \ 79 | -disable-rule AssignIvarOutsideAccessors \ 80 | -max-priority-1=100000 \ 81 | -max-priority-2=100000 \ 82 | -max-priority-3=100000 83 | 84 | if [ -f ./oclintReport.html ]; then 85 | rm compile_commands.json 86 | echo -e $COLOR_SUCC'😄分析完毕😄'$COLOR_SUCC 87 | else 88 | echo -e $COLOR_ERR'😢分析失败😢'$COLOR_ERR 89 | return -1 90 | fi 91 | 92 | echo -e $COLOR_AW'将为大爷自动打开 lint 的分析结果'$COLOR_AW 93 | # 用 safari 浏览器打开 oclint 的结果 94 | open -a "/Applications/Safari.app" oclintReport.html 95 | } 96 | 97 | oclintForProject -------------------------------------------------------------------------------- /Images/autoreleasepool_linkedlist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/autoreleasepool_linkedlist.jpg -------------------------------------------------------------------------------- /Images/autoreleasepool_pop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/autoreleasepool_pop.jpg -------------------------------------------------------------------------------- /Images/autoreleasepool_push.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/autoreleasepool_push.jpg -------------------------------------------------------------------------------- /Images/block_copy__block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/block_copy__block.png -------------------------------------------------------------------------------- /Images/block_forwarding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/block_forwarding.png -------------------------------------------------------------------------------- /Images/border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/border.png -------------------------------------------------------------------------------- /Images/bounds_coordinatesystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/bounds_coordinatesystem.png -------------------------------------------------------------------------------- /Images/bounds_originchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/bounds_originchange.png -------------------------------------------------------------------------------- /Images/bounds_originchange_display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/bounds_originchange_display.png -------------------------------------------------------------------------------- /Images/coreanimation_shadow_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/coreanimation_shadow_path.png -------------------------------------------------------------------------------- /Images/custom-presentation-and-animator-objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/custom-presentation-and-animator-objects.png -------------------------------------------------------------------------------- /Images/customcollectionviewlayout_coreprocess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/customcollectionviewlayout_coreprocess.png -------------------------------------------------------------------------------- /Images/downsample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/downsample.png -------------------------------------------------------------------------------- /Images/downsampling-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/downsampling-1.png -------------------------------------------------------------------------------- /Images/dyld3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/dyld3.png -------------------------------------------------------------------------------- /Images/dynamic_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/dynamic_link.png -------------------------------------------------------------------------------- /Images/forwarding_multipleinheritance.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/forwarding_multipleinheritance.gif -------------------------------------------------------------------------------- /Images/hitch_ratio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/hitch_ratio.png -------------------------------------------------------------------------------- /Images/hitch_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/hitch_time.png -------------------------------------------------------------------------------- /Images/https-http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/https-http.png -------------------------------------------------------------------------------- /Images/https_Man_in_the_middle_attack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/https_Man_in_the_middle_attack.png -------------------------------------------------------------------------------- /Images/https_certificate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/https_certificate.png -------------------------------------------------------------------------------- /Images/https_charles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/https_charles.png -------------------------------------------------------------------------------- /Images/https加密过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/https加密过程.png -------------------------------------------------------------------------------- /Images/http响应报文.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/http响应报文.png -------------------------------------------------------------------------------- /Images/http组成.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/http组成.png -------------------------------------------------------------------------------- /Images/http首部.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/http首部.jpeg -------------------------------------------------------------------------------- /Images/iOSLearning.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/iOSLearning.webp -------------------------------------------------------------------------------- /Images/iOS_build_link.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/iOS_build_link.jpg -------------------------------------------------------------------------------- /Images/imagebuffer_databuffer_framebuffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/imagebuffer_databuffer_framebuffer.png -------------------------------------------------------------------------------- /Images/initializersExample02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/initializersExample02.png -------------------------------------------------------------------------------- /Images/initializersExample03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/initializersExample03.png -------------------------------------------------------------------------------- /Images/instance_class_metaclass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/instance_class_metaclass.png -------------------------------------------------------------------------------- /Images/ios-background-remotenotification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios-background-remotenotification.png -------------------------------------------------------------------------------- /Images/ios-background-transfer-service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios-background-transfer-service.png -------------------------------------------------------------------------------- /Images/ios-backgroundfetch-vs-remotenotification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios-backgroundfetch-vs-remotenotification.png -------------------------------------------------------------------------------- /Images/ios-backgroundtasks-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios-backgroundtasks-structure.png -------------------------------------------------------------------------------- /Images/ios-multitask-backgroundtask-ios6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios-multitask-backgroundtask-ios6.png -------------------------------------------------------------------------------- /Images/ios-multitask-backgroundtask-ios7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios-multitask-backgroundtask-ios7.png -------------------------------------------------------------------------------- /Images/ios7-backgroundfetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios7-backgroundfetch.png -------------------------------------------------------------------------------- /Images/ios_frame_drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios_frame_drop.png -------------------------------------------------------------------------------- /Images/ios_sandbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios_sandbox.png -------------------------------------------------------------------------------- /Images/ios_screen_display_cpu_gpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios_screen_display_cpu_gpu.png -------------------------------------------------------------------------------- /Images/ios_vsync_runloop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/ios_vsync_runloop.png -------------------------------------------------------------------------------- /Images/jd_cpustuck_gpustuck.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/jd_cpustuck_gpustuck.gif -------------------------------------------------------------------------------- /Images/kvc_getter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/kvc_getter.png -------------------------------------------------------------------------------- /Images/kvc_setter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/kvc_setter.png -------------------------------------------------------------------------------- /Images/layer_border_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/layer_border_background.png -------------------------------------------------------------------------------- /Images/oc_initialization_process.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/oc_initialization_process.gif -------------------------------------------------------------------------------- /Images/oc_isa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/oc_isa.png -------------------------------------------------------------------------------- /Images/runtime_associatedobject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/runtime_associatedobject.png -------------------------------------------------------------------------------- /Images/runtime_messaging1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/runtime_messaging1.gif -------------------------------------------------------------------------------- /Images/size-class 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/size-class 3.png -------------------------------------------------------------------------------- /Images/size_class_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/size_class_open.png -------------------------------------------------------------------------------- /Images/sizeclass-vary_for_trait.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/sizeclass-vary_for_trait.jpeg -------------------------------------------------------------------------------- /Images/static_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/static_link.png -------------------------------------------------------------------------------- /Images/stuck_benchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/stuck_benchmark.png -------------------------------------------------------------------------------- /Images/tcpip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/tcpip.png -------------------------------------------------------------------------------- /Images/transitioning-context-object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/transitioning-context-object.png -------------------------------------------------------------------------------- /Images/tree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/tree.jpg -------------------------------------------------------------------------------- /Images/tree_array.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/tree_array.jpg -------------------------------------------------------------------------------- /Images/uigesturerecognizer_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/uigesturerecognizer_state.png -------------------------------------------------------------------------------- /Images/uikit_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/uikit_btn.png -------------------------------------------------------------------------------- /Images/xcode_embed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Images/xcode_embed.png -------------------------------------------------------------------------------- /Implementing a Custom Gesture Recognizer.md: -------------------------------------------------------------------------------- 1 | # Implementing a Custom Gesture Recognizer 2 | 3 | 核心的工作就是 4 | 5 | 1. 自定义`GestureRecognizer`继承自`UIGestureRecognizer` 6 | 2. 明确要实现类型是`Dicrete`还是`Continous` 7 | 3. 根据类型利用好不同`Gesture Recognizer State Machine`机制 8 | 3. 重写`UIGestureRecognizer`的`touchBegan:`等一系列touch方法,在合适的时机为`state`赋值合适的状态 9 | 4. 处理`Cancellation`情况,即重写`touchCancel`方法 10 | 5. 重写`reset`方法(每次手势识别器识别结束(不论成功与否)后,下次新的手势识别前都会执行该方法,并将`state`置为`possible`),重置自定义属性的状态 11 | 12 | ## 参考 13 | - [Implementing a Custom Gesture Recognizer](https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer) 14 | - [About the Gesture Recognizer State Machine 15 | ](https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer/about_the_gesture_recognizer_state_machine) 16 | -------------------------------------------------------------------------------- /Interview/iOS设计题之findFiles.md: -------------------------------------------------------------------------------- 1 | # iOS设计题之findFiles 2 | 3 | 项目中有这么一个方法func findfile(dir: String suffix: String) -> [String] ,可以通过输入文件夹目录,和后缀检索出所需的文件。 4 | 5 | 例如需要在某个文件中检索txt文件或者mp4文件,那就传入dir和suffix就行了。现在又有一些需求,例如需要检索utf8格式的txt或者h264编码的mp4,也会有一些例如查找最近一周更新过的文件这样的需求,你如何优化这个类,让它满足这些情况? 6 | 7 | 本题目来自--[zhangferry-快手iOS面经](https://zhangferry.com/2020/03/28/interview_kuaishou/) 8 | 9 | ## 解题思路 10 | 11 | 阅读题目,经过短暂的思考: 12 | 13 | - 显然该题目的关键点在于,要不断为该方法丰富满足各种条件的检索能力 14 | - 简单直白的做法无非是遇到一个检索条件就向该方法添加一个相应的参数,比如该题目中提到的文件格式和更新时间 15 | - 用脚指头也能猜到这肯定不是一个好方案 16 | - 如果单纯的增加方法的参数,那该方法就破坏了职责单一原则,调用方使用起来会产生困惑,不知道如何传递参数 17 | - 进一步分析,该方法的核心逻辑肯定是通过遍历目的目录下所有的文件(可能是递归地遍历),如果符合条件就添加到结果集中 18 | - 所以,如果我们将是否符合条件的判断交给调用方,那方法所做的事情就职责单一了 19 | - 其实很自然就能想到类似Swift中集合的`sort`方法的实现--将判断逻辑封装为一个closure传入方法中进行排序 20 | 21 | 根据分析,下面是优化后的方法伪代码: 22 | 23 | ``` 24 | func findfiles(by shouldBeInclude: (FileInfo) -> Bool) -> [String] { 25 | var files: [String] = [] 26 | // iterate all files and find the target file 27 | let fileInfo = FileInfo(filename: "abc", suffix: "txt", encodeFormat: .utf8) 28 | if shouldBeInclude(fileInfo) { 29 | files.append(fileInfo.filename) 30 | } 31 | return files 32 | } 33 | 34 | let txtAndmp4Files = findfiles(dir: "dir") { 35 | $0.suffix == "txt" || $0.suffix == "mp4" 36 | } 37 | 38 | let utf8Andh264Files = findfiles(dir: "dir") { 39 | $0.encodeFormat == .utf8 || $0.encodeFormat == .h264 40 | } 41 | ``` 42 | 43 | 优点: 44 | 45 | 1. 方法职责单一 46 | 2. 符合开闭原则,当需要添加新的判断逻辑时编写新的closure即可,无需修改方法本身逻辑 47 | 48 | 简单总结下: 49 | 50 | - 该题主要考察了是否有用block做逻辑抽象的经验 51 | - 对block或closure比较感兴趣或者用的多的朋友感觉这题目很简单,反之可能想不到 52 | - 所以该题目针对性有点强,且规模不大,算不上较通用的设计题目 -------------------------------------------------------------------------------- /Interview/iOS面试之weak原理.md: -------------------------------------------------------------------------------- 1 | # iOS面试之weak原理 2 | 3 | > 拒绝八股文,用理论知识+刻意练习碾压面试 4 | 5 | 面试题:weak属性是如何实现? 6 | 7 | > 个人认为这个题真的没啥意思,能考察候选人什么能力?看没看过runtime代码?还不切换Swift,都什么年代了。。。 8 | 9 | 提了三个问题,以这三个问题展开: 10 | 11 | 1. 看源码,了解weak的实现原理 12 | 2. 聊一下自己对该设计的感觉 13 | 3. 查一下有没有公司或个人受weak原理的启发而将其应用到项目实践中 14 | 15 | ## weak原理 16 | 17 | 起初,看了几篇讲解源码的文章,实在看不下去,源码虽不多,但细节很多,其实还是不太容易理清楚作者设计的初衷的,所以以下会用更通俗易懂的语言来描述weak的底层实现 18 | 19 | - `SideTable`封装了一个`weak_table_t` 20 | - `SideTable`是全局的 21 | 22 | ``` 23 | struct SideTable { 24 | spinlock_t slock; // 锁 25 | RefcountMap refcnts; // 引用计数表 26 | weak_table_t weak_table; // weak 表 27 | }; 28 | ``` 29 | 30 | - `weak_table_t`,weak表是真正存数据的哈希表 31 | - 其中有多个entry,每个entry表示一个对象地址和多个弱引用(弱引用数组)之间的关系,\ 32 | 33 | ``` 34 | struct weak_table_t { 35 | weak_entry_t *weak_entries; // hash数组,用来存储弱引用对象的相关信息 36 | size_t num_entries; 37 | uintptr_t mask; 38 | uintptr_t max_hash_displacement; 39 | }; 40 | ``` 41 | 42 | ``` 43 | struct weak_entry_t { 44 | DisguisedPtr referent; 45 | union { 46 | struct { 47 | weak_referrer_t *referrers; 48 | uintptr_t out_of_line_ness : 2; 49 | uintptr_t num_refs : PTR_MINUS_2; 50 | uintptr_t mask; 51 | uintptr_t max_hash_displacement; 52 | }; 53 | struct { 54 | weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; 55 | }; 56 | }; 57 | }; 58 | ``` 59 | 60 | ### 弱引用添加移除逻辑 61 | 62 | 在两个重要的时机,对`SideTable`的数据做操作:声明弱引用或给若引用赋值时、被引用的对象dealloc时 63 | 64 | - 声明弱引用或给弱引用赋值时 65 | - 通过对象的地址,在`SideTable`中找到对应entry,在弱引用数组中将旧的不再需要弱引用数据移除 66 | - 将新声明的弱引用加入到数组中 67 | - 被引用的对象dealloc时 68 | - 类似,找到对应entry,直接清除 69 | 70 | ## 如何看该设计 71 | 72 | 全局哈希表,检索效率高,换作我也这么设计。没了 73 | 74 | 我认为更优价值的东西,比如系统中为什么存储多个而不是一个`SideTable`,处于什么考量? 75 | 76 | ## weak原理在项目中的实践 77 | 78 | 这是唐巧之前提过的一个有意思的面试题(勉强算与weak有点关系吧): 79 | 80 | 我们知道, 从 Storyboard 往编译器拖出来的 UI 控件的属性是 weak 的, 如下所示: 81 | 82 | ``` 83 | @property (weak, nonatomic) IBOutlet UIButton *myButton; 84 | ``` 85 | 86 | 如果有一些 UI 控件我们要用代码的方式来创建, 那么它应该用 weak 还是 strong 呢? 为什么? 87 | 88 | 分析及答案 89 | 这是一道有意思的问题, 这个问题是我当时和 Lancy 一起写猿题库 App 时产生的一次小争论. 简单来说, 这道题并没有标准答案, 但是答案背后的解释却非常有价值, 能够看出一个人对于引用计数, 对于 view 的生命周期的理解是否到位. 90 | 91 | 从昨天的评论上, 我们就能看到一些理解非常不到位的解释, 例如: 92 | 93 | > @spume 说:Storyboard 拖线使用 weak 是为了规避出现循环引用的问题。 94 | 95 | 这个理解是错误的, Storyboard 拖出来的控件即使是 strong 的, 也不会有循环引用问题. 96 | 97 | 我认为 UI 控件用默认用 weak, 根源还是苹果希望只有这些 UI 控件的父 View 来强引用它们, 而 ViewController 只需要强引用 ViewController.view 成员, 则可以间接持有所有的 UI 控件. 这样有一个好处是: 在以前, 当系统收到 Memory Warning 时, 会触发 ViewController 的 viewDidUnload 方法, 这样的弱引用方式, 可以让整个 view 整体都得到释放, 也更方便重建时整体重新构造. 98 | 99 | 但是首先 viewDidUnload 方法在 iOS 6 开始就被废弃掉了, 苹果用了更简单有效地方式来解决内存警告时的视图资源释放, 具体如何做的呢? 总之就是, 除非你特殊地操作 view 成员, ViewController.view 的生命期和 ViewController 是一样的了. 100 | 101 | 所以在这种情况下, 其实 UI 控件是不是 weak 其实关系并不大. 当 UI 控件是 weak 时, 它的引用计数是 1, 持有它的是它的 superview, 当 UI 控件是 strong 时, 它的引用计数是 2, 持有它的有两个地方, 一个是它的 superview, 另一个是这个 strong 的指针. UI 控件并不会持有别的对象, 所以, 不管是手写代码还是 Storyboard, UI 控件是 strong 都不会有循环引用的. 102 | 103 | 那么回到我们的最初的问题, 自己写的 view 成员, 应该用 weak 还是 strong? 我个人觉得应该用 strong, 因为用 weak 并没有什么特别的优势, 加上上一篇面试题文章中, 我们还看到, 其实 weak 变量会有额外的系统维护开销的, 如果你没有使用它的特别的理由, 那么用 strong 的话应该更好. 104 | 105 | 另外有读者也提到, 如果你要做 Lazy 加载, 那么你也只能选择用 strong. 106 | 107 | 当然, 如果你非要用 weak, 其实也没什么问题, 只需要注意在赋值前, 先把这个对象用 addSubView 加到父 view 上, 否则可能刚刚创建完, 它就被释放了. 108 | 109 | 在我心目中, 这才是我喜欢的面试题, 没有标准答案, 每种方案各有各的特点, 面试者能够足够分清楚每种方案的优缺点, 结合具体的场景做选择, 这才是优秀的面试者. 110 | 111 | ## Q&A 112 | 113 | - 如何单步调试到runtime源码 114 | 115 | ## 参考 116 | - [[iOS底层] - weak原理](https://www.ljcoder.com/486aa82fd77f.html) 117 | - [iOS 面试题(六):自己写的 view 成员,应该用 weak 还是 strong?](https://mp.weixin.qq.com/s/Tul94tyc_qYGjn3bXaPlmg) 118 | - [iOS 面试题(五):weak 的内部实现原理](https://mp.weixin.qq.com/s/5nYZXi1ZPNm0_f95CYx0SA) -------------------------------------------------------------------------------- /Interview/一名iOS高级工程师可能需要具备什么能力.md: -------------------------------------------------------------------------------- 1 | # 一名iOS高级工程师可能需要具备什么能力 2 | 3 | > 2023年01月25日 4 | 5 | ## 说在前面 6 | 7 | 当读者看了后面关于“需要具备什么能力”内容后,可能会觉得比较难 8 | 9 | 如是说,我的感觉是一样的。其实说到这个话题我有挺多感想、抱怨想说,但还是控制一下,这里只简单地驳斥一个观点: 10 | 11 | - 不是iOS行业不行了,是你能力不够 12 | 13 | 我很理解这句话的意思,无非是勉励求职者充实自己多学习而已。 14 | 15 | 我要驳斥的是, 16 | 17 | - 不可否认这个社会有能力的人有不少,特别是我见过一些对技术超级热爱,无需过多的压力自己就能快速学习。但这肯定是少数,大部分人是比较平庸的 18 | - 所谓平庸,比如懒惰经常战胜自律,一件正确的事情很难坚持下去,很长时间一直原地踏步没有大的进步,尽管他可能还是想做出点事情 19 | - 那么我们这些平庸的人的出路在哪? 20 | - 如果了解一下其他发达国家的情况,要知道一个好的社会,是有责任让这些平庸的大多数通过自己的努力买上房、车,过上舒服的生活 21 | - 只是简单扔出一句“能力不够”,并没太考虑大环境,对平庸的人也不公平 22 | - 网上看到一句话,大致意思,是一个年轻人每天按时上下班,甚至经常无偿加班,周末在家刷刷手机,聚餐 23 | - 这在任何其他国家都是一个励志或者至少算是积极的年轻人 24 | - 但在我们国家却被人们说成不努力,躺平 25 | - 诚然,社会环境我们改变不了。那在这种情况下大部分平庸的人该如何做,才是更有价值的问题 26 | 27 | 如何做?我也一直在探索和实践,也期望以后能分享一下心得 28 | 29 | 本文算是列一下iOS中比较有价值的问题,也可以看做是面试题吧 30 | 31 | 但我本人始终反对所谓的背八股文 32 | 33 | 我会通过查阅资料了解每个题目背后知识点的原理,理解后再加以刻意练习,希望能做到1、2、3个月后,遇到该问题,还能根据底层的基础知识推导出问题的答案 34 | 35 | ## 对高级title的理解 36 | 37 | 开始之前简单的介绍一些我对“高级”的理解: 38 | 39 | 国内外对title的定义区别不太一样,国内公司通常将工程师分成:初级、中级、高级、资深、技术专家等,后面可能还会有总监之类 40 | 41 | 国外的分级少一些,junior, middle, senior,在往上可能就是architect(架构师) 42 | 43 | 本文所说的内容更多是面向于国内的高级、资深、技术专家以及国外的senior这个层面 44 | 45 | ## 正文 46 | 47 | ### 架构与设计 48 | 49 | 基础部分 50 | 51 | - MVC、MVVM、MVP、VIPER优缺点 52 | - 单向数据流 53 | - 组件化 54 | - 常用组件化方案 55 | - 组件间如何通讯 56 | - Router(路由)有哪些方案 57 | - 熟悉基本的设计原则 58 | - 熟悉常见的设计模式 59 | - 响应式编程 60 | 61 | 实践部分 62 | 63 | 请参考与本文件同目录下的设计题 64 | 65 | ### iOS基础 66 | 67 | - Block 68 | - capture variable原理 69 | - __block 变量 vs 普通变量 70 | - KVO 71 | - KVO工作原理 72 | - Runloop原理 73 | - OC runtime相关 74 | - 消息发送、消息转发 75 | - Method Swizzling 76 | - ARC内存管理原理 77 | - 数据持久化 78 | - UserDefaults 79 | - Core Data 80 | - Sqlite Database 81 | - Keychain 82 | - Realm database 83 | - NSKeyedArchiver and NSKeyedUnarchive 84 | - Saving files directly on the file system 85 | - Plist 86 | 87 | ### UI 88 | 89 | - 熟悉Autolayout 90 | - 事件响应链 91 | - UI渲染过程 92 | - CPU和GPU侧的职责 93 | - 什么是离屏渲染 94 | - 异步绘制 95 | - 熟悉CoreAnimation 96 | 97 | ### 各种优化 98 | 99 | - 卡顿优化 100 | - 包体积优化 101 | - 启动优化 102 | 103 | ### 源码阅读 104 | 105 | - SDWebImage, AFNetworking, Texture 106 | 107 | ### 代码熟练程度 108 | 109 | 该部分想说的是对于一些常规的功能,面试者能否比较流畅地给出解决方案并编码实现,主要考察候选人对开发工具和iOS常见技术的熟练程度 110 | 111 | 其实国内面试考的不太多, 112 | 113 | ### 其他 114 | 115 | - Cocoapods 116 | 117 | ## 参考 118 | 119 | - [So you think you can call yourself a "Senior iOS Developer"?](https://blog.undabot.com/so-you-think-you-can-call-yourself-a-senior-ios-developer-767fb9d6e423) 120 | - [一封来自大牛的招聘感悟: iOS开发人群到底怎么了?](https://cloud.tencent.com/developer/article/1382783?from=article.detail.1382782&areaSource=106000.1&traceId=nzKWh4lQsr7KEf_AysUL8) 121 | - [iOS面试总结(2020年6月)](https://zhangferry.com/2020/07/24/interview_summary_202006/) -------------------------------------------------------------------------------- /KVC in iOS.md: -------------------------------------------------------------------------------- 1 | # KVC in iOS 2 | 3 | KVC,Key-value coding,是一种可以实现对**对象的属性**进行非直接访问的一种方式,这种方式要求对象必须实现`NSKeyValueCoding`协议 4 | 5 | `NSObject`实现了`NSKeyValueCoding`协议的方法,所以它的子类可以直接用KVC的方法 6 | ## 常用方法 7 | ``` 8 | // 直接获取或设置属性值 9 | valueForKey:/valueForKeyPath: 10 | setValue:forKey:/setValue:forKeyPath: 11 | // 获取可变类型集合数据方法 12 | mutableArrayValueForKey:/mutableArrayValueForKeyPath: 13 | mutableSetValueForKey:/mutableSetValueForKeyPath: 14 | ``` 15 | - 当书写`keyPath`时,第一个属性部分是相对于`message receving object`而言的 16 | - `[department valueForKeyPath:@"employee.salary"]` 17 | - `employee`是相对于`department`而言 18 | - 当获取可变的集合类型数据时,得到的是一个代理对象,可以直接对该集合进行操作,操作的结果会传递到真正的非可变集合数据中 19 | - KVC的方法对对象类型属性和非对象类型属性(如`int`)等同视之 20 | 21 | ## 集合操作符(CollectionOperator) 22 | 23 | 当获取集合类型数据时,支持加入集合操作符,这样可以对返回的集合数据进行合并、求平均、求最值等简单操作,最终返回的是计算的结果值 24 | 25 | 操作符格式是 26 | 27 | `keypathToCollection.@collectionOperator.keypathToProperty` 28 | 29 | 举例 30 | 31 | ``` 32 | /// 获取交易信息中最早的时间 33 | NSDate *earliestDate = [self.transactions valueForKeyPath:@"@min.date"]; 34 | 35 | /// 获取所有交易信息中的交易人(payee)信息,而且交易人不重复 36 | /// 重复的判断需要`isEqual`方法的支持 37 | NSArray *distinctPayees = [self.transactions valueForKeyPath:@"@distinctUnionOfObjects.payee"]; 38 | /// 对集合的集合使用操作符 39 | NSArray* moreTransactions = @[<# transaction data #>]; 40 | NSArray* arrayOfArrays = @[self.transactions, moreTransactions]; 41 | NSArray *collectedDistinctPayees = [arrayOfArrays valueForKeyPath:@"@distinctUnionOfArrays.payee"]; 42 | ``` 43 | 44 | |集合操作符|功能|| 45 | |:-:|:-:|:-:| 46 | |@count|元素个数|无需keypathToProperty| 47 | |@avg/@sum|求平均、求和|| 48 | |@max/@min|求最值|| 49 | |@distinctUnionOfObjects|聚合对象,对象不重复|| 50 | |@unionOfObjects|聚合对象,允许重复|| 51 | |@distinctUnionOfArrays|将外层数组中每个数组里的每个对象的属性进行非重复聚合|| 52 | |@unionOfArrays|功能和上面类似,结果有重复|| 53 | |@distinctUnionOfSets|对集合的集合进行非重复聚合| 54 | 55 | ## KVC原理 56 | 57 | 本质上,实现了`NSKeyValueCoding`协议的`NSObject`在执行KVC的方法时,就是通过`key`去找匹配的`ivar`(成员变量),然后再进行`get`或`set` 58 | 59 | 那么最重要的也就是从`key`到`ivar`的查找过程了,官方有做详细说明,但由于都是文字,可能比较晦涩,这里贴上掘金上一个[大佬](https://juejin.im/post/5e5e06ba51882549063a9011)的总结图 60 | 61 | ![setter](https://github.com/songgeb/I-Love-iOS/blob/master/Images/kvc_setter.png?raw=true) 62 | 63 | ![getter](https://github.com/songgeb/I-Love-iOS/blob/master/Images/kvc_getter.png?raw=true) 64 | 65 | ## KVC in Swift? 66 | 67 | 当一个Class或其他Type实现了NSKeyValueCoding协议时,才可以说该类是KVC compliant或者说支持KVC的 68 | 69 | NSObject实现了该协议,当然,在Swift中也可以让其他类型实现该协议 70 | 71 | ## 应用场景 72 | 1. Objective C中Model层数据转换,比如JSON转Model 73 | 2. 通过KVC可以查看或修改一些iOS系统内部的私有属性,但不推荐发布到release环境,可能审核不通过 74 | 75 | ## 疑问 76 | 77 | ## 参考 78 | - [Introduction to Key-Value Observing Programming Guide 79 | ](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) 80 | - [Key-Value Coding Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/index.html) -------------------------------------------------------------------------------- /KVO Crash in iOS.md: -------------------------------------------------------------------------------- 1 | # KVO Crash in iOS 2 | 3 | ## KVO Crash for NSURLSessionTask.state 4 | 5 | - NSURLSessionTask.state中的cancel不一定仅执行一次 6 | 7 | ## 问题 8 | 9 | ### kvo只要发生在子线程是否都可能存在问题? 10 | 11 | ``` 12 | - (IBAction)buttonAction:(id)sender 13 | { 14 | #pragma unused(sender) 15 | [self performSelectorInBackground:@selector(recalculate) withObject:nil]; 16 | } 17 | 18 | - (void)recalculate 19 | { 20 | while ( ! self.cancelled ) { 21 | [... calculate ...] 22 | } 23 | } 24 | 25 | - (void)viewWillDisappear:(BOOL)animated 26 | { 27 | [super viewWillDisappear:animated]; 28 | self.cancelled = YES; 29 | // race starts here 30 | } 31 | ``` 32 | 33 | > Similar problems can arise when you use key-value observing (KVO) to observe the isFinished property of an NSOperation. While KVO does not retain either the observer or the observee, it's still possible that, even if you remove the observer in your -viewWillDisappear: method, a KVO notification might already be in flight for your object. If that happens, the thread running the notification could end up calling a deallocated object! 34 | 35 | - 以上代码和内容摘自苹果官方文档-[The Deallocation Problem](https://developer.apple.com/library/archive/technotes/tn2109/_index.html#//apple_ref/doc/uid/DTS40010274-CH1-SUBSECTION11) 36 | - 上面的代码想要说明的是 37 | - recalculate中访问了self,且在子线程中执行,viewWillDisappear在主线程中执行 38 | - 如果后者先结束,前者后结束,那self即当前ViewController则最终会在子线程中释放,导致其dealloc在子线程中执行,如果dealloc中写了使用UIKit的代码,则可能有问题 39 | - 内容部分的意思是,类似的问题也可能发生在kvo场景下 40 | - 比如,如果我们通过kvo对NSOperation的isFinished属性进行监听 41 | 42 | ## 参考 43 | - [KVO background threads](https://stackoverflow.com/questions/9154721/kvo-background-threads) 44 | - [Receptionist Pattern](https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ReceptionistPattern/ReceptionistPattern.html) 45 | - [99% 的 iOS 开发都不知道的 KVO 崩溃](https://juejin.cn/post/7193677784097488933) -------------------------------------------------------------------------------- /KeyChain in iOS.md: -------------------------------------------------------------------------------- 1 | # KeyChain in iOS 2 | 3 | - KeyChain是2013年iOS 7引入的技术 4 | - 类似于一个内置在系统中的一个加密数据库 5 | - 适合存放比较隐私且体量较小的数据,比如密码、token等 6 | - 通常情况下一个App只能访问自己添加到KeyChain的item(当然,一个公司或group下的其他App,通过配置也能共享) 7 | 8 | ## 疑问 9 | 1. 10 | 11 | ## 参考 12 | - [WWDC 2013 Session 709 PDF-Protecting Secrets with the Keychain](https://docs.huihoo.com/apple/wwdc/2013/session_709__protecting_secrets_with_the_keychain.pdf) 13 | - [Adding a Password to the Keychain](https://developer.apple.com/documentation/security/keychain_services/keychain_items/adding_a_password_to_the_keychain) 14 | - [OSStatus.com](https://osstatus.com/search/results?platform=all&framework=all&search=-50) -------------------------------------------------------------------------------- /LLDB debug 技巧 in iOS.md: -------------------------------------------------------------------------------- 1 | # LLDB debug 技巧 in iOS 2 | 3 | ## 参考 4 | - [Intermediate iOS Debugging](https://medium.com/@crafttang/intermediate-ios-debugging-53d33efdff) -------------------------------------------------------------------------------- /Layout/SizeClass in iOS.md: -------------------------------------------------------------------------------- 1 | # SizeClass in iOS 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | > 代码与截图基于`Xcode 10.1 + Swift 4.2` 6 | 7 | ## 概念 8 | - **iOS 8**引入了Size Class概念 9 | - 是因为iOS设备(包括iPod、iPhone、iPad等)尺寸太多,之前开发者为了适配需要了解每个设备的宽、高 10 | - ***而Size Class是想为所有尺寸的iOS设备抽象出一个统一的尺寸描述规则*** 11 | 12 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/size-class%203.png?raw=true) 13 | 14 | 1. 图中列出了当时(2017年)所有iPhone和iPad的尺寸(半透明的矩形代表不同设备),并且提供了portrait和landscape两个方向 15 | 2. 两条白线列出了四个象限,分别对应着`compact`和`regular`的尺寸。compact小于regular 16 | 3. 白线的位置距离左上角是否固定?目前来看iPhone Xs landscape下的宽度应该是`wC`和的`wR`的界限(width compact、width regular);同样iPhone Xs landscape下的高度应该是`hC`和`hR`的界限。随着新设备出来,我想白线是会变的 17 | 3. 任何一个设备,左上角对齐放入这个坐标系下,宽或高所在的象限就是它的Size Class值 18 | 4. 例如,iPhone Xs Max的landscape情况下,是compact height + regular width 19 | 20 | ## Interface Builder中的使用 21 | 22 | > 默认状态下,设置的属性适用于所有的Size Class值 23 | 24 | 开启/关闭Size Class的位置 25 | 26 | 27 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/size_class_open.png?raw=true) 28 | 29 | ### 套路一 30 | 31 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/sizeclass-vary_for_trait.jpeg?raw=true) 32 | 33 | 1. 选择`vary for trait`,选择`height`或`width`,也可都选。解释下弹出的`height`和`width`两个选项 34 | - 由前面`device` + `orientation`可以确定一个Size Class值了,比如`wC * hR`。如果选择了`height`,则表示后面我们要添加的一些属性比如autolayout,将针对所有符合`hR`状态(即宽度上比较窄时,例如iPhone 垂直状态、iPad垂直和landscape状态)的设备 35 | - 其实选中`height`或`width`后,`Xcode`会自动提示有哪些状态符合条件。类似`Varying 42 Regular Height Devices` 36 | 2. 选好后,后面针对这种状态自定义自己想要的属性就行了,比如添加autolayout 37 | 38 | 3. 选择`Done Varying` 39 | 40 | ### 套路二 41 | 42 | 我们通过Interface Builder给各种UI空间添加属性时,会在属性左边看到`+`号,这里可以添加各种Size Class下不同的属性值 43 | 44 | ## 代码中的使用 45 | 46 | - 通过`UITraitCollection`对象可以获取到Size Class值 47 | - `UITraitCollection`是`UITraitEnvironment`协议的属性 48 | - 下面这些类实现`UITraitEnvironment`协议 49 | - **UIScreen**, **UIWindow**, **UIViewController**, **UIPresentationController**, **UIView** 50 | 51 | 52 | ## 参考 53 | 54 | - [Size Classes and Core Components-WWDC2017](https://developer.apple.com/videos/play/wwdc2017/812/) 55 | - [What is 'Vary for Traits' in Xcode 8?](https://stackoverflow.com/questions/39890055/what-is-vary-for-traits-in-xcode-8) 56 | - [Building Adaptive User Interfaces](https://developer.apple.com/design/adaptivity/) -------------------------------------------------------------------------------- /Layout/anchor in iOS.md: -------------------------------------------------------------------------------- 1 | # anchor in iOS 2 | 3 | 验证是否理解了知识的一个重要方法,就是见足够多的题目(场景) 4 | 5 | 再提anchor源于一个题目: 6 | 7 | ``` 8 | 9 | ``` 10 | 11 | const char * a; 12 | char const *a; 13 | char * const a 14 | 15 | ## 参考 16 | -------------------------------------------------------------------------------- /Layout/bounds in iOS.md: -------------------------------------------------------------------------------- 1 | # bounds in iOS 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | bounds指的是View.bounds 6 | 7 | 与其关联的内容其实还是挺多的,bounds与内容绘制、动画等都有关系 8 | 9 | 由于笔者能力限制,无法全部覆盖,本文仅解释一下bounds.origin的原理、意义 10 | 11 | > 当然,由于bounds内部的源码是不知道的,所以以下所有的内容都是根据经验和试验得出 12 | 13 | ## 问题引入 14 | 15 | 你是否曾遇到过下面的问题? 16 | 17 | 1. 作为iOSer,你是否经常在面试中被问到Frame和Bounds区别是什么? 18 | - 可能你会很轻易的回答出Frame的意思。但Bounds呢?是不是会说“Bounds是基于自己坐标系的rectangle信息” 19 | - 那这个所谓的“基于自己的坐标系”你是否真正理解呢? 20 | - 我看过几个讲解Frame vs Bounds的视频,一个共同点是,当讲解到bounds.origin时,就卡了,含糊地带过 21 | 2. 当修改Bounds.origin时,所有的subViews进行了位移,但位移为什么和第一感觉不一致呢? 22 | - 该如何解释这种感觉不一致呢? 23 | 24 | 如上的问题,我都有过 25 | 26 | 下面我尝试用形象一些的思路来解释`Bounds.origin` 27 | 28 | ## Bounds.origin 29 | 30 | 首先,列出官方对Bounds的解释 31 | 32 | > UIView.bounds, The bounds rectangle, which describes the view’s location and size in its own coordinate system. 33 | 34 | 关键点在于`coordinate system`指的是什么,坐标系原点在哪? 35 | 36 | ### View's own coordinate system 37 | 38 | 先看另一端官方的描述 39 | 40 | > The bounds property defines the internal dimensions of the view as it sees them, and its use is almost exclusive to custom drawing code. 41 | 42 | 意思是,bounds定义了视图自己要显示内容的一个边界 43 | 44 | 为了方便地绘制视图内部的内容(不止是绘制内容了,像旋转、位移、缩放等效果),有一个虚拟的坐标系会让事情容易实现 45 | 46 | 所以可以想像一下,这个坐标系大概是这样子,它会跟着视图绑定在一起,即使视图旋转,坐标系也跟着转 47 | 48 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/bounds_coordinatesystem.png?raw=true) 49 | 50 | ### 坐标系原点 51 | 52 | 其实原点放在视图的左上角也不错,这样的话bounds.origin就不需要了 53 | 54 | 但实际上不是这样,可能是因为要实现scrollview的scroll效果吧 55 | 56 | 那坐标系原点在哪里 57 | 58 | - 既然bounds.origin是view在坐标系中的位置,那自然可以推导出原点在哪里了 59 | - bounds.origin可以被修改,所以坐标系的原点(或者说坐标系)也是在变的 60 | 61 | 我们看一个例子 62 | 63 | 1. 新创建一个View1,默认bounds.origin是(0, 0),则说明此时View1的左上角就是坐标系的原点 64 | 2. 我们将View1.bouns.origin变更为(10, 10),则坐标系原点就发生了位移,移到了距离View1左上角,向左向上10的位置。结果就是改坐标系下的所有内容都跟着发生位移 65 | 66 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/bounds_originchange.png?raw=true) 67 | 68 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/bounds_originchange_display.png?raw=true) 69 | 70 | ### 另一种bounds的理解角度 71 | 72 | - 也可以将bounds认为是希望显示在屏幕上的内容的区域 73 | - 比如当bounds.origin从(0, 0)变为(10, 10)时,则希望将(10, 10, width, height)区域的内容显示出来 74 | - 当然,此处的(0, 0)和(10, 10)两个坐标点都是基于视图的左上角而言的(将视图左上角视作原点) 75 | 76 | ## 关于Bounds的其他思考 77 | 78 | - 为何进行缩放后,Bounds却没有变化 79 | - 个人解释,是不是由于缩放的实现其实是将坐标系进行了缩放(刻度发生了变化),所以,其实按照逻辑点来说Bounds并没有发生变化 80 | - 当然,我大概了解,缩放等变化背后其实就是矩阵乘法等数学操作,这个所谓的坐标系其实还是虚拟的 81 | 82 | ## 参考 83 | - [UIView](https://developer.apple.com/documentation/uikit/uiview) 84 | - [UIView frame, bounds and center](https://stackoverflow.com/questions/5361369/uiview-frame-bounds-and-center/11282765#11282765) -------------------------------------------------------------------------------- /Layout/layoutSubviews执行时机.md: -------------------------------------------------------------------------------- 1 | > 深入理解代替单纯记忆 2 | 3 | ## 执行时机 4 | 5 | > 官方并没有明确说`UIView`的`layoutSubviews`方法执行时机,文中说的执行时机是总结自参考文章中,主要来自实际测试 6 | 7 | - `view.bounds`发生变化变化时 8 | - 所以,当`view.frame.size`变化时,方法会被执行,因为实际上改变的是`view.bounds.size` 9 | - 注意`bounds`发生变化意味着除了`size`,`origin`的变化也会导致方法执行,比如`UIScrollView`的滚动原理就是改变`bounds.origin` 10 | - 当然也包括size间接被修改的情况,比如subview设置了autoresizingMask,superview'size改变触发subview的size变化的情况 11 | - 如果设置前后,`bounds`的值并没有变,方法也不会被执行 12 | - direct subviews' size改变时会触发执行,注意,是直接子view 13 | - 执行`addSubview`方法时,`targetView.addSubview(subView)`,`subView`和`targetView`的`layoutSubviews`方法会执行 14 | - 通过`setNeedsLayout`或`layoutIfNeed`方法触发视图更新时 15 | - `layoutIfNeed`执行时不一定会触发`layoutSubviews`执行,需要有`layout updates`时 16 | 17 | > 再注意一点,当同时进行多层级视图的`layoutSubviews`调用时,顺序是从上层到下层 18 | 19 | ## 一点思考 20 | 21 | 根据上面触发时机很容易能看出来 22 | > `layoutSubviews`是在那些需要执行的时候执行 23 | 24 | 从事一段时间iOS开发之后就能发现,苹果的设计思路(其他机构应该也是如此)就是,尽量只在有必要的时候才增加逻辑,能不增加系统额外消耗就不增加 25 | 26 | - 就拿`addSubview`时机来说,对于`targetView`,有子视图加入了,如果我想做一些精细化的布局调整,那就得依靠`targetView`的`layoutSubviews`时机;同时,`subView`加入到新的视图体系里面,也可能有些需要依赖`superView`才能完成的逻辑,此时也是通过`layoutSubviews`时机 27 | 28 | ## 参考 29 | - [When is layoutSubviews called?](https://stackoverflow.com/questions/728372/when-is-layoutsubviews-called) 30 | -------------------------------------------------------------------------------- /NSMutableString in iOS.md: -------------------------------------------------------------------------------- 1 | # AttributedString in iOS 2 | 3 | 苹果官方文档看了一部分,没怎么看懂 4 | 5 | 6 | 7 | 8 | ## 疑问 9 | 1. RTF(Rich Text)是什么?有什么用? 10 | 2. 怎么理解attributed string的不一致性问题?设置了attributed value为什么不能修改? 11 | 12 | ## 参考 13 | - [Attributed String Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/AttributedStrings/AttributedStrings.html#//apple_ref/doc/uid/10000036-BBCCGDBG) 14 | -------------------------------------------------------------------------------- /Network/Socks协议笔记.md: -------------------------------------------------------------------------------- 1 | # Socks协议笔记 2 | 3 | - 什么是Socks协议 4 | - 如何让iOS App应用Socks协议 5 | 6 | 7 | 8 | ## 参考 9 | 10 | - [理解socks5协议的工作过程和协议细节](https://wiyi.org/socks5-protocol-in-deep.html) 11 | -------------------------------------------------------------------------------- /Network/iOS网络编程笔记.md: -------------------------------------------------------------------------------- 1 | # iOS网络编程笔记 2 | 3 | ## URLSession 4 | 5 | - 支持iOS App后台suspend时下载数据 6 | - 通过创建`URLSessionTask`来执行任务 7 | - 一个session可以创建多个task 8 | - 可以给session设置delegate,用于监听网络请求状态 9 | - 也可以为task提供completion block,这种情况下,delegate方法就不会执行了 10 | - task刚创建完处于suspend状态,可以cancel、restart、resume task 11 | - 尽可能复用session,避免重建没必要的session 12 | - 注意,session的delegate方法,是执行在一个串行的operationQueue中,所以task的结果是串行回调 13 | 14 | > session对delegate是强引用,如果session不进行invalidate,会引起泄漏 15 | 16 | ### session类型 17 | - shared,单例,不能设置delegate和configuration对象 18 | - 也可以根据需要自己创建`URLSession`,根据configuration不同,有三种类型的session 19 | - default session,使用了`URLSessionConfiguration.default`,和shared类似,但可以设置configuration 20 | - Ephemeral session,使用了`URLSessionConfiguration.ephemeral`,不能往磁盘中写入cache、cookie和credentials证书信息 21 | - Background session,使用`URLSessionConfiguration.background`,可以在suspend情况下上传、下载数据 22 | 23 | ### 协议的支持 24 | - http/https,http/1.1、http/2 25 | - ftp、file、data 26 | - 也可以自己添加协议 27 | 28 | ### NSCopying的支持 29 | - copy `URLSession`和`URLSessionTask`时,返回自己 30 | - copy `URLSessionConfiguration`时,创建一个新的对象返回 31 | 32 | ### Shared Session 33 | - 不能设置deegate和configuration 34 | - 不能进行后台下载 35 | - 不适用需要重新实现cache、cookie的情况 36 | 37 | ## URLSessionConfiguration 38 | 39 | - 可以控制timeout、cache、cookie、对host的最大可建立的连接数等 40 | - 还可以控制移动数据下是否建立连接等 41 | - 一个URLSession创建的所有task共享一个`URLSessionConfiguration` 42 | - URLSession在使用configuration时,会先将其copy一份。所以初始化URLSession结束后,再修改configuration就没用了 43 | - `NSURLRequest`的配置可以重写configuration中的配置,但如果configuration中的要求更严苛,那也会尊重。比如如果configuration中要求不能使用移动数据访问网络,那NSURLRequest就不能访问 44 | 45 | ## URLSessionTask 46 | 47 | 1. 通过`URLSession`创建task 48 | 1. task可以下载数据、下载文件、上传数据 49 | 1. 几个个类型的task 50 | - `URLSessionDataTask`,与服务器交互数据的数据结构是NSData,适用于频繁、短暂的请求 51 | - `URLSessionUploadTask`,支持后台上传 52 | - `URLSessionDownloadTask`,接收文件格式数据,支持后台上传、下载数据 53 | - `URLSessionStreamTask`,用于tcp通信 54 | 55 | ### State 56 | 57 | - `URLSessionDataTask`等Task类都是继承自`URLSessionTask` 58 | - 有几个API 59 | - resume 60 | - suspend,挂起后相当于暂停,还可以resume 61 | - cancel 62 | 63 | ## Handling an Authentication Challenge 64 | 65 | ## Socket 66 | 67 | - socket翻译为套接字,是支持TCP/IP协议的网络通信的基本操作单元。 68 | - 它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。 69 | - socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。 70 | - 它不属于OSI七层协议,它只是对于TCP,UDP协议的一套封装,让我们开发人员更加容易编写基于TCP、UDP的应用。 71 | 72 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/Socket_architecture.png?raw=true) 73 | 74 | ### 带着问题学习 75 | 1. iOS中长链接的实现方式有哪些,特点和使用场景是什么 76 | 2. WebSocket与Socket区别是什么 77 | 78 | ### 初识Socket 79 | 80 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/Socket_communication_process.png?raw=true) 81 | 82 | - iOS框架层面没有对socket的支持;但C的代码库中有socket的相关API,可以用之 83 | - 根据 [iOS 用原生代码写一个简单的socket连接](https://juejin.cn/post/6844903940459331598)的demo可以完整的体验一把简单地socket长链接 84 | 85 | 需要注意的是 86 | 87 | - 必须需要一端首先出于accept状态后,另一端才能建立连接和发送数据 88 | 89 | ### 长链接方案 90 | 91 | 92 | ### 参考 93 | - [iOS 用原生代码写一个简单的socket连接](https://juejin.cn/post/6844903940459331598) 94 | - [聊聊iOS中网络编程长连接的那些事](https://zhuanlan.zhihu.com/p/34944894) 95 | 96 | ## 遗留问题 97 | 1. 如何处理重定向问题 98 | 99 | 100 | ## 参考 101 | - [URL Loading System](https://developer.apple.com/documentation/foundation/url_loading_system) 102 | - [URLSession](https://developer.apple.com/documentation/foundation/urlsession) 103 | - [URLSessionConfiguration](https://developer.apple.com/documentation/foundation/urlsessionconfiguration) 104 | - [Fetching Website Data into Memory](https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory) 105 | -------------------------------------------------------------------------------- /Objective-C笔记.md: -------------------------------------------------------------------------------- 1 | # Objective-C笔记 2 | 3 | ### Category and Extension 4 | 5 | - category中只能添加方法 6 | - 方法名不能与原类中冲突,否则运行时可能会出现覆盖的问题 7 | - extensnion中既可以添加方法也可以添加方法 8 | - extension必须要在有原类源码的情况下才能声明,因为声明的方法要在类的`implementation`中实现 9 | 10 | ### self vs super 11 | ``` 12 | @implementation Son : Father 13 | - (id)init 14 | { 15 | self = [super init]; 16 | if (self) 17 | { 18 | NSLog(@"%@", NSStringFromClass([self class])); 19 | NSLog(@"%@", NSStringFromClass([super class])); 20 | } 21 | return self; 22 | } 23 | @end 24 | 25 | Son *son = [[Son alloc] init]; 26 | 27 | // 打印结果 28 | // son 29 | // son 30 | ``` 31 | 32 | - 简言之,区别于self,super只是一个编译器标识符(Magic Keyword),对super执行方法时,会找到self的父类的方法列表中对应的方法,并通过objc_msgSend的方式执行 33 | - super指向的内容仍然是self,所以在父类中找到方法后,objc_msgSend执行时传的第一个参数仍是self 34 | - [iOS-Self & Super(Runtime)](https://www.jianshu.com/p/71bbcb99ddbc) 35 | - [Cocoa Fundamentals Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW3) 36 | 37 | ### sleep vs usleep 38 | 39 | usleep的参数时u秒 40 | 41 | ### 宏定义的使用 42 | 43 | - 宏定义并不是OC的特性,而是C语言的语法 44 | - 宏定义有对象宏 和 函数宏 45 | - `#define PI 3.1415926`是对象宏 46 | - `#define MIN(x, y) (x < y ? x : y)`是函数宏(注意这个MIN的写法并不完备,完备写法请看参考文章) 47 | 48 | 49 | #### 常用的预定义宏 50 | 51 | 不同编译器预定义了一些内置的宏,方便开发者拿来使用 52 | 53 | #### 那些之前没见过的宏 54 | 55 | - PATH_MAX 56 | 57 | ### 常见的宏中的运算符 58 | 59 | - \##:`#define JOINT(A, B) A##B`, 将A和B参数的值拼接到一起 60 | - \#:`#define String(A) #A`,将A的变量名(并非A的值)转为字符串 61 | 62 | ### 函数宏中的可变参数 63 | 64 | ``` 65 | //1 66 | #define Log(format, ...) NSLog(format, ##__VA_ARGS__) 67 | //2 68 | #define Log(format, ...) NSLog(format, __VA_ARGS__) 69 | ``` 70 | - 三个点用在宏定义的声明部分,__VA_ARGS__用在实现部分 71 | - 既然是参数个数可变,那么也可以是0个参数,即没有参数 72 | - 此处之所以写`##`,是一个特例,因为可以可变参数可以为0个,那么对于2的写法,就会多出一个逗号,编译是不通过的。如果加了`##`,编译器会在可变参数为0时,将其前面的逗号吃掉 73 | - `...`和`__VA_ARGS__`配对使用,与这个配对有相同作用的是`NAME...`和`NAME`配对使用,这里的NAME可以是任意内容 74 | 75 | ### 如何打印百分号% 76 | 77 | ``` 78 | NSLog(@"%"); //%无法打印出来 79 | NSLog(@"%%"); //正确姿势,且适用于`print`和Swift的String方法 80 | ``` 81 | 82 | #### 参考 83 | - [宏定义的黑魔法 - 宏菜鸟起飞手册](https://onevcat.com/2014/01/black-magic-in-macro/) 84 | - [builtin-macros in clang](http://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros) 85 | - [predefine macros in gcc](https://gcc.gnu.org/onlinedocs/cpp/Predefined-Macros.html#Predefined-Macros) 86 | - [宏定义中\# 和 \##的区别](https://stackoverflow.com/questions/4364971/and-in-macros) 87 | 88 | ### Attribute 89 | - UNAVAILABLE_ATTRIBUTE 90 | - NS_DESIGNATED_INITIALIZER 91 | 92 | ### Protocol 93 | 94 | ``` 95 | @protocol TOCProtocolA 96 | + (void)doClassMethod; 97 | - (void)doSth; 98 | @end 99 | 100 | // 写代码表示一个对象,一个类,一个遵循了某协议的对象,一个协议本身,一个遵循某协议的类 101 | UIViewController *obj1 = [UIViewController new]; 102 | id obj2 = [UIViewController new]; 103 | 104 | Class class1 = [UIViewController class]; 105 | Class class2 = [obj1 class]; 106 | 107 | id protocolObject = [UITableViewController new]; 108 | 109 | Protocol *p = @protocol(TOCProtocolA); 110 | 111 | Class protocolClass = [TOFoundationViewController class]; 112 | [protocolClass doClassMethod]; 113 | ``` 114 | 115 | 116 | ## 参考 117 | - [Programming with Objective-C](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011210) -------------------------------------------------------------------------------- /Opensource/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Opensource/.DS_Store -------------------------------------------------------------------------------- /Opensource/DragControl/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Opensource/DragControl/.DS_Store -------------------------------------------------------------------------------- /Opensource/DragControl/drag_applemap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Opensource/DragControl/drag_applemap.gif -------------------------------------------------------------------------------- /Opensource/DragControl/iOS拖拽组件实现.md: -------------------------------------------------------------------------------- 1 | # iOS拖拽组件实现 2 | 3 | > 之前实现的一个拖拽组件,本文还原一下当时实现的细节,后面会分享源代码 4 | 5 | 本次实现要达到两个目的: 6 | 7 | 1. 完全还原苹果地图中类似组件的用户交互体验效果 8 | 2. -------------------------------------------------------------------------------- /Opensource/EditMenu/EditMenuInteractionDummy.h: -------------------------------------------------------------------------------- 1 | // 2 | // EditMenuInteractionDummy.h 3 | // 4 | // Created by songgeb on 2023/9/27. 5 | // Copyright © 2023 songgeb. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | /// 该类用于配合EditMenuInteraction一同实现EditMenu效果 12 | @interface EditMenuInteractionDummy : UIView 13 | 14 | + (instancetype)dummyWithActionCallback:(void (^)(SEL))callback; 15 | - (void)updateActions:(NSSet *)actions; 16 | 17 | - (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; 18 | - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; 19 | - (instancetype)init NS_UNAVAILABLE; 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Opensource/EditMenu/EditMenuInteractionDummy.m: -------------------------------------------------------------------------------- 1 | // 2 | // EditMenuInteractionDummy.m 3 | // 4 | // Created by songgeb on 2023/9/27. 5 | // Copyright © 2023 songgeb. All rights reserved. 6 | // 7 | 8 | #import "EditMenuInteractionDummy.h" 9 | 10 | @interface EditMenuInteractionDummy () 11 | @property(nonatomic, copy) NSSet *actions; 12 | @property(nonatomic, strong) void (^callback)(SEL); 13 | @end 14 | 15 | @implementation EditMenuInteractionDummy 16 | 17 | + (instancetype)dummyWithActionCallback:(void (^)(SEL))callback { 18 | NSParameterAssert(callback); 19 | return [[EditMenuInteractionDummy alloc] initWithCallback:callback]; 20 | } 21 | 22 | - (instancetype)initWithCallback:(void (^)(SEL))callback { 23 | self = [super initWithFrame:CGRectZero]; 24 | if (self) { 25 | _actions = [NSSet set]; 26 | _callback = callback; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)updateActions:(NSSet *)actions { 32 | if (!actions) return; 33 | self.actions = actions; 34 | } 35 | 36 | - (BOOL)isSelectorSupported:(SEL)selector { 37 | BOOL supported = NO; 38 | for (NSString *actionStr in self.actions) { 39 | SEL supportedAction = NSSelectorFromString(actionStr); 40 | if (supportedAction == selector) { 41 | supported = YES; 42 | break; 43 | } 44 | } 45 | return supported; 46 | } 47 | 48 | + (void)fake { 49 | } 50 | 51 | #pragma mark override 52 | - (BOOL)canBecomeFirstResponder { 53 | return YES; 54 | } 55 | 56 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { 57 | return [self isSelectorSupported:action]; 58 | } 59 | 60 | - (void)forwardInvocation:(NSInvocation *)anInvocation { 61 | if ([self isSelectorSupported:anInvocation.selector]) { 62 | self.callback(anInvocation.selector); 63 | } 64 | } 65 | 66 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { 67 | return [EditMenuInteractionDummy methodSignatureForSelector:@selector(fake)]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Opensource/Slide/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Opensource/Slide/.DS_Store -------------------------------------------------------------------------------- /Opensource/Slide/slider_pageenable.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Opensource/Slide/slider_pageenable.gif -------------------------------------------------------------------------------- /Opensource/Slide/slider_pageenable_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Opensource/Slide/slider_pageenable_1.gif -------------------------------------------------------------------------------- /Opensource/Slide/slider_pageoffset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Opensource/Slide/slider_pageoffset.png -------------------------------------------------------------------------------- /Opensource/Slide/slider_secretscrollview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Opensource/Slide/slider_secretscrollview.gif -------------------------------------------------------------------------------- /Pending/Daily Questions.md: -------------------------------------------------------------------------------- 1 | # Daily Questions 2 | 3 | ### Long term 4 | - 系统学一下View Programing Guide for iOS 5 | - 区分一下View和CALayer的区别 6 | - [Using Objective-C Runtime Features in Swift](https://developer.apple.com/documentation/swift/using_objective-c_runtime_features_in_swift) 7 | - [Pattern Matching](https://alisoftware.github.io/swift/pattern-matching/2016/03/27/pattern-matching-1/) 8 | 9 | ### 2022年09月21日 10 | - NSEnum与typedef nsenum {} A的区别 11 | - 离屏渲染 12 | 13 | ### 2022年09月23日 14 | - dispatch_barrier应用场景 15 | - UIButton 16 | - 为什么自定义btn显示不出来 17 | 18 | ### 2022年10月20日 19 | - 如何加载超大图片 20 | - pattern image 21 | 22 | ### 2022年10月21日 23 | 24 | ### 2022年11月18日 25 | - 设备中有哪些日志 -------------------------------------------------------------------------------- /Pending/General list in iOS.md: -------------------------------------------------------------------------------- 1 | # General list in iOS 2 | 3 | 4 | There are many knowledge that needs to be understood without deep learning. 5 | 6 | I called this General knowledge.(通识知识) This passage will list the general knowledge in iOS. 7 | 8 | - APNs 9 | - Dynamic in UIKit 10 | - Extension 11 | - switch in Swift 12 | - advanced pattern matching 13 | - Swift 4 -> Swift 5 14 | - iClound 15 | - supported data types 16 | - dictionary about http protocol -------------------------------------------------------------------------------- /Push in iOS.md: -------------------------------------------------------------------------------- 1 | # Push in iOS 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | 时间节点:写本文时(2023年09月17日),当前稳定版本是iOS 16,最新的iOS 17正式版本即将上线 6 | 7 | 本文重点内容是iOS中的Push(推送),本文尝试回顾Push的发展过程以及Framework、API的演进。期望通过综述这些发展过程,能帮助自己和其他开发者 8 | 9 | - 从宏观视角看待iOS中Push的能力范围 10 | - 了解不同API的使用方式,更容易上手工程 11 | 12 | 写本文时的一些疑惑: 13 | 14 | - Push分为本地推送和远端推送 15 | - Push自iOS ?开始就存在 16 | - iOS 10之前,推送部分的API属于UIKit? 17 | - 从iOS 10开始,Apple推出了一个新的Framework--User Notifications,来支持推送的能力 18 | 19 | 20 | ## API 21 | 22 | ### `application(_:didReceiveRemoteNotification:)` 23 | 24 | - 目前已经废弃,应用与iOS 3- iOS 10 25 | - 执行时机 26 | - 根据官方说明,当App在前台运行时,收到Remote Notification时调用该方法 27 | - 当App没有运行时,收到通知后,用户点击了通知,会在application:willFinishLaunchingWithOptions: o和application:didFinishLaunchingWithOptions:中携带通知内容启动App;同时该方法依然会执行 28 | - **但是**,经过测试,至少在iOS 15.7.3中,后台情况下发送Remote Notification,该方法依然会执行 29 | 30 | ### `application:didReceiveRemoteNotification:fetchCompletionHandler:` 31 | 32 | - iOS 7引入的API 33 | 34 | ## 疑问 35 | 1. Push从iOS几开始支持? 36 | 2. PushKit Framework是做什么的? 37 | 3. 什么是 notification center history、Scheduled Summary 38 | 4. App在前台时到底能不能收到通知,能收到什么样的通知,屏幕顶部的可以吗? 39 | - 在前台可以收到通知,并且可以通过自定义来显示顶部通知 40 | 5. 什么是actionable notification 41 | 6. 远程推送触发的application:willFinishLaunchingWithOptions:,是什么时候执行?推送到来时?还是用户点击时 42 | 43 | 44 | ## 参考 45 | - [Human Interface Guidelines-Managing notifications](https://developer.apple.com/design/human-interface-guidelines/managing-notifications) -------------------------------------------------------------------------------- /RAC学习/RAC学习笔记3.md: -------------------------------------------------------------------------------- 1 | # RAC学习笔记3 2 | > 深入理解代替单纯记忆 3 | 2021年01月14日 4 | 5 | 初学RAC之后,已经可以上手写一些简单的功能了。但在遇到高阶应用的时候还是因为对底层理解不够而无所适从,比如: 6 | 7 | 1. zip、concat的区别是什么? 8 | 2. 能否通过RAC实现多个异步任务的同步执行 9 | 3. RACDispose是什么,怎么用? 10 | 4. RAC为什么那么容易触发循环引用,如何轻松解决循环引用 11 | 12 | ## Stream 13 | 14 | 即一系列的内容 15 | 16 | - 这些内容是有先后顺序的,必须按照先后顺序,第一个处理完之前,不可能拿到第二个 17 | - 在RAC中`RACStream`表示Stream,它本身是一个抽象类 18 | - `RACSignal`和`RACSequence`是`RACStream`的子类 19 | 20 | ## RACSignal 21 | - RAC可以保证同一个信号的两个event,不可能并发传递到处理callback中 22 | - 即当一个event正在被处理时,另一个event会等待 23 | 24 | ## Subscription 25 | 26 | - Subscriptions retain their signals, and are automatically disposed of when the signal completes or errors. 27 | - Subscriptions can also be disposed of manually. 28 | 29 | ## RACSubject 30 | 31 | 可以手动控制的signal 32 | 33 | ## RACSequence 34 | 35 | 类似`NSArray`的集合类,但有更多特性 36 | 37 | - 默认情况,RACSequence中的item是lazy load的 38 | 39 | ``` 40 | NSArray *strings = @[ @"A", @"B", @"C" ]; 41 | RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) { 42 | NSLog(@"%@", str); 43 | return [str stringByAppendingString:@"_"]; 44 | }]; 45 | 46 | // Logs "A" during this call. 47 | NSString *concatA = sequence.head; 48 | ``` 49 | 50 | ## Cold and Hot Signal 51 | 52 | ReactiveCocoa官方这样说: 53 | > 54 | - hot: already activated by the time it's returned to the caller 55 | - cold: activated when subscribed to 56 | 57 | 但还是不理解 58 | 59 | 冷、热信号的概念源于.NET框架Reactive Extensions中的Hot Observable和Cold Observable 60 | > 61 | - Hot Observable是主动的,尽管你并没有订阅事件,但是它会时刻推送,就像鼠标移动;而Cold Observable是被动的,只有当你订阅的时候,它才会发布消息 62 | - Hot Observable可以有多个订阅者,是一对多,集合可以与订阅者共享信息;而Cold Observable只能一对一,当有不同的订阅者,消息是重新完整发送 63 | 64 | ### 为什么要区分冷、热信号 65 | 66 | 67 | ### 问题 68 | 1. 为什么要有冷热信号区分 69 | 70 | ## RACSignal VS RACSequence 71 | 72 | - RACSignal是`push-driven`,RACSequence是`pull-driven` 73 | - `push-driven`其实 74 | 75 | ## RACDispose 76 | 77 | ## side effects 78 | 79 | 1. 要了解RACCommand,就得知道multicast是干啥的 80 | 2. 进而要了解RACMulticastConnection是干啥的 81 | 82 | ``` 83 | // This signal starts a new request on each subscription. 84 | RACSignal *networkRequest = [RACSignal createSignal:^(id subscriber) { 85 | AFHTTPRequestOperation *operation = [client 86 | HTTPRequestOperationWithRequest:request 87 | success:^(AFHTTPRequestOperation *operation, id response) { 88 | [subscriber sendNext:response]; 89 | [subscriber sendCompleted]; 90 | } 91 | failure:^(AFHTTPRequestOperation *operation, NSError *error) { 92 | [subscriber sendError:error]; 93 | }]; 94 | 95 | [client enqueueHTTPRequestOperation:operation]; 96 | return [RACDisposable disposableWithBlock:^{ 97 | [operation cancel]; 98 | }]; 99 | }]; 100 | 101 | // Starts a single request, no matter how many subscriptions `connection.signal` 102 | // gets. This is equivalent to the -replay operator, or similar to 103 | // +startEagerlyWithScheduler:block:. 104 | RACMulticastConnection *connection = [networkRequest multicast:[RACReplaySubject subject]]; 105 | [connection connect]; 106 | 107 | [connection.signal subscribeNext:^(id response) { 108 | NSLog(@"subscriber one: %@", response); 109 | }]; 110 | 111 | [connection.signal subscribeNext:^(id response) { 112 | NSLog(@"subscriber two: %@", response); 113 | }]; 114 | ``` 115 | 116 | ## 疑问 117 | - RACStream的两个子类:RACSignal、RACSequence的区别是什么? 118 | - multicast的使用 119 | 120 | ## 参考 121 | - [Framework Overview](https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/Documentation/FrameworkOverview.md) 122 | - [DesignGuidelines](https://github.com/ReactiveCocoa/ReactiveObjC/blob/master/Documentation/DesignGuidelines.md) 123 | - [细说ReactiveCocoa的冷信号与热信号(三):怎么处理冷信号与热信号](https://tech.meituan.com/2015/11/03/talk-about-reactivecocoas-cold-signal-and-hot-signal-part-3.html) -------------------------------------------------------------------------------- /RAC学习/ReactiveCocoa使用技巧.md: -------------------------------------------------------------------------------- 1 | # ReactiveCocoa使用技巧 2 | 3 | 本文记录一些使用RAC过程中好用的技巧 4 | ## RACTuple 5 | 专业名叫`元组` 6 | 7 | - 就是一个可以存储多个不同类型数据的数据类型 8 | - 可以实现一个方法多个返回值 9 | 10 | ## RACSequence 11 | 12 | 一个可以替换`OC`中字典或数组的类型 13 | 14 | 可以据此遍历数组 15 | 16 | ``` 17 | NSArray * array = @[@"大吉大利",@"今晚吃鸡",@66666,@99999]; 18 | [array.rac_sequence.signal subscribeNext:^(id _Nullable x) { 19 | NSLog(@"%@",x); 20 | }]; 21 | ``` 22 | 23 | 字典也可以 24 | 25 | ``` 26 | NSDictionary * dict = @{@"大吉大利":@"今晚吃鸡", 27 | @"666666":@"999999", 28 | @"dddddd":@"aaaaaa" 29 | }; 30 | 31 | [dict.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) { 32 | NSLog(@"%@",x); 33 | }]; 34 | ``` 35 | 36 | 还有可以做映射的`map`方法 37 | 38 | ``` 39 | NSArray * persons = [[array.rac_sequence map:^id _Nullable(NSDictionary* value) { 40 | return [Person personWithDict:value]; 41 | }] array]; 42 | 43 | NSLog(@"%@",persons); 44 | ``` 45 | 46 | 也有类似`firstObjectWhere:`的方法 47 | 48 | ``` 49 | UIView *emptyDataSetView = [self.tableView.subviews.rac_sequence objectPassingTest:^(UIView *view) { 50 | return [NSStringFromClass(view.class) isEqualToString:@"DZNEmptyDataSetView"]; 51 | }]; 52 | ``` 53 | 54 | ## 参考 55 | 56 | - [iOS RAC - 集合RACTuple、RACSequence](https://www.jianshu.com/p/a57060bf6158) -------------------------------------------------------------------------------- /RAC学习/ReactiveCocoa笔记-实践篇.md: -------------------------------------------------------------------------------- 1 | # ReactiveCocoa笔记-实践篇 2 | 3 | ## RACSubject 4 | 由于`RACSubject`遵循了`RACSubscriber`协议且是`RACSignal`的子类,所以既可以被订阅,也可以主动发送消息 5 | 6 | ### 自定义代理 7 | 8 | 可以代替iOS原生的代理模式 9 | 10 | 比如有页面A和B,A可以跳转到B,B处理完逻辑后,将内容通过代理回传给A 11 | 12 | iOS原生代理实现可以是, 13 | 14 | 1. B中声明一个Delegate Protocol,让A实现该协议 15 | 2. B中持有一个对Delegate Protocol的弱引用变量`delegate` 16 | 3. 跳转到B时,将`delegate`赋值为A实例 17 | 4. B中处理完逻辑后,执行代理方法 18 | 19 | 改用RAC实现则可以这样实现 20 | 21 | 1. B中定义一个`RACSubject`类型的信号变量`delegateSignal` 22 | 2. 跳转到B时,初始化B的同时,也初始化并赋值`delegateSignal` 23 | 3. 同时订阅`delegateSignal`信号 24 | 4. B中处理完逻辑后直接给`delegateSignal`发送消息`[delegateSignal sendNext: obj]` 25 | 26 | > RAC的实现感觉和为B定义一个block没啥区别 27 | 28 | 29 | ## RACCommand 30 | ### 猜喜页RACCommand使用实践 31 | 32 | 介绍一下上下文 33 | 34 | - `MMCGuessLikeViewModel`表示猜喜页需要展示相关的数据信息 35 | - `recommendCommand`,该command的核心任务是创建一个搜索推荐内容的请求的信号`requestSignal`,并订阅该信号,同时请求结束后,通过`subscriber`继续发送事件 36 | - 外部vc中,通过`viewModel.recommandCommand`获取到该`RACCommand`,并订阅该command的`executionSignals`进行收到数据后的处理工作 37 | - 然后在合适的时机执行`[recommendCommand execute]`即可 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | 3 | iOS好,iOS妙,iOS呱呱叫 4 | -------------------------------------------------------------------------------- /Runtime笔记/OC Runtime笔记.md: -------------------------------------------------------------------------------- 1 | # OC Runtime 笔记 2 | 3 | ## objc_msgSend 4 | 5 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/runtime_messaging1.gif?raw=true) 6 | 7 | - 类这个数据结构中有一个`Dispatch Table`,存放着当前类的实例方法的selector和方法实现地址信息 8 | - 当通过`[object message]`形式发送消息时 9 | - `objc_msgSend`方法根据`receiver`和`selector`,根据下面的路线寻找相应`selector`的实现 10 | - 若当前类中找不到,则通过`superclass`去父类中找,一直找下去 11 | - 这个过程在面向对象中也叫做动态绑定`dynamic binding` 12 | - 每个类中对`selector`做了缓存,缓存不仅包括本类中使用过的方法,也有从继承下来的方法。先访问缓存,速度更快 13 | 14 | - 编译结束后,编译器其实会将`receiving object`和`selector`作为两个隐藏参数写入到方法实现中 15 | - 在方法中可以`_cmd`来获得`selector`,用`self`获取`receiving object` 16 | 17 | ## Dynamic Method Resolution 18 | 19 | ### Dynamic Method Resolution 20 | - 开发者可以动态的添加类或实例方法 21 | - `resolveInstanceMethod`或`resolveClassMethod`这两个方法会当在系统在类层级中找不到对应方法时执行 22 | - 开发者可以选择在这个时机使用runtime的一些方法比如`class_addMethod`给类添加方法 23 | 24 | ### class、objc\_getClass、object\_getClass方法 25 | 26 | ``` 27 | // Returns the class definition of a specified class. 28 | id objc_getClass(const char *name); 29 | ``` 30 | 31 | ``` 32 | // Returns the class of an object. 33 | Class object_getClass(id obj); 34 | ``` 35 | 36 | ``` 37 | // Returns the class object. 38 | + (Class)class; // in NSObject Class 39 | 40 | // Returns the class object for the receiver’s class. 41 | - (Class)class; // in NSObject Protocol 42 | ``` 43 | 44 | 我们来看下打印结果 45 | 46 | ``` 47 | [instance class] is 0x10fa70b30 48 | [Class class] is 0x10fa70b30 49 | object_getclass(instance) is 0x10fa70b30 50 | object_getclass(class) is 0x10fa70b08 51 | objc_getMetaClass(Class) is -> 0x10fa70b08 52 | ``` 53 | 54 | - 从规律上来说这三个方法的作用不是很容易记忆 55 | - 然后从每个方法官方的注释和方法签名上来理解一下可能更容易记忆 56 | - 需要一些前置知识 57 | - 区分这三个概念:实例、类对象、元类 58 | - `Class`类型的定义是`typedef struct objc_class *Class;` 59 | - 其实对于类对象、元类,都是`Class`类型 60 | - 实例的定义是`struct objc_object { 61 | Class _Nonnull isa OBJC_ISA_AVAILABILITY; 62 | };` 63 | - 类对象和元类都是单例 64 | 65 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/instance_class_metaclass.png?raw=true) 66 | 67 | ### ivar的值存在哪里 68 | 69 | 之所以有这个疑问是因为,从OC实例、类对象、元类的C结构体来看,搞不明白一个实例的成员变量的值在哪里,因为在类对象中只是存储了ivar的name和type 70 | 71 | - 其实ivar成员变量并没有存在`objc_class`结构体中 72 | - 而是对于每个实例,成员变量存储位置实际上紧跟着这个实例的内存地址来存储 73 | 74 | ### Getting a Method Address 75 | 76 | 前面我们知道一个方法的执行,要通过一些查找过程才能最终确定方法实现 77 | 78 | runtime也提供了直接获取方法实现地址的方法 79 | 80 | ``` 81 | - (IMP)methodForSelector:(SEL)aSelector; 82 | ``` 83 | 84 | 使用该方法和OC的发送消息的不同在于,发送完消息runtime要经过一系列查找,而该方法则不用 85 | 86 | ## Messaging Forwarding 87 | 88 | 消息转发,是当给某个对象发送消息时,本身该对象并没有处理该消息的方法时,默认情况下会走到`NSObject`的`doesNotRecognizeSelector`方法,抛出异常 89 | 90 | 但运行时提供了一些机会,让开发者可以修改消息内容,或者修改消息的receiver,即将消息转发给其他对象 91 | 92 | > 这部分详细流程在《Effective in Objective-C》笔记中有提到 93 | 94 | ### Forwarding and Multiple Inheritance 95 | 96 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/forwarding_multipleinheritance.gif?raw=true) 97 | 98 | - 给`Warrior`对象发送了`negotiate`消息,但并没有实现 99 | - `Warrior`将消息转发给了`Diplomat`对象来处理 100 | - 看上去好像`Warrior`能处理`negotiate`消息,好像`Warrior`对象继承了`Diplomat`的方法 101 | - 这就是所谓的多继承 102 | 103 | ## Method Swizzling 104 | 105 | 看到一篇分析Method Swizzling的风险点以及更优写法的文章 106 | 107 | - [https://stackoverflow.com/questions/5339276/what-are-the-dangers-of-method-swizzling-in-objective-c](https://stackoverflow.com/a/8636521/5792820) 108 | 109 | ## Type Encodings 110 | 111 | - 未完待续 112 | 113 | # 参考 114 | - [Objective-C Runtime Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048-CH1-SW1) -------------------------------------------------------------------------------- /Runtime笔记/iOS思考之self vs super.md: -------------------------------------------------------------------------------- 1 | # iOS思考之self vs super 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | 有一个题目大致如此: 6 | 7 | 有一个类叫`TOSon`,它的父类是`TOFather`,`TOFather`的父类是`NSObject`。看如下代码分析打印结果 8 | 9 | ``` 10 | @implementation TOSon 11 | - (instancetype)init { 12 | if (self = [super init]) { 13 | NSLog(@"self.class is %@", [self class]); 14 | NSLog(@"self.superclass is %@", [self superclass]); 15 | NSLog(@"super.class is %@", [super class]); 16 | NSLog(@"super.superclass is %@", [super superclass]); 17 | } 18 | return self; 19 | } 20 | @end 21 | ``` 22 | 23 | ## 题目分析 24 | 25 | 说白了,关键想要考察的点大致有两个: 26 | 27 | 1. 什么是super,super和self的区别是什么 28 | 2. Objective C的消息派发机制 29 | 30 | ## 解题过程 31 | 32 | 首先看一下官方关于self、super的定义是怎么说的([The Objective-C Programming Language-Defining a Class](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-SW1)) 33 | 34 | 提取一下核心的内容: 35 | 36 | - self应用于实例方法或类方法的调用(实质是消息发送) 37 | - self是一个变量,表示当前实例对象或类对象,在进行方法调用时作为隐藏参数传入底层的runtime的方法查找和执行实际的方法 38 | - super是一个给编译器使用的标记,并非变量 39 | - 所以我们无法打印super的内容 40 | - super也可以用于实例方法或类方法的调用 41 | - super与self的最大不同在于,super进行方法调用时寻找目的函数的过程不同于self 42 | - self寻找目的函数时当然就是从self所指的对象进行寻找 43 | - super则是直接在super所在位置的父类所表示的对象开始寻找 44 | - super和self表示同一个receiver(英文原文叫做 self and super both refer to the receiving object) 45 | - 这一点其实我一开始是不太理解的,后面逐渐有了些了解 46 | 47 | 接下来看一下题目 48 | 49 | ### `[self class]` 50 | 51 | - self表示当前`TOSon`类型的实例对象,我们试图打印`class`方法(消息)的返回值 52 | - 通过`class`方法的注释我们可以了解到--`Returns the class object for the receiver’s class.` 53 | - 通过打印结果我们也能看出---TSon 54 | 55 | ### `[self superClass]` 56 | 57 | - superClass是`NSObject`协议的一个只读属性--`@property(readonly) Class superclass;` 58 | - 注释文档中对该属性的描述是--`Returns the class object for the receiver’s superclass.` 59 | - 所以,结果应该就是--TOFather 60 | 61 | ### `[super class]` 62 | 63 | > 该问题经常出现在面试题中,面试官很多时候其实就是想考这部分 64 | 65 | - 根据官方对于super的解释,我们知道该方法会从TOFather开始寻找class方法 66 | - 通常情况下我们的自定义类都没有实现class方法,所以最终寻找class方法会一直找到`NSObject`协议中对该方法的实现 67 | - 再来回顾一下`class`方法的注释--`Returns the class object for the receiver’s class.` 68 | - 那对于`[super class]`来说,super就是class方法的receiver,super表示的receiver和self表示的是同一个 69 | - 所以在该题目下[self class]和[super class]的返回值是一样的,都是TOSon 70 | 71 | ### `[super superclass]` 72 | 73 | 和`[super class]`同样的分析套路 74 | 75 | - 首先,从TOFather类对象中开始寻找superclass实现,没找到,一直找到`NSObject`协议中 76 | - `Returns the class object for the receiver’s superclass.` 77 | - receiver和self一样,所以结果为TOFather 78 | 79 | ## 总结 80 | 81 | - 该题最核心要考察的是对super和self的概念、使用、区别的理解是否深入 82 | - 在我几年前刚学习iOS时,没有好好看The Objective-C Programming Language,反而去看了些质量不太高的博文,其实这里面才是宝藏 83 | - 其实完全没必要非得从runtime源码层面分析super的实现或者class方法的底层实现,源码可能会随时间变化,基本思想却不会轻易改变 84 | 85 | ## 参考 86 | - [The Objective-C Programming Language-Defining a Class](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-SW1) 87 | -------------------------------------------------------------------------------- /Swift笔记/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/Swift笔记/.DS_Store -------------------------------------------------------------------------------- /Swift笔记/Compatibility in Swift.md: -------------------------------------------------------------------------------- 1 | # Compatibility in Swift 2 | 3 | 作为一个对Swift历史不太了解的开发者,如何才能清晰的理解Swift中compatibility问题,比如 4 | 5 | - 什么是ABI 稳定 6 | - Swift编译器与Clang区别是啥 7 | - 啥是Swift编译器兼容模式 8 | 9 | 粗看了几篇文章,不甚理解,正好一次面试中被问到了,于是想记录一下 10 | 11 | ## 编译器(compiler) 12 | 13 | - 编译iOS程序的编译器是llvm 14 | - llvm是一个编译器和编译过程的项目,llvm不仅支持OC和Swift 15 | - 当然,编译不同的语言,需要用到不同的编译前端,编译前端是llvm的组成部分 16 | - OC对应的编译前端程序是clang 17 | - Swift对应的编译前端程序也叫Swift 18 | - 编译器或者说前面提到的编译器前端的作用是什么 19 | - 预处理代码,比如将macro, import, include展开 20 | - 校验我们写的程序是否存在语法错误,并进行相应的错误,警告提醒 21 | - Swift编译器(前端)版本是跟随Xcode的版本在更新的,且一个Xcode中只有一个Swift编译器 22 | 23 | ## Swift编译器兼容模式(compatibility mode) 24 | 25 | Swift 4之前,Swift编译器(前端)只能编译对应版本的Swift语言开发的程序,简言之,就是Swift 3的编译器只能用来编译Swift 3开发的程序,如果是Swift 2开发的程序,是无法用Swift 3编译器来编译的,原因在于Swift 3的编译器在进行语法检查时是根据Swift 3语法标准执行的,Swift 3与Swift 2之间语法不兼容 26 | 27 | 自Swift 4和Swift 4编译器开始,引入了兼容模式 28 | 29 | - 当选择使用兼容模式时,Swift编译器可以支持更早版本的Swift语法 30 | - 比如Swift 4的编译器仍然可以编译Swift 3.2的程序 31 | - 当然即使在兼容模式下,Swift 4的编译器仍然支持Swift 4的语法特性 32 | - 这一选项就是通过Xcode->build setting->Swift Language Version控制的 33 | 34 | ## ABI稳定(ABI Stability) 35 | 36 | ABI是Application Binary Interface的缩写,深入了解它确实需要汇编、操作系统等深入的知识储备。本文仅做简单描述 37 | 38 | ABI顾名思义,应用程序二进制接口。可以和API对比着来理解,API是一个程序或SDK提供的一些函数、方法,ABI也是类似作用,只不过是更底层的函数,方法,协议或约定 39 | 40 | ABI描述更底层的协议约定,比如我们程序中的对象如何在内存中申请空间,运行时系统包含哪些方法等,下面列出ABI包含的内容: 41 | 42 | - Data Layout 43 | - Type Metadata 44 | - Mangling 45 | - Calling Convention 46 | - Runtime 47 | - Standard Library 48 | 49 | 一个新的语言,除了要有基本的语法,还要有支持它运行的编译器和运行时等条件 50 | 51 | 所以,Swift 5之前,Swift的ABI一直不稳定,其中内容在不同的版本差异是比较大的 52 | 53 | - 那时,一个用Swift开发的程序,打包后是需要将ABI内容打包进这个App包中的 54 | - 也就是说,可能一个iPhone上,有多个App,每个App对应的Swift的ABI是不同的 55 | 56 | 自Swift 5开始,ABI则稳定了,由此带来的好处是 57 | 58 | - 就是说只要是Swift 5或之后版本的编译器编译打包出来的App,都是ABI稳定的 59 | - 所以也无需再将ABI程序、内容打包到App中,可以直接集成到iPhone的操作系统iOS中了,App的包体积会有明显的减少 60 | - 因为所有App都将使用同一个Swift运行时、standard library,所以内存占用和启动耗时上也会有优化 61 | - 官方给了一个例子,一个Swift 5编译器打包的App可以运行在Swift 5的运行时下,也可以运行在Swift 6的运行时环境下 62 | 63 | ## 参考 64 | - [What is the "Swift Language Version" Xcode setting for? Because it still builds newer Swift code with an older version set](https://stackoverflow.com/questions/60177016/what-is-the-swift-language-version-xcode-setting-for-because-it-still-builds) 65 | - [Swift 4.0 Released!](https://www.swift.org/blog/swift-4.0-released/) 66 | - [Swift ABI 稳定对我们到底意味着什么](https://onevcat.com/2019/02/swift-abi/) 67 | - [ABI Stability and More](https://www.swift.org/blog/abi-stability-and-more/) -------------------------------------------------------------------------------- /Swift笔记/Exclusive Access to Memory in Swift.md: -------------------------------------------------------------------------------- 1 | # Exclusive Access to Memory in Swift 2 | 3 | ## Accesses Overlap 4 | 5 | 6 | 7 | ## 参考 8 | - [Enforce Exclusive Access to Memory](https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md) -------------------------------------------------------------------------------- /Swift笔记/JSON与Model互转 in Swift.md: -------------------------------------------------------------------------------- 1 | # JSON与Model互转 in Swift 2 | 3 | - Codable, ObjectMapper, CodableWrapper 4 | 5 | ## Codable存在什么问题 6 | 7 | 根据日常开发经验,我们看一下Swift原生的Codable使用起来会有什么不方便的地方 8 | 9 | ### 无法设置默认值 10 | 11 | ``` 12 | struct User: Codable { 13 | var name: String 14 | var age: Int 15 | } 16 | 17 | let json = #"{"name": "Tom"}"# 18 | let user = try JSONDecoder().decode(User.self, from: Data(json.utf8)) 19 | ``` 20 | 21 | 以上代码会报错,原因是JSON中没有age,所以decode时直接报错缺少该字段。那有没有办法解决呢? 22 | 23 | 当然是可以的,即自己重新实现`init`方法 24 | 25 | 但这样并不精简,为了一个字段默认值,我还得写一堆胶水代码。能不能直接这样呢? 26 | 27 | ``` 28 | struct User: Codable { 29 | var name: String 30 | var age: Int = 0 31 | } 32 | ``` 33 | 34 | 显然,不行 35 | 36 | ### -------------------------------------------------------------------------------- /Swift笔记/Key-Path in Swift.md: -------------------------------------------------------------------------------- 1 | # Key-Path expression in Swift 2 | 3 | A key-path expression refers to a property or subscript of a type. 4 | 5 | We can use it to get or change the property or data at subscript. 6 | 7 | It is like the key or keypath in KVO, but more powerful. 8 | 9 | The basic format is: 10 | 11 | `\type name.path` 12 | 13 | - The type name is the name of a concrete type, including any generic parameters, such as String, [Int], or Set. 14 | - The path consists of property names, subscripts, optional-chaining expressions, and forced unwrapping expressions. 15 | - At compile time, a key-path expression is replaced by an instance of the [KeyPath](https://developer.apple.com/documentation/swift/keypath) class. 16 | 17 | ``` 18 | struct SomeStructure { 19 | var someValue: Int 20 | } 21 | 22 | let s = SomeStructure(someValue: 12) 23 | let pathToProperty = \SomeStructure.someValue 24 | 25 | let value = s[keyPath: pathToProperty] 26 | // value is 12 27 | ``` 28 | 29 | ### Omit type name 30 | 31 | The type name can be omitted in contexts where type inference can determine the implied type. 32 | 33 | ``` 34 | class SomeClass: NSObject { 35 | @objc dynamic var someProperty: Int 36 | init(someProperty: Int) { 37 | self.someProperty = someProperty 38 | } 39 | } 40 | 41 | let c = SomeClass(someProperty: 10) 42 | c.observe(\.someProperty) { object, change in 43 | // ... 44 | } 45 | ``` 46 | 47 | ### Use as subscript 48 | 49 | ``` 50 | let greetings = ["hello", "hola", "bonjour", "안녕"] 51 | let myGreeting = greetings[keyPath: \[String].[1]] 52 | // myGreeting is 'hola” 53 | ``` 54 | 55 | > can also use `\[String][1]` 56 | 57 | ### Use optional chaining and force unwrapping 58 | 59 | ``` 60 | let count = greetings[keyPath: \[String].first?.count] 61 | print(count as Any) 62 | // Prints "Optional(5) 63 | ``` 64 | 65 | > force unwrapping in keypath can also cause runtime error 66 | 67 | ### Identify Key Path 68 | 69 | `\.self` or `\Type name.self` refers to a whole instance. 70 | 71 | ``` 72 | var compoundValue = (a: 1, b: 2) 73 | // Equivalent to compoundValue = (a: 10, b: 20) 74 | compoundValue[keyPath: \.self] = (a: 10, b: 20) 75 | ``` 76 | 77 | ### Use like function or closure 78 | 79 | > Swift 5.2 required 80 | 81 | You can use a key path expression in contexts where you would normally provide a function or closure. Specifically, you can use a key path expression whose root type is SomeType and whose path produces a value of type Value, instead of a function or closure of type (SomeType) -> Value. 82 | 83 | ``` 84 | struct Task { 85 | var description: String 86 | var completed: Bool 87 | } 88 | var toDoList = [ 89 | Task(description: "Practice ping-pong.", completed: false), 90 | Task(description: "Buy a pirate costume.", completed: true), 91 | Task(description: "Visit Boston in the Fall.", completed: false), 92 | ] 93 | 94 | // Both approaches below are equivalent. 95 | let descriptions = toDoList.filter(\.completed).map(\.description) 96 | let descriptions2 = toDoList.filter { $0.completed }.map { $0.description } 97 | ``` 98 | 99 | ## Reference 100 | - [What is a KeyPath in Swift](https://sarunw.com/posts/what-is-keypath-in-swift/) 101 | -------------------------------------------------------------------------------- /Swift笔记/Protocol in Swift.md: -------------------------------------------------------------------------------- 1 | # Protocol in Swift 2 | 3 | > 本文编写于2024年04月04日,此时Swift最新版本为Swift 5.10 4 | 5 | 用简洁的语言来描述`Protocol`就是: 6 | 7 | - `Protocol`是一组`Property`、`Method/Function`的要求,中文翻译为协议 8 | - Swift中的`Class`、`Enum`、`Struct`类型可以实现`Protocol`,这些类型必须满足`Protocol`中的要求 9 | - `Protocol`中可能会有associated type、where等高级的语法特性,但归根到底还是反映到`Property`和`Fuction`的要求上 10 | 11 | ## 本文要解决的问题 12 | - 系统学一下Swift中的Protocol 13 | - 对比OC中的Protocol,都多了哪些能力 14 | - 这些高级的Protocol技巧,在实践中的使用是怎样?目前只有概念但完全没有实践经验 15 | - 什么是existential type? 16 | - 什么是primary associatedtype? 17 | - primary associatedtype与generic parameters of a concrete type的区别 18 | 19 | 20 | ## 基础语法 21 | 22 | ### Property Requirements 23 | 24 | ``` 25 | protocol AProtocol { 26 | var instanceProperty: String { get set } 27 | var readonlyInstanceProperty: String { get } 28 | static var aTypeProperty: Int { get set } 29 | static var aReadonlyTypeProperty: Int { get } 30 | } 31 | ``` 32 | 33 | > 注意,`static`标注的属性,`Class`和其他类型在实现该协议时都可以支持该属性。但如果在`Protocol`中,用`class`标注属性时,则只有`Classs`能支持,其他类型不支持。所以Swift编译器不允许在`Protocol`中使用`class` 34 | 35 | ### Method Requirements 36 | 37 | 38 | 39 | ## 高级用法 40 | 41 | ## 参考 42 | - [Protocols-The Swift Programming Language (5.10) 43 | ](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/protocols#Adding-Constraints-to-Protocol-Extensions) 44 | - -------------------------------------------------------------------------------- /Swift笔记/Some and Any in Swift.md: -------------------------------------------------------------------------------- 1 | # some and any in Swift 2 | 3 | `some` keyword in Swift 5.1 4 | 5 | `Opaque Type`、`Opaque Parameter`、`Boxed Protocol Types`、`Existential Type` 6 | 7 | - some会preserve the identity of the underlying type,但为什么不能像泛型那样复用,怎么理解文章中所说的匿名 8 | 9 | ## 疑问 10 | 1. primary associated type 11 | 12 | ## 参考 13 | - [WWDC-Embrace Swift generics](https://developer.apple.com/videos/play/wwdc2022/110352/) 14 | - [WWDC-Design protocol interfaces in Swift](https://developer.apple.com/videos/play/wwdc2022/110353/) 15 | - [Opaque and Boxed Types](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/opaquetypes/) 16 | - [Swift Protocol 背后的故事(理论)](https://zxfcumtcs.github.io/2022/02/04/SwiftProtocol2/) 17 | - [11. Advanced Protocols & Generics](https://www.kodeco.com/books/swift-apprentice-beyond-the-basics/v1.0/chapters/11-advanced-protocols-generics) 18 | - [Opaque Parameter Declarations](https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md) -------------------------------------------------------------------------------- /Swift笔记/Swift Package Manager(Swift PM)笔记.md: -------------------------------------------------------------------------------- 1 | # Swift Package Manager(Swift PM)笔记 2 | 3 | - `Package.swift`定义了Package或Module的源码、依赖等信息 4 | - `Package.resolved`中是最终工程依赖库的版本信息,类似于Cocoapods中的Podfile.lock 5 | - 添加Package Dependency的方式 6 | - 通过Xcode添加 7 | 8 | ## Q&A 9 | 1. SPM的工程能否进行混编? 10 | - 可以 11 | 2. SPM的工程能否使用OC的library 12 | 3. SPM工程和Cocoapods工程能否共同存在 13 | - 实测可以 14 | 15 | ## 参考 16 | - [Package Manager](https://www.swift.org/package-manager/) 17 | - [Adding package dependencies to your app](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app) -------------------------------------------------------------------------------- /Swift笔记/Swift-OC混编.md: -------------------------------------------------------------------------------- 1 | ## 疑问 2 | 3 | ``` 4 | typedef enum MyEnum { 5 | MyEnumA = 1, 6 | MyEnumB = 2, 7 | MyEnumC = 3, 8 | } MyEnum; 9 | ``` 10 | 11 | OC里这种写法,在Swift中无法识别成正常的enum 12 | 13 | ## 参考 14 | 15 | - [Imported C and Objective-C APIs](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis) 16 | - [Using Imported C Macros in Swift](https://developer.apple.com/documentation/swift/using-imported-c-macros-in-swift) -------------------------------------------------------------------------------- /Swift笔记/What is lazy collections feature in Swift.md: -------------------------------------------------------------------------------- 1 | # Lazy collections feature in Swift 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | 当前最新Swift版本为Swift 5.10 6 | 7 | 之前一直没注意过`Lazy Collection`这个Swift特性,最近在看[Design protocol interfaces in Swift-WWDC](https://developer.apple.com/videos/play/wwdc2022/110353)时偶然看到,查阅了一下资料,发现这个特性很早就有了,早在WWDC2018时官方在`Using Collections Effectively` session中就提到了 8 | 9 | 先对该特性做一个概括性的描述: 10 | 11 | - 该特性是`Sequence` protocol的一个属性---`var lazy: LazySequence { get }` 12 | - 该属性返回的值同样描述了一个Sequence,虽然元素和原Sequence是相同的 13 | - 但对返回的Sequence进行各种高阶函数操作时(如map、reduce、filter等等),就拥有了`lazy`(延迟计算)能力 14 | - 所谓`延迟计算`的特性,可以理解为,直到真正需要用到集合中的元素时(而非在此之前的某个时机),才会真正去遍历或读取集合中的元素,且尽可能只读取所需要的元素,避免多余的读取操作 15 | 16 | 我把官方文档中对`lazy`属性的描述原文贴出来: 17 | 18 | > A sequence containing the same elements as this sequence, but on which some operations, such as map and filter, are implemented lazily. 19 | 20 | 我认为其实但看上面的内容是比较难描述`lazy`的能力,通过下面的两个例子可以更容易理解 21 | 22 | ## Demo1 23 | 24 | ``` 25 | let array = [1, 2, 3] 26 | 27 | let mappedArray = array.map({ 28 | print("map on array --- \($0)") 29 | return $0 30 | }) 31 | 32 | 33 | let lazyMappedArray = array.lazy.map({ 34 | print("map on lazy array --- \($0)") 35 | return $0 36 | }) 37 | 38 | print(lazyMappedArray.first) 39 | ``` 40 | 41 | 控制台输出结果为 42 | 43 | ``` 44 | map on array --- 1 45 | map on array --- 2 46 | map on array --- 3 47 | map on lazy array --- 1 48 | Optional(1) 49 | ``` 50 | 51 | 以上例子得出的结论是: 52 | 53 | - 普通的集合,在没有`lazy`能力加持下,当执行到`array.map`时就会对集合所有元素进行遍历 54 | - 有了`lazy`能力加持后,`array.lazy.map`执行时,`map`中的closure完全不会执行 55 | - 直到执行到`lazyMappedArray.first`时,`map`的closure才会执行。而且并不会遍历整个array,仅对第一个元素执行了closure 56 | 57 | ## Demo2 58 | 59 | 另一个场景是,有些时候我们需要声明一个`Readonly`的属性,返回一个集合, 60 | 61 | - 该集合的创建可能需要遍历一个已有的集合 62 | - 该`Readonly`的属性可能需要在程序整个生命周期中执行多次,每次使用都会执行上面的逻辑 63 | 64 | ``` 65 | struct ABC { 66 | let array = [1, 2, 3] 67 | var mappedArray: [Int] { 68 | return array.map { 69 | print("map on array --- \($0)") 70 | return $0 71 | } 72 | } 73 | 74 | var lazyMappedArray: LazyMapSequence.Elements, LazySequence<[Int]>.Element> { 75 | return array.lazy.map({ 76 | print("map on lazy array --- \($0)") 77 | return $0 78 | }) 79 | } 80 | } 81 | 82 | let abc = ABC() 83 | var mappedArray = abc.mappedArray 84 | var lazyMappedArray = abc.lazyMappedArray 85 | abc.mappedArray 86 | abc.lazyMappedArray 87 | 88 | lazyMappedArray.first 89 | lazyMappedArray.first 90 | ``` 91 | 92 | 控制台输出结果是 93 | 94 | ``` 95 | map on array --- 1 96 | map on array --- 2 97 | map on array --- 3 98 | map on array --- 1 99 | map on array --- 2 100 | map on array --- 3 101 | 102 | map on lazy array --- 1 103 | map on lazy array --- 1 104 | ``` 105 | 106 | - 每次执行`abc.mappedArray`,array都会被遍历一遍 107 | - 而`abc.lazyMappedArray`则不会去遍历array,因为还没有使用`lazyMappedArray`中的元素 108 | - `lazyMappedArray.first`尝试读取集合元素时,才会真正执行map closure逻辑 109 | 110 | ## 总结 111 | 112 | - `Lazy collection`特性是通过`Sequence`的`lazy`属性来提供给开发者使用,所以实现了`Sequence`协议的类型都支持`lazy`能力 113 | - 所谓`延迟计算`的特性,可以理解为,直到真正需要用到集合中的元素时(而非在此之前的某个时机),才会真正去遍历或读取集合中的元素,且尽可能只读取所需要的元素,避免多余的读取操作 114 | - `lazy`所赋予的延迟计算能力,在仅使用大数据量集合的部分元素、创建临时集合场景时,能显著降低额外性能损耗 115 | 116 | 117 | ## 参考 118 | - [Swift Collection 中的 lazy 作用](https://juejin.cn/post/6844903566772027406) 119 | - [“懒”点儿好](https://swift.gg/2016/03/25/being-lazy/) 120 | - [[ WWDC2018 ] - 高效使用集合 Using Collections Effectively](https://juejin.cn/post/6844903623189594125) -------------------------------------------------------------------------------- /Swift笔记/What is type erasure in Swift.md: -------------------------------------------------------------------------------- 1 | # What is type erasure in Swift 2 | 3 | ## 参考 4 | - [Design protocol interfaces in Swift-WWDC2022](https://developer.apple.com/videos/play/wwdc2022/110353) -------------------------------------------------------------------------------- /Swift笔记/What's new in Swift 5.md: -------------------------------------------------------------------------------- 1 | # What's new in Swift 5 2 | 3 | ## Raw Strings 4 | 5 | 在Swift 4中,当在String literal中使用反斜杠、双引号时,需要使用反斜杠进行转义,比如 6 | 7 | ``` 8 | let escape = "You use escape sequences for \"quotes\"\\\"backslashes\" in Swift 4.2." 9 | print(escape) 10 | // You use escape sequences for "quotes"\"backslashes" in Swift 4.2. 11 | ``` 12 | 13 | - 这会导致string literal中充斥着很多与语义无关的反斜杠,影响代码可读性 14 | 15 | Swift 5中做了优化,我们只需要在string literal的开始和结束位置各添加一个#,那么literal部分便可以去掉干扰阅读的反斜杠了,比如 16 | 17 | ``` 18 | let escape = #"You use escape sequences for "quotes"\"backslashes" in Swift 4.2."# 19 | print(escape) 20 | // You use escape sequences for "quotes"\"backslashes" in Swift 4.2. 21 | 22 | let multiline = #""" 23 | You can create """raw"""\"""plain""" strings 24 | on multiple lines 25 | in Swift 5. 26 | """# 27 | print(multiline) 28 | 29 | // multiline打印结果如下 30 | You can create """raw"""\"""plain""" strings 31 | on multiple lines 32 | in Swift 5. 33 | ``` 34 | 35 | > 有一点需要注意,当在String interpolation中使用Raw Strings时,因为#会讲双引号字符串部分进行特殊处理,所以String interpolatiion也会被误当做纯字符串,所以要做下特殊处理,如下 36 | 37 | ``` 38 | let track = #"Nothing Else #Matters"# 39 | print(#"songgeb is not \#(track)"#) 40 | songgeb is not Nothing Else #Matters 41 | ``` 42 | 43 | ## Some vs Any 44 | 45 | 46 | 参考 47 | 48 | - [Understanding the “some” and “any” keywords in Swift 5.7](https://swiftsenpai.com/swift/understanding-some-and-any/) 49 | 50 | ## References 51 | 52 | - [What’s New in Swift 5?](https://www.kodeco.com/55728-what-s-new-in-swift-5) 53 | - [How to use custom string interpolation](hackingwithswift.com/articles/163/how-to-use-custom-string-interpolation-in-swift) -------------------------------------------------------------------------------- /Swift笔记/Whatisnew.md: -------------------------------------------------------------------------------- 1 | # What's new in Swift 2 | 3 | - [What’s new in Swift 5.7](https://www.hackingwithswift.com/articles/249/whats-new-in-swift-5-7) -------------------------------------------------------------------------------- /Swift笔记/as-is in Swift.md: -------------------------------------------------------------------------------- 1 | # Type Casting(as/is) in Swift 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | 本文主要是对as和is语法的备忘 6 | 7 | Type Casting,翻译为类型转换 8 | 9 | Swift中使用as和is,进行**类型判断**和**类型转换** 10 | 11 | ## 类型判断-is 12 | 13 | 语法:`expression is type` 14 | 15 | 工作原理:运行时检测expression是否是type类型,是则返回true,否则false 16 | 17 | ``` 18 | struct StructA { 19 | let a: Int = 2 20 | } 21 | 22 | var someThing: Any = StructA() 23 | if someThing is StructA { 24 | print("someThing is StructA") 25 | } 26 | 27 | protocol ProtocolA { 28 | } 29 | 30 | extension StructA: ProtocolA { } 31 | 32 | if someThing is StructA { 33 | print("someThing is StructA") 34 | } 35 | ``` 36 | 37 | - Type可以是普通的类型,如Class、Struct、Enum、Tuple 38 | - 也可以是Protocol 39 | 40 | ## 类型转换-as 41 | 42 | as是将类型A的实例转换成B类型实例的语法, 43 | 44 | as的用法有三种情况:as?、as!和as 45 | 46 | - `expression as? type` 47 | - `expression as! type` 48 | - `expression as type` 49 | 50 | |项目|解释|| 51 | |:-:|:-:|:-:| 52 | |as?|runtime时期,尝试进行类型转换,转换成功返回否则返回nil,所以返回值是Optional的|| 53 | |as!|先做as?的工作,然后强制解包|如果转换失败,则会因为强制解包而crash| 54 | |as|当编译期间,编译器可以确定A->B类型转换一定成功时,可以使用as将A转换为B|一般用于upcasting或bridging| 55 | 56 | - upcasting,译为向上转换 57 | - subclass->superclass 58 | - concrete type -> Any 59 | - bridging则是由Swift Standard Library中的类型转为对应的Cocoa中的数据类型,比如`String as NSString` 60 | 61 | ``` 62 | func f(_ any: Any) { print("Function for Any") } 63 | func f(_ int: Int) { print("Function for Int") } 64 | let x = 10 65 | f(x) 66 | // Prints "Function for Int" 67 | 68 | let y: Any = x 69 | f(y) 70 | // Prints "Function for Any" 71 | 72 | f(x as Any) 73 | // Prints "Function for Any 74 | ``` 75 | 76 | ## 还有什么? 77 | 78 | as和is在Pattern Maching中也有使用,比如下面代码 79 | 80 | ``` 81 | var things: [Any] = [] 82 | 83 | ... add something to things 84 | 85 | for thing in things { 86 | switch thing { 87 | case 0 as Int: 88 | print("zero as an Int") 89 | case 0 as Double: 90 | print("zero as a Double") 91 | case let someInt as Int: 92 | print("an integer value of \(someInt)") 93 | case let someDouble as Double where someDouble > 0: 94 | print("a positive double value of \(someDouble)") 95 | case is Double: 96 | print("some other double value that I don't want to print")” 97 | default: 98 | print("default") 99 | } 100 | } 101 | ``` 102 | 103 | > 后面单独为Pattern Matching写笔记记录一下 104 | 105 | -------------------------------------------------------------------------------- /Swift笔记/iOS混编项目Tips.md: -------------------------------------------------------------------------------- 1 | # iOS混编项目Tips 2 | -------------------------------------------------------------------------------- /Swift笔记/rethrow in Swift.md: -------------------------------------------------------------------------------- 1 | # rethrows in Swift 2 | 3 | 最主要的作用是--让代码更简洁,省掉没必要的`try` 4 | 5 | 它是如何做到的呢? 6 | 7 | 来看一个实际的应用案例 8 | 9 | ``` 10 | // rethrows的方法定义如下 11 | 12 | func rethrowingFunction(throwingCallback: () throws -> Void) rethrows { 13 | try throwingCallback() 14 | } 15 | 16 | // 情况1:该种情况下在调用rethrowingFunction时就无需使用try 17 | rethrowingFunction { 18 | print("I'm not throwing errors") 19 | } 20 | 21 | // 情况2:该情况下必须使用try或其他方式处理Error 22 | do { 23 | try rethrowFunction { 24 | // xxx do some thing 25 | throw xxxxError() 26 | } 27 | } catch { 28 | } 29 | ``` 30 | 31 | - 能够看的出来,起作用的原理是根据rethrowingFunction方法的closure参数中的内容 32 | - 如果closure的操作有可能throw error,那外部调用时就要处理Error 33 | - 如果closure的操作根本不可能throw error,外部调用也就无需做任何Error处理动作了 34 | 35 | 非常好,基于它的原理我们自己来推导一下,使用rethrows的条件: 36 | 37 | - rethrows方法中至少得有一个接收closure的参数 38 | - 只有这样,编译器才能知道closure是否有潜在throws error的风险 39 | - 这个closure的参数,必须是有可能throws error的 40 | 41 | 注意事项 42 | 43 | 下面这种情况是不能使用rethrows的,因为语义含糊。编译器无法只通过callback中的内容决定外部使用者是否要进行错误处理 44 | 45 | ``` 46 | func alwaysThrows() throws { 47 | throw SomeError.error 48 | } 49 | 50 | func someFunction(callback: () throws -> Void) rethrows { 51 | do { 52 | try callback() 53 | try alwaysThrows() // Invalid, alwaysThrows() isn't a throwing parameter 54 | } catch { 55 | throw AnotherError.error 56 | } 57 | } 58 | ``` 59 | 60 | ## 其他 61 | 62 | 官方有一段表示rethrows方法的话 63 | 64 | > “A throwing method can’t override a rethrowing method, and a throwing method can’t satisfy a protocol requirement for a rethrowing method. That said, a rethrowing method can override a throwing method, and a rethrowing method can satisfy a protocol requirement for a throwing method.” 65 | 66 | 翻译一下就是 67 | 68 | - throw method 不能重载(重写)rethrow method 69 | - throw method 不能满足rethrow method的协议 70 | - rethrow method可以重载(重写)throw method 71 | - rethrow method可以满足throw method的协议 72 | 73 | 原因也容易想到 74 | 75 | - rethrow method给编译器传递的信息是,使用者是有可能不用做错误处理的;而throw method传递的信息是调用者必须做错误处理 76 | - 如果throw method可以重写rethrow,那原来使用rethrow方法的地方岂不是要编译不过了 77 | 78 | ## 参考 79 | - [How to use the rethrows keyword in Swift](https://www.avanderlee.com/swift/rethrows/) -------------------------------------------------------------------------------- /Target, Project, Workspace, Scheme in Xcode.md: -------------------------------------------------------------------------------- 1 | # Target, Project, Workspace, Scheme in Xcode 2 | 3 | ## Project 4 | 5 | - Project就是一个仓库,存放着各种文件,如源码、library,build configuration、target等 6 | - Project中可以有多个target 7 | - Project可以有build configuration,可以应用于所有的target,target也可以自定义自己的configuration 8 | - Project既可以独立存在,也可以加入到Worksapce中 9 | 10 | ### SubProject 11 | - 可以在一个Project中嵌套Project 12 | 13 | ## Target 14 | 15 | - 一个Target对应着一个product,这个product可以是很多类型,比如App, App Extension, library, framework等 16 | - Target是build的基本单元 17 | - 每个Target都有自己的build configuration,默认情况下也会继承Project的配置 18 | - 在一个Poject或Workspace下,不同target之间是可以建立显式或隐式的依赖关系的 19 | 20 | ## Workspace 21 | 22 | - Workspace是一个Xcode document,用来组织projects和其他documents 23 | - 相关的Project可以放在一个Workspace下 24 | - 一个Project也可以放到不同的Workspace下 25 | - 所有Project的build结果都会在同一个目录下 26 | - 因此Workspace中不同Project之间建立依赖关系,查看function definition,重命名等操作时都会因为自动indexing而受益 27 | 28 | ## Scheme 29 | 30 | 中文翻译为“方案” 31 | 32 | An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute. 33 | 34 | 翻译一下就是,target的build等操作时的配置信息,就当前Xcode版本(13.4.1)来说,这些操作有: 35 | 36 | - Build, Run, Test, Profile, Analyze, Archive 37 | 38 | 所以,每个target都对应着一套配置信息 39 | 40 | 当然,每次只能选择一个Scheme进行build或其他操作 41 | 42 | ## 参考 43 | 44 | - [Xcode Concepts](https://developer.apple.com/library/archive/featuredarticles/XcodeConcepts/Concept-Targets.html#//apple_ref/doc/uid/TP40009328-CH4-SW1) -------------------------------------------------------------------------------- /Timer in iOS.md: -------------------------------------------------------------------------------- 1 | # Timer in iOS 2 | 3 | The "timer" word in title represents the time-related technique in iOS, Timer, GCD Timer, CADidplayLink, say. 4 | 5 | ## Timer 6 | 7 | Timer class in Swift(NSTimer in Objective c) 8 | 9 | - based on Runloop 10 | - stopped when runloop stopped, such as in background state 11 | - not accurate, because there may be heavy task in runloop or system may introduce tolerance to reduce power usage 12 | - For repeating timers, the next fire date is calculated from the original fire date regardless of tolerance applied at individual fire times, to avoid drift. 13 | - General rule, set the tolerance to at least 10% of the interval, for a repeating timer 14 | - runloop has astrong reference to timer 15 | - memory leak 16 | - invalidate() can remove reference 17 | - can not be reused after invalidate() 18 | - Prefer to utilize CADisplayLink for smooth animation 19 | 20 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/Timer_vs_CADisplayLink.png?raw=true) 21 | 22 | ## CADisplayLink 23 | 24 | A timer object that allows your app to synchronize its drawing to the refresh rate of the display. 25 | 26 | - bound to the display’s vsync 27 | - also can be set rate(15, 20, 60 etc) 28 | - more accurate than timer 29 | - more sutiable for UI Refresh, animation etc 30 | 31 | ## GCD Timer 32 | 33 | ``` 34 | dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, aQueue); 35 | 36 | dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, ti * NSEC_PER_SEC, ti * 0.1 * NSEC_PER_SEC); 37 | 38 | dispatch_source_set_event_handler(timer, ^{ 39 | //... 40 | }); 41 | 42 | dispatch_resume(timer); 43 | ``` 44 | 45 | - based on dispatch_source, not runloop 46 | - Note that some latency is to be expected for all timers, even when a leeway value of zero is specified. 47 | 48 | ## Q&A 49 | - Is CADisplayLink accurate? 50 | - more than timer and GCD Timer, because timer and GCD Timer may introduce clock drift 51 | 52 | ## References 53 | - [Timer](https://developer.apple.com/documentation/foundation/timer) 54 | - [The secret world of NSTimer](https://danielemargutti.medium.com/the-secret-world-of-nstimer-708f508c9eb) 55 | - [iOS Timer Tutorial](https://www.raywenderlich.com/113835-ios-timer-tutorial) 56 | - [从NSTimer的失效性谈起(二):关于GCD Timer和libdispatch](https://developer.aliyun.com/article/17709) -------------------------------------------------------------------------------- /UIAppearance in iOS.md: -------------------------------------------------------------------------------- 1 | # UIAppearance in iOS 2 | 3 | 看下UIAppearance的定义 4 | 5 | ``` 6 | @protocol UIAppearance 7 | + (instancetype)appearance; 8 | + (instancetype)appearanceWhenContainedIn:(nullable Class )ContainerClass, ... NS_REQUIRES_NIL_TERMINATION API_DEPRECATED_WITH_REPLACEMENT("appearanceWhenContainedInInstancesOfClasses:", ios(5.0, 9.0)) API_UNAVAILABLE(tvos); 9 | + (instancetype)appearanceWhenContainedInInstancesOfClasses:(NSArray> *)containerTypes API_AVAILABLE(ios(9.0)); 10 | + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait API_AVAILABLE(ios(8.0)); 11 | + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedIn:(nullable Class )ContainerClass, ... NS_REQUIRES_NIL_TERMINATION API_DEPRECATED_WITH_REPLACEMENT("appearanceForTraitCollection:whenContainedInInstancesOfClasses:", ios(8.0, 9.0)) API_UNAVAILABLE(tvos); 12 | + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedInInstancesOfClasses:(NSArray> *)containerTypes API_AVAILABLE(ios(9.0)); 13 | 14 | @end 15 | ``` 16 | 17 | - UIAppearance是一个协议,继承自`NSObject`协议 18 | - 该协议定义了一系列方法,都是用于获取`UIAppearance`示例对象的方法 19 | - 注意每个方法的返回值都是`instancetype`,所以receiver有什么属性,receiver.appearance()就可以设置什么属性 20 | 21 | UIAppearance的作用是:**通过UIAppearance这种代理对象的全局方式修改UIKit中各个UI控件的样式信息** 22 | 23 | UIAppearance提供了两类方法来修改控件样式: 24 | 25 | - 通过`+ (instancetype)appearance`方法,可以获得所有receiver对应的代理对象,所以可以用来修改所有receiver实例的样式,如下,所有的UINavigationBar示例的barTintColor都修改了 26 | 27 | ``` 28 | UINavigationBar.appearance().barTintColor = navBarTintColor 29 | ``` 30 | - 通过其他方法,可以获取某些特定情况下的receiver对应代理对象。如下,只会修改所有在UINavigationController中的UINavationBar的背景图片 31 | 32 | ``` 33 | let navigationBarAppearance = 34 | UINavigationBar.appearance(whenContainedInInstancesOf: [UINavigationController.self]) 35 | navigationBarAppearance.setBackgroundImage(navBarBackgroundImage, for: .any, barMetrics: .default) 36 | ``` 37 | 38 | > 注意UIAppearance仅对未加入到Window中的UI控件起作用,如果要应用于已经添加在Window中控件,可以移除重新添加 -------------------------------------------------------------------------------- /UIKit笔记.md: -------------------------------------------------------------------------------- 1 | # UIKit笔记 2 | 3 | UIKit中各UI控件备忘录 4 | 5 | ## UIVisualEffectView 6 | 7 | 8 | - [UIVisualEffectView Tutorial: Getting Started](https://www.kodeco.com/16125723-uivisualeffectview-tutorial-getting-started) 9 | - [3 Approaches to Applying Blur Effects in iOS](https://betterprogramming.pub/three-approaches-to-apply-blur-effect-in-ios-c1c941d862c3) 10 | 11 | ## UICollectionView 12 | - `reloadItemsAtIndexPaths`执行时默认是有fade动画 13 | - `reloadData`则不会 14 | - 可以通过`[UIView performWithoutAnimation]:`取消fade动画 15 | 16 | ## 键盘 17 | - 对同一个observer添加多次通知,该observer就会收到多次通知 18 | - 在模拟器上由于不确定的因素,可能导致键盘show和hide通知展示多次 19 | - `UIKeyboardWillChangeFrameNotification`时机可能早于`UIKeyboardWillShowNotification` 20 | - 可以通过监听`UIKeyboardWillChangeFrameNotification`通知,判断frame于屏幕frame的关系 21 | 22 | ``` 23 | CGRect beginFrame = [value.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue]; 24 | CGRect endFrame = [value.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; 25 | BOOL willShow = beginFrame.origin.y >= SCREEN_HEIGHT && endFrame.origin.y < SCREEN_HEIGHT; 26 | BOOL willHide = beginFrame.origin.y < SCREEN_HEIGHT && endFrame.origin.y >= SCREEN_HEIGHT; 27 | ``` 28 | 29 | ## UITextView 30 | 31 | - 不支持placeholder 32 | - 但可以自己添加UILabel实现placeholder效果 33 | 34 | ## UITextField 35 | 36 | ### 复制粘贴 37 | - 默认会有复制粘贴效果 38 | - 没有很好的禁止粘贴的办法 39 | - 可以通过`UITextFieldDelegate`的begin和endeditting回调,使得在编辑过程中设置`userInteractiveEnable`为NO来关闭粘贴效果 40 | 41 | ## Target-Action & UIControl 42 | - target-action是一个设计模式,UIControl实现了该设计模式 43 | - 如果target设置为nil,则事件会发送给firstResponder,沿着responder chain传递 44 | - 当添加多个target时,action发生时,多个target的action method都会执行 45 | 46 | ## Bounds VS Frame 47 | 48 | - frame是view的最小包围盒矩形 49 | - 当view的transform变化时,比如旋转、缩放,都会导致frame改变;但bounds不会改变 50 | - 官方说设置frame的size时会影响到bounds的size,是说开发者设置frame时会这样;像通过修改transform进行缩放时,frame的size发生了变化,但bounds仍不变 51 | 52 | ### 参考 53 | 54 | - [View Programming Guide for iOS](https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html) 55 | - [iOS:重识Transform和frame](https://www.jianshu.com/p/e1fec2f92c63) 56 | 57 | ## UIImage 58 | 59 | ### images 60 | 61 | 貌似是为动图设置的比如gif,别的类型还不清楚 62 | - 可以通过CG的方法获取data中每一帧数据,和UIImage的`animatedImage`方法创建包含`images`的UIImage 63 | - 包含images数据的图片,在button、imageview上展示时都是动态的 64 | 65 | ## UIButton 66 | 67 | ### titleEdgeInsets 68 | 69 | 普通btn 70 | 71 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/uikit_btn.png?raw=true) 72 | 73 | ### imageEdgeInsets 74 | 75 | ### contentEdgeInsets 76 | 77 | ## 自定义视图 78 | 79 | ### 布局 80 | 81 | #### frame布局 82 | - `initWithFrame`中只添加对应的视图 83 | - `layoutsubviews`中设置frame 84 | 85 | #### autolayout布局 86 | - `initWithFrame`中直接添加视图并设置约束即可 87 | 88 | ### 自适应高(宽)度视图 89 | 90 | 所谓的自适应高宽度视图,可以参照UILabel、UIButton这种,满足以下两点或其中一点 91 | 92 | - 如果使用autolayout布局,只需要设置部分约束,则高度或宽度就会自动改变 93 | - 如果使用frame布局,则通过执行sizeToFit,视图的frame会自适应到合适尺寸 94 | 95 | 如果要做很通用的视图组件,最好同时满足上面两点,这样使用方用着会很舒服 96 | 97 | 满足上面两点有两种思路 98 | 99 | 1. 为了让调用方在使用autolayout布局时可以自适应 100 | - 视图内部可以使用autolayout布局,然后添加满约束,同时有些约束要设置为低优先级 101 | - 同时为了满足sizeToFit可以work,还要重写视图的sizeThatFit方法,该部分使用frame布局,frame和autolayout的约束要保持逻辑一致 102 | 103 | 2. 第二种方式是 104 | - 自定义视图内部使用frame进行布局 105 | - 这样sizeThatFit方法中可以返回正确的宽高度 106 | - 同时,intrinsicContentSize也要有返回值,不能返回0 107 | - 而且要衡量view不同方法调用时机,避免出现循环调用问题 -------------------------------------------------------------------------------- /UIScrollViewDelegate回调方法执行时机备忘.md: -------------------------------------------------------------------------------- 1 | ``` 2 | // 开始拖拽时执行 3 | func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 4 | print(#function) 5 | 开始拖拽 6 | } 7 | // 抬起手指时执行,可以修改滚动结束时的位置 8 | func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { 9 | print(#function) 10 | } 11 | // 抬起手指时执行,decelerate为true表示还会滚动一会儿 12 | func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 13 | print(#function) 14 | print("decelerate->\(decelerate)") 15 | } 16 | //当抬起手指时,还要继续滚动时执行 17 | func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { 18 | print(#function) 19 | } 20 | //停止滚动时执行 21 | func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 22 | print(#function) 23 | } 24 | // 当通过`setContentOffset/scrollRectVisible:animated:`方法通过动画方式使得滚动,且滚动停止时执行 25 | func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 26 | print(#function) 27 | } 28 | // 询问delegate,是否支持点击顶部状态栏让距离状态栏最近的scrollView滚动到top 29 | // 若scrollView.scrollToTop和该方法有一个是false,那点击状态栏都不会滚动 30 | func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { 31 | return false 32 | } 33 | 34 | func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { 35 | print(#function) 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /UITableView in iOS.md: -------------------------------------------------------------------------------- 1 | # UITableView in iOS 2 | 3 | ## 4 | 5 | ## UITableViewCell 6 | 7 | 系统提供了四种UITableViewCell的样式,分别是 8 | 9 | [![6jJfn1.md.png](https://z3.ax1x.com/2021/03/26/6jJfn1.md.png)](https://imgtu.com/i/6jJfn1) 10 | 11 | ### 如何使用cell的系统样式 12 | 13 | 两种方式 14 | 15 | 第一种方式是遵循正统的自定义cell套路 16 | 17 | 1. 创建自定义cell,比如`CustomCell`,重写`initWithStyle:reuseId`初始化方法,在其中指定要用的style 18 | 2. 通过`UITbleview.register`方法注册`CustomCell` 19 | 3. `cellForRow`中通过`dequeueCellWithIdForIndexPath`获取cell,并为cell相应属性赋值 20 | 21 | 第二种方法比较古老一些 22 | 23 | 1. 无需`register`cell 24 | 2. 直接在`cellForRow`中通过`dequeueCellWithId`获取cell,紧跟着需要判断如果cell为空,则使用`initWithStyle:reuseId`新建一个cell 25 | 3. 为cell相应的属性赋值 26 | 27 | ### UITableViewCell.imageView 28 | 29 | 不论使用自定义cell还是系统样式的cell,imageView都存在与cell中,如果给它赋值 30 | 31 | - 它会显示 32 | - 并且默认情况下,cell分割线会右移 33 | 34 | [![6jwsbj.md.png](https://z3.ax1x.com/2021/03/26/6jwsbj.md.png)](https://imgtu.com/i/6jwsbj) 35 | 36 | ## cell之间的横线 37 | 1. 当设置了seperator样式后,tableview的cell之间就会显示横线 38 | 2. 但有个问题,多余的cell也会展示 39 | 3. 解决办法是给tableviewFooter设置一个空的view 40 | 41 | ## 动画 42 | 43 | - `insertRows`等系列方法,默认情况是有动画地执行 44 | - `reloadData`则是无动画的刷新 45 | 46 | ### tableviewHeader动画 47 | 可以通过如下代码实现tableViewHeader的动画 48 | 49 | ``` 50 | tableView.beginUpdates() 51 | //header animation 52 | tableView.endUpdates() 53 | ``` 54 | 55 | ## automaticDimension 56 | 57 | 当UITableView+isPagingEnable配合使用时,比如短视频App的大屏视频feed流页面,滚动过程中会发现contentSize不准确问题,原因在于`UITableView.estimatedRowHeight`属性 58 | 59 | - 该属性默认开启,值为`automaticDimension` 60 | - 因为了能每一行高度都不同,为了避免每次load开启后表示`UIKit`会通过该值估算`contentSize`等属性 61 | - 所以若需要精确的`contentSize`值,需要设置为0进行关闭 62 | 63 | ## UITableViewDelegate 64 | 65 | ## UITableViewCell 66 | 67 | ### prepareForReuse和cellForRow不一定是成对出现的 -------------------------------------------------------------------------------- /UNNotification in iOS.md: -------------------------------------------------------------------------------- 1 | # UNNotification in iOS 2 | 3 | 4 | iOS 10引入,可以认为统一了原来的本地推送和远程推送的使用方式 5 | 6 | > 注意,与NotificationCenter无关 7 | 8 | 9 | 10 | ## 参考 11 | 12 | - [Notification Center 与 UNUserNotification](https://zhuanlan.zhihu.com/p/116560060) -------------------------------------------------------------------------------- /WWDC-Image and Graphics Best Practices 笔记.md: -------------------------------------------------------------------------------- 1 | # WWDC-Image and Graphics Best Practices 笔记 2 | 3 | 内存、CPU消耗增多时,相应电量消耗也增多 4 | 5 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/imagebuffer_databuffer_framebuffer.png?raw=true) 6 | 7 | - JPEG、PNG等格式都是压缩格式,在内存中是一段连续的`data buffer` 8 | - 解码过程是要将`data buffer`转为`image buffer` 9 | - `image buffer`和真正的图片大小成比例的,所以当是大图时,解码工作很耗CPU 10 | - 解码工作由`Core Graphics`完成 11 | 12 | ## DownSampling 13 | 14 | 通过DownSampling,降低解码所占用内存大小 15 | 16 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/downsampling-1.png?raw=true) 17 | 18 | - 说白了就是通过`Core Graphic`API获取到缩略图 19 | 20 | ## Decoding in Scrollable view 21 | 22 | 滚动scrollview同时加载多个图片时遇到内存、CPU激增,导致卡顿 23 | 24 | 在Collectionview的prefetching时机进行预处理,提前对图片进行解码工作,可以分散CPU占用情况 25 | 26 | 同时将解码工作放到后台线程,能减少卡顿 27 | 28 | ## 疑问 29 | 30 | 1. 通过autoreleasepool可否避免UIImage内存激增问题 31 | 32 | ## 参考 33 | 34 | - [Image and Graphics Best Practices](https://developer.apple.com/videos/play/wwdc2018/219) -------------------------------------------------------------------------------- /Xcode Practice/Assets.md: -------------------------------------------------------------------------------- 1 | # Assets 2 | 3 | ## Slicing 4 | 5 | Image Slicing是WWDC 2013 Xcode 5中引入的功能,简言之,功能等同于`UIImage`的`resizableImage(withCapInsets:)`方法 6 | 7 | 参考--[关于Assets中图片的Slicing功能](https://juejin.cn/post/6844903705829965831),[WWDC 2013 Session笔记 - Xcode5和ObjC新特性](https://onevcat.com/2013/06/new-in-xcode5-and-objc/) 8 | 9 | 新版本Xcode中可视化slicing工具`show slicing`改变了位置,可以选中图片后,在右上角三个点中找到 10 | 11 | ## Alignment 12 | -------------------------------------------------------------------------------- /Xcode Practice/AttachProcess.md: -------------------------------------------------------------------------------- 1 | # Attach Process in Xcode 2 | 3 | -------------------------------------------------------------------------------- /architecture/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/architecture/.DS_Store -------------------------------------------------------------------------------- /architecture/AOP(Aspect Oriented Programming)学习笔记.md: -------------------------------------------------------------------------------- 1 | # AOP(Aspect Oriented Programming)学习笔记 2 | 3 | 中文译作面向切面的编程 4 | 5 | 本质上是试图用某种技术或方式,将一些切面逻辑嵌入到核心业务逻辑的前后。比如一些非功能性的业务如日志记录,就算是一个切面 6 | 7 | 这种编码思想,可以避免对核心业务逻辑做不必要的修改 8 | 9 | AOP的实现通常会考虑使用动态代理的方式: 10 | 11 | 1. 使用代理模式(Proxy Design Pattern),将切面逻辑写到切面逻辑中 12 | 2. 通过一些动态技术,将代理类嵌入到代码执行流程中 13 | 14 | 对于iOS来说, 15 | 16 | - 可以使用Method-Swizzling,修改运行时某个方法的执行逻辑 17 | - 也可以通过Clang插桩技术,在编译期修改源代码的逻辑 18 | 19 | ## 参考 20 | 21 | - [使用AOP](https://www.liaoxuefeng.com/wiki/1252599548343744/1266265125480448) -------------------------------------------------------------------------------- /architecture/App's lifecycle in iOS.md: -------------------------------------------------------------------------------- 1 | # 跨页面数据同步方案学习 2 | 3 | ## 参考 4 | 5 | - [](https://casatwy.com/OOP_nomodel.html) 6 | - [iOS 中跨页面状态同步方案比较](https://juejin.cn/post/6844903951268052999) 7 | - [客户端全局数据同步方案(一)](http://djs66256.github.io/2017/04/02/2017-04-09-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%85%A8%E5%B1%80%E6%95%B0%E6%8D%AE%E5%90%8C%E6%AD%A5%E6%96%B9%E6%A1%88%E4%B8%80/) -------------------------------------------------------------------------------- /architecture/Clean Architecture学习笔记.md: -------------------------------------------------------------------------------- 1 | # Clean Architecture学习笔记 2 | 3 | ![](https://cdn.jsdelivr.net/gh/songgeb/picx-images-hosting@master/CleanArchitecture.70aa44fhju.webp) 4 | 5 | Clean Architecture是Robert C. Marti在2012年提出的概念,他汇总了已有的架构系统,将核心的思想汇聚到该架构系统中--一种按层划分的系统架构模型 6 | 7 | 上图中每一圈对应一层,从里到外分别是:Entities、Use Cases、Presenter Layer、UI/Web Layer 8 | 9 | ### 依赖原则 10 | 11 | 该架构系统中最重要的一点便是其规定了每一层之间严格的依赖原则,即: 12 | 13 | **只能是外层依赖内层,一定不能出现内层依赖外层** 14 | 15 | - 内层的任何代码(方法、类、数据结构)都不能对外层有任何依赖 16 | 17 | #### 为什么必须只能外层依赖内层 18 | 19 | > 以下内容来自ChatGPT 20 | 21 | 在Clean Architecture中,要求只能外层依赖内层,而不能内层依赖外层,主要是为了实现以下几个关键目标: 22 | 23 | 1. 独立性: 24 | 内层(核心业务逻辑)不依赖于外层(UI、数据库、网络等),使得核心业务逻辑独立于具体的实现细节。这种独立性使得业务逻辑可以在不改变的情况下被复用或移植到不同的应用程序中。 25 | 26 | 2. 可测试性: 27 | 由于内层不依赖于外层,核心业务逻辑可以在没有UI、数据库或网络等外部依赖的情况下进行单元测试。这大大简化了测试过程,提高了测试的覆盖率和质量。 28 | 29 | 3. 可维护性: 30 | 当外层的实现细节(如UI框架、数据库技术等)发生变化时,内层的业务逻辑不需要做出相应的修改。这种分离使得代码更容易维护和扩展。 31 | 32 | 4. 灵活性: 33 | 通过依赖倒置原则(Dependency Inversion Principle),外层通过接口或抽象类与内层进行交互,具体的实现细节可以在外层进行定义和修改。这种设计使得系统更加灵活,能够适应不断变化的需求。 34 | 35 | 5. 模块化: 36 | 内层和外层的分离使得系统更加模块化,每个模块都有明确的职责和边界。这种模块化设计有助于团队协作和代码管理。 37 | 总结来说,Clean Architecture通过限制依赖方向,确保了系统的独立性、可测试性、可维护性、灵活性和模块化。这些特性使得系统更加健壮,能够更好地应对复杂的业务需求和技术变化。 38 | 39 | 40 | 41 | ### Entities 42 | 43 | - Entities表示业务数据模型 44 | 45 | ### Use Cases 46 | 47 | 中文翻译为“用例” 48 | 49 | - 这是应用程序特定的业务规则/流程 50 | - 它包含了应用程序所有的功能 51 | - 它来协调数据流,如从Entities到外部,或者反过来(指导Entities做怎样的修改) 52 | 53 | ### Interface Adapters 54 | 55 | - 负责将对数据进行转换 56 | - 将外部的数据转换为内部Use Cases和Entities所需的数据格式 57 | - 将内部的数据格式转换为外部UI、外部数据库、外部服务所需的格式 58 | 59 | ### Crossing boundaries 60 | 61 | - 有时内圈是需要使用外圈的,但直接使用就违反了单向依赖原则 62 | 63 | ### 总结 64 | 65 | The outermost circle is low level concrete detail. As you move inwards the software grows more abstract, and encapsulates higher level policies. The inner most circle is the most general. 66 | 67 | 68 | ## 疑问 69 | 1. 为什么只能外圈依赖内圈,不能反过来 70 | 71 | 72 | ## 参考 73 | - [The Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) 74 | - [Clean Architecture and MVVM on iOS](https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3) 75 | 76 | -------------------------------------------------------------------------------- /architecture/Images/App_responsibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songgeb/I-Love-iOS/fddae967a065f81f2076190c58ae99883eae464b/architecture/Images/App_responsibility.png -------------------------------------------------------------------------------- /architecture/MVP.md: -------------------------------------------------------------------------------- 1 | # MVP 2 | -------------------------------------------------------------------------------- /architecture/MVVM学习笔记.md: -------------------------------------------------------------------------------- 1 | # MVVM学习笔记 2 | 3 | 4 | ![](https://user-images.githubusercontent.com/5978164/93760502-b7338400-fc3e-11ea-8080-e6504c0c5d25.jpg) 5 | 6 | - ViewModel有两项主要的职责 7 | - 存储用于视图展示的数据,这些数据通常是只和展示相关,而不一定就是底层的数据model,更常见的形式是,vm持有底层model,然后有一些只和展示相关的属性,用于提供给视图使用 8 | - 除了存储视图相关的数据,vm也负责与业务相关的数据网络请求、数据转换等工作,这样有助于减轻vc的负担 9 | - ViewModel的目的是尽量让它与其他部分(ViewController、Model)隔离开 10 | - 就像纯函数一样,不论何时,不论外部环境如何变化,同样的输入只对应同样的输出,不会改变外部,也不会被外部改变,这就是没有副作用,页就是纯函数含义 11 | - 比如说一个函数中的工作是发送网络请求,其实这就有副作用,因为对整个app的网络环境产生了变化 12 | - ViewModel内部核心的工作应该是进行各种计算、数据读写等,最终将得出的结果存储到ViewModel的property中,等待外部使用 13 | - ViewModel是只关心业务不关心视图,因为不用关联view,所以也就容易测试了 14 | - vc不关心业务,只需处理好viewmodel和view之间的逻辑即可 15 | - ViewModel的存在就是为了极大的消除副作用 16 | - 除了图中所说,MVVM中还有一个隐藏的`Binder`约定 17 | - 这个`Binder`主要是用于`view`和`viewModel`之间数据同步的 18 | - `view`和`viewModel`之间一般是双向绑定,`view`的事件会触发`viewModel`数据更新;`viewModel`的数据变化会导致`view`状态变化 19 | 20 | ## 疑问 21 | 1. 对比一下有无observer实现方案的区别,是否必须做数据监听 22 | 23 | ## 参考 24 | - [ReactiveCocoa and MVVM, an Introduction](http://thumbworks.io/blog/2014/12/06/reactivecocoa-mvvm-introduction/) 25 | - [iOS 关于MVVM Without ReactiveCocoa设计模式的那些事](https://zhuanlan.zhihu.com/p/38420233) -------------------------------------------------------------------------------- /architecture/VIPER.md: -------------------------------------------------------------------------------- 1 | # VIPER 2 | 3 | ## 什么是VIPER 4 | 5 | ## VIPER优势 6 | 7 | ## 适合场景 8 | 9 | 10 | ## 参考 11 | - [Architecting iOS Apps with VIPER](https://www.objc.io/issues/13-architecture/viper/) -------------------------------------------------------------------------------- /architecture/iOS Architecture Note.md: -------------------------------------------------------------------------------- 1 | # iOS Architecture Note 2 | 3 | 大佬Casa Taloyum在[iOS应用架构谈 开篇](https://casatwy.com/iosying-yong-jia-gou-tan-kai-pian.html)对一个App所做的事情进行了概括,感觉特别好,下面进行引用 4 | 5 | 一个移动应用,核心要实现的功能无外乎如下所示: 6 | 7 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/architecture/Images/App_responsibility.png?raw=true) 8 | 9 | 为了实现这些功能,技术层面要解决如下的问题: 10 | 11 | - 调用网络API 12 | - 页面展示 13 | - 数据的本地持久化 14 | - 动态部署方案 15 | 16 | 稍微详细介绍一下每部分的内容: 17 | 18 | - 如何让业务开发工程师方便安全地调用网络API?然后尽可能保证用户在各种网络环境下都能有良好的体验? 19 | - 页面如何组织,才能尽可能降低业务方代码的耦合度?尽可能降低业务方开发界面的复杂度,提高他们的效率? 20 | - 当数据有在本地存取的需求的时候,如何能够保证数据在本地的合理安排?如何尽可能地减小性能消耗? 21 | - iOS应用有审核周期,如何能够通过不发版本的方式展示新的内容给用户?如何修复紧急bug? 22 | 23 | 以上是App对于用户方面所做的事情,App对于开发团队也有有一些事情要做: 24 | 25 | - 收集用户数据,给产品和运营提供参考 26 | - 合理地组织各业务方开发的业务模块,以及相关基础模块 27 | - 每日app的自动打包,提供给QA工程师的测试工具 28 | 29 | ## View层的组织和调用方案 30 | 31 | ### 几个规范 32 | 33 | #### ViewController不要有private method 34 | - private method是指除了View创建、delegate等逻辑之外的方法,比如图片裁剪,日期转换等小功能 35 | - private method应放到相应的模块,比如某个具体业务中,如果是通用的可以放入分类或工具类中。ViewController中逻辑已经很多了,不适合再加入这种小逻辑 36 | 37 | #### 不建议使用BaseViewController 38 | 39 | - 这样会增加业务方的使用成本,且并不属于使用继承的明显场景 40 | - 可以改用AOP的方式,比如Method Swizzling 41 | 42 | ### MVC, MVVM等架构 43 | 44 | 谈到架构,有三个角色:数据管理者,数据加工者和数据展示者,所有的架构都是处理这三个角色的分工,制定他们之间交互的规范 45 | 46 | iOS的架构有MVC, MVCS, MVVM, VIPER, MVP 47 | 48 | #### MVCS 49 | 50 | 胖Model与瘦Model 51 | 52 | ## Q&A 53 | 54 | 1. 当无法进行大粒度抽象时,比如业务很复杂,参数很多时,要考虑策略模式 55 | - 看一下策略模式 56 | ## 参考 57 | - [iOS应用架构谈 开篇](https://casatwy.com/iosying-yong-jia-gou-tan-kai-pian.html) 58 | - [iOS应用架构谈 view层的组织和调用方案](https://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html) -------------------------------------------------------------------------------- /architecture/iOS中跨页面数据同步方案.md: -------------------------------------------------------------------------------- 1 | # iOS中跨页面数据同步方案 2 | 3 | 跨页面数据同步指的是不同页面可能需要同步更新某些状态,比如列表页与详情页,详情页中做了某些操作UI发生了变化,希望将变化同步到列表页 4 | 5 | 将不同的场景进行一下抽象,可以将“跨页面数据同步”描述为,某一处数据发生了变化,同时希望其他一处或多处地方也感知到并且也进行数据更新 6 | 7 | 要做到数据同步,有几种不同的实现思路: 8 | 9 | - **主动拉**,每次都获取最新数据,比如每次进入页面都尝试获取一次最新数据 10 | - **监听数据变化**,当某处数据变化时,通过某种机制将变化主动推给所有监听者 11 | 12 | 我认为**监听数据变化**方式要比**主动拉**好,因为主动拉不够准确和灵活, 13 | 14 | - 主动拉方式,实现起来基本是每次某个固定时机(比如页面每次进入)进行无脑拉取,由于比较难定位到真正数据变化的时机,所以会造成多余的拉取工作 15 | 16 | ## 监听数据变化 17 | 18 | 该思路中对应的实现也有多种,比如: 19 | 20 | - delegate、block回调 21 | - NSNotificationCenter、[SwiftNotificationCenter](https://github.com/100mango/SwiftNotificationCenter) 22 | - 支持强类型仿照EventBus实现的--[TPEventBus](https://github.com/wanhmr/TPEventBus) 23 | - 基于响应式编程思想的实现 24 | - Swift中的Combine 25 | - 知名框架Rx系列,如RxSwift、ReactiveCocoa 26 | - CoreData,再配合FetchResultController,任何CoreData的数据更新会自动通知到应用它的地方 27 | 28 | 29 | ## 疑问 30 | 1. eventbus与nsnotificationcenter区别?有什么优势? 31 | 32 | 33 | ## 参考 34 | - [iOS 中跨页面状态同步方案比较](https://juejin.cn/post/6844903951268052999) 35 | - [西瓜视频 Android 端内数据状态同步方案 VM-Mapping](https://www.infoq.cn/article/prxrkuxcbgvqw7ghbgs5) -------------------------------------------------------------------------------- /architecture/《App Architecture》Note.md: -------------------------------------------------------------------------------- 1 | # App Architecture 2 | 3 | Applications Are a Feedback Loop 4 | 5 | 1. Construction—Who constructs the model and the views and connects the two? 6 | 2. Updating the model—How are view actions handled? 7 | 3. Changing the view—How is model data applied to the view? 8 | 4. View state—How are navigation and other non-model state handled? 9 | 5. Testing—What testing strategies are used to achieve reasonable testcase code coverage? 10 | 11 | The answers to these five questions form the basis of the application design patterns we’ll look at in this book. 12 | 13 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/application_feedback_loop.png?raw=true) 14 | 15 | ## 书中未提及的架构 16 | 17 | ### MVP 18 | 19 | ### VIPER 20 | 21 | ## MVC 22 | 23 | - Model和View由ViewController关联起来 24 | - View要做到独立(自给自足)、可复用 25 | - 为了做到View独立可复用和保证UI来自单一数据源,需要通过观察者模式订阅Model的变化 26 | 27 | ### View State 28 | 那些与数据源Model无关的UI上的变化,统称为View State,比如 29 | 30 | - 导航栏的变化,新的ViewController进入 31 | - View内部状态的变化,如UISwitch点击后,自动发生UI变化 32 | 33 | 这些View State在处理时,一般会在本地简单处理掉,比如一个按钮点击后,文案要发生变化,那就直接写个方法修改就好 34 | 35 | ### Apple将MVC看成三种设计模式的组合 36 | 37 | - ViewController将视图层级组合在一起管理,这是组合模式 38 | - ViewController作为Model和View的沟通桥梁,处理它们之间各种交互,这是策略模式 39 | - 当Model数据变化时需要更新视图,需要通过观察者模式实现 40 | 41 | ### 优点 42 | 43 | - 与iOS的一些Framework比较搭配,比如Storyboard 44 | - 简单、学习使用成本低 45 | 46 | ### 缺点 47 | - ViewController臃肿 48 | - 业务逻辑不容易测试 49 | - 只能使用集成测试(integration test) 50 | 51 | ### 如何改进缺点 52 | - ViewController臃肿问题:将任务分解到不同组件中 53 | - 比如数据处理相关的可以放到Model层 54 | - 复杂视图部分,是否可以抽离成子ViewController 55 | - 当ViewControler中充斥大量回调处理逻辑且这些逻辑只是将时,考虑将这些与ViewController的视图关联不大的逻辑移到 56 | 57 | ### 疑问 58 | 1. 实际开发中,View经常会持有Model,这样做可能会降低可复用性,这种做法是否不合理? 59 | 2. MVC中还需要通过观察者模式来监听Model的变化吗?实践中感觉没用过 60 | 3. Interface test、data-driven regression tests、Integration Test 61 | 62 | ## MVVM+C 63 | 64 | As with all good patterns, this isn’t just about moving code into a new location. The purpose of the new view-model layer is twofold: 65 | 66 | 1. To encourage structuring the relationship between the model and the view as a pipeline of transformations. 67 | 2. To provide an interface that is independent of the application framework but substantially represents the views’ presentation state. 68 | 69 | ### 疑问 70 | 1. 订阅model操作也要在ViewModel中吗? 71 | 72 | ## MVC+VS 73 | 74 | MVC+ViewState 75 | 76 | 77 | ## 疑问 78 | 1. 如何理解书中提到的View State 79 | 2. MVVM+C中的Coordinator的优势是什么? 80 | 3. MVVM中ViewModel需不需要持有Model? 81 | 4. 文中多次提到State Restoration,实际开发中几乎没用过,可能需要学一下 82 | 83 | ## 参考 84 | -[App Architecture](https://www.objc.io/books/app-architecture/) -------------------------------------------------------------------------------- /architecture/《谈谈 MVX 中的 Model》笔记.md: -------------------------------------------------------------------------------- 1 | # How to Design Model in iOS 2 | 3 | ## 疑问 4 | 1. 胖Model还是瘦Model? 5 | 6 | ## 参考 7 | - [谈谈 MVX 中的 Model](https://draveness.me/mvx-model/) -------------------------------------------------------------------------------- /architecture/关于架构的一些读书笔记.md: -------------------------------------------------------------------------------- 1 | # 关于架构的一些读书笔记 2 | 3 | ## 去Model化 4 | 5 | 什么是Model化? 6 | 7 | 无论前端还是后端开发中,都会使用个钟自定义Model对象作为数据载体,在不同模块(比如MVC)之间传递,这就是Model化 8 | 9 | 什么是去Model化? 10 | 11 | 即减少上面自定义Model的使用 12 | 13 | 为什么要去Model化? 14 | 15 | - 其实这种仅用于数据载体的设计是违反OOP思想的 16 | - 一个类或对象,只包含自己领域的数据,而外部则可能对其进行任意修改,违背了封装特性 17 | - 有可能这个Model引入不必要的依赖 18 | - 比如在跨层或跨模块使用Model进行数据传递时,该Model在模块1中或许依赖了1中的东西,为了能在模块2中正常使用Model可能也依赖了模块2中的各种东西,这就间接地让模块1和模块2之间有了依赖 19 | 20 | 21 | ## 参考 22 | - [去model化和数据对象](https://casatwy.com/OOP_nomodel.html) 23 | - [laizhenwei/YFProtocolModel](https://github.com/laizhenwei/YFProtocolModel) 24 | - [猿题库 iOS 客户端架构设计](https://mp.weixin.qq.com/s?__biz=MjM5NTIyNTUyMQ==&mid=444322139&idx=1&sn=c7bef4d439f46ee539aa76d612023d43) 25 | - [ShannonChenCHN/iOS-App-Architecture-参考资料](https://github.com/ShannonChenCHN/iOS-App-Architecture/blob/master/Reference.md) -------------------------------------------------------------------------------- /architecture/单向数据流(Unidirectional data flow).md: -------------------------------------------------------------------------------- 1 | # 单向数据流(Unidirectional data flow) 2 | 3 | 从几个问题开始学习单向数据流 4 | 5 | - 单向数据流是什么,历史和背景有哪些 6 | - 适用场景 7 | - 工业中的应用有什么 8 | 9 | 10 | 多个UI操作维护着一个变量,多个状态变量去更新同一个UI元素 11 | 12 | ## 单向数据流在iOS中的应用 13 | 14 | 从OneV的例子来看 15 | 16 | - title 17 | - 决定title的数据只有todo数组个数 18 | - 需要更新title的地方有两处:增加todo,和删除todo 19 | - tableview 20 | - 决定tableview数据的是todo数组 21 | - 需要更新tableview的时机有首次显示列表 22 | - 增加todo,删除todo时 23 | - +号 24 | - 决定+的数据是第一个cell中textfield的内容 25 | - textfield内容变化时要更新 26 | - 增加todo时 27 | 28 | 如果能用单一的状态控制UI的变化 29 | 30 | 31 | - 其单向的特点,适用于于复杂的页面,比如视频播放页面,可以降低开发复杂度 32 | - 提高页面及其中逻辑的可测试性 33 | 34 | ## 参考 35 | - [单向数据流动的函数式 View Controller](https://onevcat.com/2017/07/state-based-viewcontroller/) 36 | - [Unidirectional Data Flow](https://www.geeksforgeeks.org/unidirectional-data-flow/) 37 | - [架构系列—基于状态管理的单向数据流架构](https://juejin.cn/post/6844904179467550734) 38 | - [单向数据流和双向数据流及双向数据绑定](https://blog.csdn.net/qq_43101321/article/details/102585867) 39 | - [Unidirectional Data Flow Architecture (Redux) in Swift](https://medium.com/seyhunakyurek/unidirectional-data-flow-architecture-redux-in-swift-6fa2ed5c3c76) -------------------------------------------------------------------------------- /architecture/移动端复杂页面架构设计.md: -------------------------------------------------------------------------------- 1 | # 移动端复杂页面架构设计 2 | 3 | 模块化、组件化、插件等概念的理解和实践 4 | 5 | > 要注意一点,关于插件化,还有另一个含义,是关于动态库相关的,不要搞混了 6 | 7 | 看了抖音的文章,感觉好像和优酷也差不多,仍是插件化方案。但有点说的比较好: 8 | 9 | - 因为改动量比较大,不是一次性改完,那就要一边开发需求一边重构。类似于汽车在高速公路行驶的过程中换轮胎 10 | - 所以,如何保证重构后的功能和之前完全一样。而避免给测试同学带来过多的压力 11 | 12 | ## 页面架构时要考虑哪些问题 13 | 14 | 1. 谁负责构建 model 和 view,以及将两者连接起来? 15 | 2. 如何根据 view 的变更来更新 model? 16 | 3. 如何将 model 中的数据应用到 view 上? 17 | 4. 如何处理导航、非 model 相关的状态? 18 | 5. 要采取怎样的测试策略来提高测试覆盖率? 19 | 20 | 21 | ## 插件化方案 22 | 23 | 1. 优酷文章中,“将 UI 元素从大到小进行模块的划分”的思路值得借鉴 24 | 25 | ### 插件之间通信 26 | 27 | - 发布订阅模式 28 | - 可以解决A插件变化后通知给其他插件 29 | 30 | ## Chatgpt推荐 31 | 以下是一些知名App的技术博客或者开发者平台,你可以在这些平台上找到关于复杂页面架构设计的实践经验: 32 | 33 | - Uber Engineering Blog:Uber的工程团队在他们的技术博客上分享了很多关于移动端开发的技术文章,包括复杂页面的架构设计和最佳实践。 34 | 35 | - Airbnb Engineering & Data Science:Airbnb的工程团队也在他们的技术博客上分享了很多关于移动端开发的实践经验,包括复杂页面的架构设计和技术挑战。 36 | 37 | - Facebook Engineering Blog:Facebook的工程团队在他们的技术博客上分享了很多关于移动端开发的技术文章,包括复杂页面的架构设计和技术创新。 38 | 39 | # Q&A 40 | 41 | 1. 国外复杂页面App,页面是如何设计的。也是插件化吗 42 | 2. 关于插件间通信,如果插件A就是想执行其他插件的逻辑怎么办? 43 | 3. 插件之间通信需要传递数据时,如何避免特定业务Model的依赖 44 | 4. 优酷文章中,关于如何管理数据方面感觉没讲清楚,可能需要涉及更多数据、持久化层只是 45 | 5. 优酷文章中,关于模块、功能单元等概念也没有理解很清楚 46 | 47 | 48 | 49 | 50 | ## 参考 51 | 52 | - [为什么这么设计系列文章](https://draveness.me/whys-the-design/) 53 | - [优酷 iOS 插件化页面架构方案](https://www.infoq.cn/article/ejkw6sz5qouuhxgag5vy) 54 | - [iOS 上的插件化设计](http://zenonhuang.me/2021/07/01/technology/2021-4-22-plugin/) 55 | - [抖音 iOS 最复杂功能的重构之路 -- 播放器交互区重构实践](https://mp.weixin.qq.com/s/ZmF5w3zzpqJb7AiBWGJUvA) 56 | - [Shield——开源的移动端页面模块化开发框架](https://tech.meituan.com/2017/12/28/shield-opensource.html) 57 | - [移动客户端架构设计-01-iOS架构设计|综述](https://juejin.cn/post/7118599362036367391) 58 | - [移动客户端架构设计-02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】](https://juejin.cn/post/7221020355292233789) 59 | - [一字之差,MVP 比 MVC 强在哪里?](https://blog.ficowshen.com/page/post/82) 60 | -------------------------------------------------------------------------------- /architecture/读《iOS应用架构谈 本地持久化方案及动态部署》笔记.md: -------------------------------------------------------------------------------- 1 | # 读《iOS应用架构谈 本地持久化方案及动态部署》笔记 2 | 3 | 原文链接--[iOS应用架构谈 本地持久化方案及动态部署](https://casatwy.com/iosying-yong-jia-gou-tan-ben-di-chi-jiu-hua-fang-an-ji-dong-tai-bu-shu.html) 4 | 5 | 设计持久层时,需要注意做到几个隔离: 6 | 7 | - 持久层与业务层的隔离 8 | - 数据库读写隔离 9 | - 多线程控制导致的隔离 10 | - 数据表达和数据操作的隔离 11 | 12 | ## 胖Model vs 瘦Model 13 | 14 | "胖模型"和"瘦模型"是描述数据模型在业务逻辑处理中扮演角色的两种方式。 15 | 16 | "胖模型"(Fat Model)是指模型中包含大量的业务逻辑。优点是: 17 | 18 | 1. 代码重用:由于业务逻辑在模型中,所以可以在多个控制器或视图中重用。 19 | 2. 保持控制器和视图的简洁:将业务逻辑放在模型中可以使控制器和视图保持简洁,更易于维护。 20 | 21 | 缺点是: 22 | 23 | 1. 模型复杂度高:模型中包含大量的业务逻辑可能会使模型变得复杂,难以理解和维护。 24 | 2. 难以测试:由于业务逻辑和数据紧密耦合,可能会使单元测试变得困难。 25 | 26 | "瘦模型"(Thin Model)是指模型主要负责数据的存储和简单的数据操作,而将大部分业务逻辑放在服务或控制器中。优点是: 27 | 28 | 1. 模型简单:模型主要负责数据的存储和简单的数据操作,所以模型的复杂度较低,易于理解和维护。 29 | 2. 易于测试:由于业务逻辑和数据分离,可以更容易地进行单元测试。 30 | 31 | 缺点是: 32 | 33 | 1. 代码重用性低:由于业务逻辑在服务或控制器中,可能会导致代码重复。 34 | 2. 控制器和视图可能变得复杂:如果将大量的业务逻辑放在控制器或视图中,可能会使它们变得复杂,难以维护。 35 | 36 | 在实际开发中,应根据具体的项目需求和团队习惯来选择使用胖模型还是瘦模型。 37 | 38 | ## 持久层与业务层隔离 39 | 40 | > 作者倾向于去Model化,即使用通用的类型(比如字典)表示业务模型 41 | 42 | ## 数据库读写隔离 43 | 44 | 此处不是数据库读和写要隔离开,而是而是以某一条界限为准,在这个界限以外的所有数据模型,都是不可写不可修改,或者修改属性的行为不影响数据库中的数据 45 | 46 | 通常这个界限和业务层与持久层隔离的界限是保持一致的,即业务层从持久层拿到数据后的修改是不会更新到数据库的。这样做的目的是提高代码的可维护性,一个反例是Core Data的设计,任何业务层代码对NSManagedObject属性的修改在context执行save时都会更新到数据库,这使得一些问题很难调试 47 | 48 | ## 多线程导致的隔离 49 | 50 | ## 数据表达和数据操作的隔离 51 | 52 | -------------------------------------------------------------------------------- /dSYM in iOS.md: -------------------------------------------------------------------------------- 1 | # dSYM in iOS 2 | 3 | 如何查看一个dSYM文件中都包含了哪些代码的符号信息 4 | 5 | dwarfdump xx.dSYM 6 | 7 | 8 | 9 | - [Xcode中和symbols有关的几个设置](https://xushuangqing.github.io/post/Xcode-zhong-he-symbols-you-guan-de-ji-ge-she-zhi/) -------------------------------------------------------------------------------- /for-in in Objective-C-Swift.md: -------------------------------------------------------------------------------- 1 | # for-in in Objective-C/Swift 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | > 好久没写博客了,水一篇凑个数,解答几个困惑很久的问题 6 | 7 | ## for-in in Objective-C 8 | 9 | ``` 10 | NSArray *array = [NSArray arrayWithObjects: 11 | @"one", @"two", @"three", @"four", nil]; 12 | 13 | for (NSString *element in array) { 14 | NSLog(@"element: %@", element); 15 | } 16 | ``` 17 | 18 | ### 哪些类型可以使用`for-in`特性 19 | 20 | - Objective-C中,`for-in`特性叫做Fast Enumeration 21 | - 所有遵循`NSFastEnumeration`协议的类型,都能使用Fast Enumeration特性 22 | 23 | ### Fast Enumeration有哪些特性 24 | 25 | - 既然叫做快速枚举,那相比其他遍历方式,一般是比较快的 26 | - 语法简洁 27 | - 遍历集合过程中,不允许修改集合。保证安全 28 | 29 | 以下代码会运行出错:❌❌ 30 | 31 | ``` 32 | NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1", @"2", nil]; 33 | for (NSString *str in array) { 34 | if ([str isEqualToString:@"1"]) { 35 | [array removeObject:str]; 36 | } 37 | } 38 | ``` 39 | 40 | ### 遍历顺序 41 | 42 | 经常遇到的一个疑问是:使用Fast Enumeration时,是按照从前往后的顺序在遍历的吗? 43 | 44 | 官方的原话是这样: 45 | > For collections or enumerators that have a well-defined order—such as an NSArray or an NSEnumerator instance derived from an array—the enumeration proceeds in that order, so simply counting iterations gives you the proper index into the collection if you need it. 46 | 47 | ``` 48 | NSArray *array = <#Get an array#>; 49 | NSUInteger index = 0; 50 | 51 | for (id element in array) { 52 | NSLog(@"Element at index %u is: %@", index, element); 53 | index++; 54 | } 55 | ``` 56 | 57 | 总结下 58 | 59 | - 对于NSArray使用Fast Enumeration时,是按照从前到后的顺序遍历的 60 | - 对于其他类型的集合(如NSDictionary),顺序是不确定的 61 | 62 | ### 参考 63 | - [Fast Enumeration](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocFastEnumeration.html) 64 | - [NSFastEnumeration](https://developer.apple.com/documentation/foundation/nsfastenumeration) 65 | - [iOS 中集合遍历方法的比较和技巧](https://blog.sunnyxx.com/2014/04/30/ios_iterator/) 66 | 67 | ## for-in in Swift 68 | 69 | ``` 70 | let array = [1,2,3] 71 | for value in array { 72 | } 73 | 74 | for (index, value) in array.enumerated() { 75 | } 76 | 77 | for i in 0..3 { 78 | } 79 | 80 | for i in stride(from: 10, to: 0, by: -1) { 81 | } 82 | ``` 83 | 84 | ### 为什么可以使用for-in语法 85 | 86 | 通过上面的例子可见,Swift中的for-in更丰富 87 | 88 | - 所有遵循`Sequence`的类型,都能用for-in语法遍历数据 89 | - 除了常见的`Array`、`Dictionary`,还有`EnumeratedSequence`、`StrideTo`等很多类型 90 | - 能够通过`Sequence`遍历的关键在于,conforming type需要实现`func makeIterator() -> Self.Iterator`方法 91 | - 该方法也决定了遍历元素的顺序 92 | 93 | ### 在对Sequence使用for-in时,需注意什么 94 | 95 | > The Sequence protocol makes no requirement on conforming types regarding whether they will be destructively consumed by iteration. As a consequence, don’t assume that multiple for-in loops on a sequence will either resume iteration or restart from the beginning 96 | 97 | Sequence协议并没有要求conforming type在遍历元素时 98 | 99 | - 不能修改序列中的元素 100 | - 遍历结束后,游标自动回到初始位置,即可以进行多次相同的遍历操作 101 | 102 | ### Collection 103 | 104 | `Collection`协议继承自`Sequence` 105 | 106 | 像Array、Dictionary都实现了`Collection`协议 107 | 108 | - `Collection`规定,遍历过程中不能向集合删除、添加元素 109 | 110 | ### 为什么一边遍历Array(Dictionary)一边删除不会报错 111 | 112 | ``` 113 | var array = [1,2,3,4] 114 | for i in array { 115 | array.remove(at: 0) 116 | } 117 | ``` 118 | 119 | 不会报错,岂不是和`Collection`协议的规定冲突了么? 120 | 121 | - 不会报错其实是因为Array(Dictionary)是值类型,for循环中进行remove操作时会触发copy-on-write 122 | - 其实在遍历的过程中有两个Array,遍历的Array元素不会删除,真正删除元素是在另一个Array中 123 | 124 | ### for-in遍历顺序 125 | 126 | 前面提到了,决定for-in遍历顺序的是`func makeIterator() -> Self.Iterator`方法 127 | 128 | 像Array、Dictionary等Collection默认都是生成一个`IndexingIterator` 129 | 130 | 该iterator,就是按照C`=ollection的indices,进行遍历的 131 | 132 | 通过查看Collection协议的indices方法,默认实现是按照升序的 133 | 134 | 所以 135 | 136 | Array通过for-in遍历时,也是按照从前往后的顺序遍历 137 | 138 | ### 参考 139 | - [Collection](https://developer.apple.com/documentation/swift/collection) 140 | - [Remove element from collection during iteration with forEach](https://stackoverflow.com/questions/37997465/remove-element-from-collection-during-iteration-with-foreach) -------------------------------------------------------------------------------- /iOS Test Note.md: -------------------------------------------------------------------------------- 1 | # iOS Test笔记 2 | 3 | Criteria 4 | 5 | - Fast: Tests should run quickly. 6 | - Independent/Isolated: Tests shouldn’t share state with each other. 7 | - Repeatable: You should obtain the same results every time you run a test. External data providers or concurrency issues could cause intermittent failures. 8 | - Self-validating: Tests should be fully automated. The output should be either “pass” or “fail”, rather than relying on a programmer’s interpretation of a log file. 9 | - Timely: Ideally, you should write your tests before writing the production code they test. This is known as test-driven development. 10 | 11 | ## Terms 12 | 13 | sut: System Under Test,待测系统 14 | 15 | 16 | ## UI Test 17 | 18 | UI testing lets you test interactions with the user interface. UI testing works by finding an app’s UI objects with queries, synthesizing events and then sending the events to those objects. The API enables you to examine a UI object’s properties and state to compare them against the expected state. 19 | 20 | ``` 21 | // given 22 | let slideButton = app.segmentedControls.buttons["Slide"] 23 | let typeButton = app.segmentedControls.buttons["Type"] 24 | let slideLabel = app.staticTexts["Get as close as you can to: "] 25 | let typeLabel = app.staticTexts["Guess where the slider is: "] 26 | // then 27 | if slideButton.isSelected { 28 | XCTAssertTrue(slideLabel.exists) 29 | XCTAssertFalse(typeLabel.exists) 30 | 31 | typeButton.tap() 32 | XCTAssertTrue(typeLabel.exists) 33 | XCTAssertFalse(slideLabel.exists) 34 | } 35 | ``` 36 | 37 | ## Testing Performance 38 | A performance test takes a block of code that you want to evaluate and runs it ten times, collecting the average execution time and the standard deviation for the runs. The averaging of these individual measurements form a value for the test run that can then be compared against a baseline to evaluate success or failure. 39 | 40 | > Baselines are stored per device configuration, so you can have the same test executing on several different devices. Each can maintain a different baseline dependent upon the specific configuration’s processor speed, memory, etc. 41 | 42 | ``` 43 | func testScoreIsComputedPerformance() { 44 | measure( 45 | metrics: [ 46 | XCTClockMetric(), 47 | XCTCPUMetric(), 48 | XCTStorageMetric(), 49 | XCTMemoryMetric() 50 | ] 51 | ) { 52 | sut.check(guess: 100) 53 | } 54 | } 55 | ``` 56 | 57 | ## QA 58 | 59 | 1. what is code coverage tool 60 | 2. “Fake interactions with library or system objects by using stubs and mocks”,what is stubs? 61 | 62 | ## 参考 63 | - [What is @testable?](https://medium.com/@ani.sam2015/what-is-testable-c26ee882ada4) 64 | - [iOS Unit Testing and UI Testing Tutorial](https://www.raywenderlich.com/21020457-ios-unit-testing-and-ui-testing-tutorial) -------------------------------------------------------------------------------- /iOS之自定义UICollectionViewLayout.md: -------------------------------------------------------------------------------- 1 | # iOS之自定义UICollectionViewLayout 2 | 3 | ## Core Layout Process 4 | 5 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/customcollectionviewlayout_coreprocess.png?raw=true) 6 | 7 | UICollectionView进行布局时需要借助UICollectionViewLayout,两者交互的过程大致分三个阶段: 8 | 9 | 1. 首先执行UICollectionViewLayout的`prepare`方法,做数据准备 10 | 2. UICollectionView通过UICollectionViewLayout 的`collectionViewContentSize`属性或方法获取滚动视图的内容尺寸 11 | 3. UICollectionView通过UICollectionViewLayout的`layoutAttributesForElementsInRect: `方法获取正在展示中的cell的样式等信息 12 | 13 | ## 如何自定义布局 14 | 15 | 通过重写上面三阶段不同的方法,我们可以自定义布局 16 | 17 | - prepare阶段,我们可以做一些数据缓存工作,方便后序使用 18 | - 比较关键的是后序layoutAttributesForElementsInRect阶段 19 | - 该阶段我们可能根据需要为cell、supplementaryView、decorationView创建不同的layoutAttribute对象 20 | - 通常的做法可能是,通过datasource遍历寻找那些需要调整样式的cell(比如可视区域内的cell),为每一个cell创建一个layoutAttribute,放入数组作为结果返回 21 | - 因为layoutAttributesForElementsInRect可能会频繁执行,所以要考虑性能问题 22 | - `layoutAttributesForElementsInRect:`是针对多个元素创建laoutAttrbute,还要考虑可能也要为单个的item提供layoutAttribute的情况,就是类似`layoutAttributesForItemAtIndexPath:`这一类的方法 23 | 24 | ## 自定义布局可以做哪些事 25 | 26 | - 可以看一下`UICollectionViewLayoutAttributes`中支持哪些属性,比如zIndex、transform 27 | - 可以自定义插入、删除动画 28 | - 瀑布流效果,在社交软件中很常用 29 | - 通过创建`UICollectionViewLayoutAttributes`的子类+增加自定义背景色属性,为collectionView添加sectionBacgroundColor 30 | 31 | 32 | ## 参考 33 | 34 | - [Creating Custom Layouts](https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CreatingCustomLayouts/CreatingCustomLayouts.html#//apple_ref/doc/uid/TP40012334-CH5-SW1) -------------------------------------------------------------------------------- /iOS事件响应简单综述.md: -------------------------------------------------------------------------------- 1 | 本文算是一个对iOS中事件响应的一个简单“综述”,方便快速查询相关资料 2 | 3 | 4 | - iOS事件有三种 5 | - 触摸事件(touch events) 6 | - 加速计事件(motion events) 7 | - 远程控制事件(remote control events) 8 | - `UIResponder`是一个抽象类,只有继承了`UIResponse`类之后,才能处理上面的事件 9 | - `UIView`、`UIApplication`、`UIViewController`都是它的子类 10 | - `UIEvent`表示一个事件 11 | - `UITouch`表示触摸事件中的一个触摸对象(多个手指触摸时就有多个对象) 12 | 13 | 14 | ## 触摸事件响应过程 15 | 16 | > 注意,这里说的只是触摸事件 17 | 18 | 1. 系统产生触摸对象,通过`UIApplication`、`UIWindow`等上层对象发出 19 | 2. 通过hitTest过程,找到first responder,确认了事件responder chain(响应链) 20 | 3. 然后两条路一起进行事件响应的处理,在默认情况下 21 | - 响应链上的手势识别器优先于first responder收到`touchBegan`等消息,优先判定是否可以识别手势 22 | - first responder也会收到`touchBegan`消息 23 | - 但如果响应链上的手势识别器识别成功了,first responder则会收到`touchcancel`消息,first responder的响应链也会因此中断 24 | - 如果手势识别未成功,则不会中断响应链 25 | 26 | ## 手势识别器 27 | 28 | - 手势识别器有两种:discrete 和 continuous 29 | - 两种手势识别器的状态变化不同 30 | 31 | 系统内置的手势识别器按照类型进行划分如下: 32 | 33 | **discrete gesture recognizer** 34 | 35 | - UITapGestureRecognizer 36 | - UISwipeGestureRecognizer 37 | - UIScreenEdgePanGestureRecognizer 38 | 39 | **continuous gesture recognizer** 40 | 41 | - UIPanGestureRecognizer 42 | - UIRotationGestureRecognizer 43 | - UIPinchGestureRecognizer 44 | 45 | ### state of discrete gesture recognizer 46 | 47 | ![state of discrete gesture recognizer](https://docs-assets.developer.apple.com/published/7c21d852b9/9ce946b4-9661-4a40-86bc-2f78abf3a8b1.png) 48 | 49 | 50 | ### state of continuous gesture recognizer 51 | 52 | ![](https://docs-assets.developer.apple.com/published/7c21d852b9/86fa3739-c97b-44cc-b51d-0215697660b7.png) 53 | 54 | ## 一点心得 55 | 56 | 关于responder chain与gesture recognizer之间的协同工作原理,官方并没有详细说明,目前网上的文章主要是以代码测试结果为依据。这里强烈推荐参考文章中的第一篇 57 | 58 | 所以 59 | 60 | 可能实际项目中仍会遇到一些很难解的问题 61 | 62 | ## 未解难题 63 | 1. 向tableview中添加一个自定义view,view.userInteractionEnable = YES;此时,被view遮盖住的cell,无法点击,但是在view区域拖动,tableview却可以跟随滚动。为什么? 64 | 65 | ## 参考 66 | - [iOS | 响应链及手势识别](https://juejin.cn/post/6905914367171100680) 67 | - [About the Gesture Recognizer State Machine](https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/implementing_a_custom_gesture_recognizer/about_the_gesture_recognizer_state_machine?language=objc) -------------------------------------------------------------------------------- /iOS包体积优化笔记.md: -------------------------------------------------------------------------------- 1 | # iOS包体积优化笔记 2 | 3 | 4 | ## 如何得到包体积大小 5 | 6 | ### 苹果官方建议 7 | 8 | > 该部分内容基本是翻译了参考中官方文档 9 | 10 | 首先,以下几种查看大小的方式都是不准确,且官方不建议的: 11 | 12 | - Xcode编译结束后的App bundle 13 | - 通过Xcode archive后的包 14 | - 上传到App Store Connect上的IPA文件 15 | 16 | 之所以上面的包不准确, 17 | 18 | - 一方面因为包中可能包含了一些最终不会出现在用户设备上的资源如DSYM和图片等; 19 | - 另一方面,相比于上传到App Store Connect的文件,最终App Store中的文件大小可能会增加,苹果可能会增加一些防盗版、压缩等处理 20 | 21 | 苹果官方建议的获取包大小的方案有: 22 | 23 | - Create an app size report 24 | - Gather infomation from App Store Connect 25 | 26 | ![](https://docs-assets.developer.apple.com/published/8f5ee51a35acc3bf93bf2fc7d0c55653/reducing-your-app-s-size-1@2x.png) 27 | 28 | - Create an app size report这一方法可以在开发电脑上,有Xcode就可以搞定的 29 | - 而且也可以通过命令的方式,将该过程自动化,或集成到持续集成中 30 | 31 | ![](https://docs-assets.developer.apple.com/published/18e290971c5b775a3c34264f44a972c6/doing-basic-optimization-to-reduce-your-app-s-size-1@2x.png) 32 | 33 | - file assets 34 | - using asset files instead of putting the data into your code 35 | - such as, use a property list for bundling any data with your app instead of using strings in code 36 | 37 | ![](https://docs-assets.developer.apple.com/published/b1a7494fa1ea9b2f09cd33a1f2d74702/doing-advanced-optimization-to-further-reduce-your-app-s-size-1@2x.png) 38 | 39 | - Reduce the size of app updates 40 | - 系统在从App Store中更新App时,不总是下载完整App而是会更新update package 41 | - 减少不必要的修改,会降低update package的大小 42 | - on-demand resoures 43 | - 将不常用或需购买使用的资源延迟提供 44 | - 既可以通过苹果提供的延迟提供机制,也可以通过自己的服务器延迟下载 45 | 46 | ### LinkMap 47 | 48 | 依赖Xcode链接程序时生成的LinkMap文件: 49 | 50 | - 其中描述可执行文件的构成 51 | - 根据其中的信息能够知道不同模块,不同类在可执行文件的大小 52 | 53 | ## 难点 54 | 55 | 1. 随着App业务变多,如何监控不同业务模块每次迭代带来的包体积增量(LinkMap) 56 | 2. 对于OC代码,一些代码调用是通过runtime,很难通过简单的静态扫描来甄别哪些类或资源没有使用 57 | 58 | ## 疑问 59 | 1. 何为png无损压缩? 60 | 2. 安装包超过150MB不能通过OTA下载,只能WIFI环境? 61 | - 我理解OTA跟WIFI不是对等且相反的概念吧? 62 | 3. WebP比PNG、JPG好在哪 63 | 4. HEIC比WebP好在哪 64 | 5. 官方文档中提到一个场景:对于用户引导这种feature,明显是不常用的,如何通过苹果官方的机制来延迟下载使用呢? 65 | 66 | ## 参考 67 | - [iOS微信安装包瘦身](https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207986417&idx=1&sn=77ea7d8e4f8ab7b59111e78c86ccfe66&scene=24&srcid=0921TTAXHGHWKqckEHTvGzoA#rd) 68 | - [Reducing your app’s size-Apple Document](https://developer.apple.com/documentation/xcode/reducing-your-app-s-size) 69 | - [10 | 包大小:如何从资源和代码层面实现全方位瘦身?](https://time.geekbang.org/column/article/88573) -------------------------------------------------------------------------------- /iOS开发高手课笔记/《iOS开发高手课》笔记-App启动速度如何优化与监控.md: -------------------------------------------------------------------------------- 1 | # 《iOS开发高手课》Note-App启动速度如何优化与监控 2 | 3 | 4 | ## Launch Process 5 | 6 | From the WWDC, there are three iOS launch types: Cold、Warm、Resume 7 | 8 | - Cold: App is not in memory. No App process exists. 9 | - Warm: App is partly in memory. No App process exists. 10 | - Resume: App is in memory. App process exists. 11 | 12 | > Launch Process below is referred to Cold Launch Process 13 | 14 | The Cold launch process can be divided into three parts in sequence: 15 | 16 | - pre-main 17 | - from main method to welcome page shown 18 | - after welcome page shown 19 | 20 | ### pre-main 21 | 22 | pre-main refers to the time period from the time when the user taps the App icon to the execution of `main` method 23 | 24 | - load executable file 25 | - load dynamic libraries 26 | - initialize runtime(Objective C and Swift) 27 | - `+load` method 28 | 29 | What can we do to optimize this part? 30 | 31 | - avoid linking unused frameworks 32 | - limit the number of dynamic library 33 | - Apple 34 | - avoid unecessary `+load` codes or move codes from `load` to `initialize` 35 | - `initialize` is called after `main` 36 | 37 | ### Main to Welcome Page Shown 38 | 39 | - system creates UIApplication, UIAppDelegate and call relative callback method, for example, `didFinishLaunching` 40 | - prepare welcome page data 41 | - display welcome page 42 | 43 | How to optimize? 44 | 45 | - only prepare for welcome page, avoid other tasks 46 | 47 | ### After welcome page 48 | 49 | The main task is initialization, such as initializing SDK, database, etc 50 | 51 | In order to optimize the time 52 | 53 | - defer these tasks as much as possible 54 | 55 | ## How to monitor or measure the time of launch 56 | 57 | - Time Profiler 58 | 59 | 60 | ## Q&A 61 | - 什么是动态库?启动时都要加载哪些动态库? 62 | - 如何合并动态库,合并就能提高速度了? 63 | 64 | ## References 65 | - [02 | App 启动速度怎么做优化与监控?](https://time.geekbang.org/column/article/85331) 66 | - [Optimizing App Launch](https://developer.apple.com/videos/play/wwdc2019/423/) 67 | - [grab 68 | / 69 | cocoapods-pod-merge](https://github.com/grab/cocoapods-pod-merge) 70 | - [iOS 性能优化 - TimeProfiler分析代码耗时](https://blog.csdn.net/Hello_Hwc/article/details/84311933) 71 | - [如何精确度量 iOS App 的启动时间](https://www.jianshu.com/p/c14987eee107) 72 | - [美团外卖iOS App冷启动治理](https://tech.meituan.com/2018/12/06/waimai-ios-optimizing-startup.html) -------------------------------------------------------------------------------- /iOS开发高手课笔记/《iOS开发高手课》笔记-列表.md: -------------------------------------------------------------------------------- 1 | # 《iOS开发高手课》笔记-列表 2 | 3 | 刚本课程的第一篇,就发现内容深度有点驾驭不了,上来就汇编语言。 4 | 5 | 所以先想列一下感兴趣的部分,排个序,按照顺序学习该课程 6 | 7 | - 04 | 项目大了人员多了,架构怎么设计更合理? 8 | - 09 | 无侵入的埋点方案如何实现? 9 | - 17 | 远超你想象的多线程的那些坑 10 | - 27 | 如何用 Flexbox 思路开发?跟自动布局比,Flexbox 好在哪? 11 | - 42 | iOS原生、大前端和Flutter分别是怎么渲染的? -------------------------------------------------------------------------------- /iOS开发高手课笔记/《iOS开发高手课》笔记之04 - 项目大了人员多了,架构怎么设计更合理?.md: -------------------------------------------------------------------------------- 1 | # 《iOS开发高手课》笔记之04 | 项目大了人员多了,架构怎么设计更合理? 2 | 3 | 4 | 5 | ## Q&A 6 | 1. 组件化,一般分为了协议式和中间者两种架构设计方案。What is the 协议式方案 looks like? -------------------------------------------------------------------------------- /iOS思考之如何在遍历集合过程中删除元素.md: -------------------------------------------------------------------------------- 1 | # iOS思考之遍历集合过程中删除元素的几种方式 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | > 这里的集合不只是`Set`,还包含所有容器类型的数据结构,比如数组、字典,其实在iOS中统称为`collection`即集合 6 | 7 | > 我发现有时遇到该问题时虽然知道怎么解,但还是有点困惑,可能还是理解的不到位,索性这次将所有方案列出来,做一下分析,也算是个备忘 8 | 9 | 首先,遍历集合过程中并不是只有删除元素会出问题,**增加、替换**仍会出错,通过大致猜测一下内部实现(比如有个游标,随着遍历一直在更新)很容易能理解 10 | 11 | 但遍历过程中更新元素内部的内容是没问题的 12 | 13 | ## copy collection 14 | 15 | ``` 16 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 17 | dict[@"1"] = @1; 18 | dict[@"2"] = @2; 19 | NSMutableDictionary *copyDict = [dict mutableCopy]; 20 | for (NSString *key in copyDict) { 21 | dict[key] = nil; 22 | } 23 | NSLog(@"%@", dict); 24 | ``` 25 | 26 | ## 先记录后删除 27 | 28 | ``` 29 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 30 | dict[@"1"] = @1; 31 | dict[@"2"] = @2; 32 | NSMutableArray *keysToDelete = [NSMutableArray array]; 33 | for (NSString *key in dict) { 34 | if ([key isEqualToString:@"2"]) { 35 | [keysToDelete addObject:key]; 36 | } 37 | } 38 | [dict removeObjectsForKeys:keysToDelete]; 39 | NSLog(@"%@", dict); 40 | ``` 41 | 42 | ## 倒序遍历+删除 43 | 44 | ``` 45 | NSMutableArray *array = [NSMutableArray array]; 46 | [array addObject:@1]; 47 | [array addObject:@2]; 48 | [array addObject:@3]; 49 | 50 | [array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 51 | if ([obj isEqual:@1]) { 52 | [array removeObject:obj]; 53 | } 54 | }]; 55 | NSLog(@"%@", array); 56 | ``` 57 | 58 | - 不推荐这种方式 59 | - 首先,倒序遍历也是遍历,遍历过程中的增删替换的风险仍存在,或者说背后的实现苹果并没有明确告诉我们不会出问题,虽然现在来看确实没问题 60 | - 另外,倒序遍历仅适合数组,字典和集合没有类似方法 61 | 62 | ## 参考 63 | 64 | - [Best way to remove from NSMutableArray while iterating?](https://stackoverflow.com/questions/111866/best-way-to-remove-from-nsmutablearray-while-iterating) 65 | - [iOS 中集合遍历方法的比较和技巧](https://blog.sunnyxx.com/2014/04/30/ios_iterator/) -------------------------------------------------------------------------------- /iOS电池电量优化.md: -------------------------------------------------------------------------------- 1 | # iOS电池电量优化 2 | 3 | 1. 如何判定一个App电量使用上存在问题,看什么指标,指标阈值是啥? 4 | 2. 当发现问题时,如何快速定位问题 5 | 3. 日常开发中有哪些可以关注,以减少电量消耗的措施 6 | 7 | ## 参考 8 | - [Energy Efficiency Guide for iOS Apps](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/index.html) 9 | - [WWDC Videos](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/WWDCVideos.html) 10 | - Writing Energy Efficient Apps--WWDC 2017 session 238 11 | - [18 | 怎么减少 App 电量消耗?](https://time.geekbang.org/column/article/90874) -------------------------------------------------------------------------------- /iOS笔记.md: -------------------------------------------------------------------------------- 1 | # iOS笔记 2 | 3 | 记录简单了解类型的知识点 4 | 5 | ## 热修复 6 | 7 | ### Native Language 8 | 9 | 早期iOS热修复框架,JSPatch比较有名。原理如下: 10 | 11 | - 依赖Objective C runtime特性 12 | - 服务端将要热修复的JavaScript补丁包下发到客户端 13 | - 客户端通过iOS系统内置的JavaScriptCore执行补丁包代码,利用runtime特性动态创建类、替换、修改某个方法的实现,以达到热修复支持 14 | 15 | 缺点有: 16 | 17 | - 后序JSPatch由于未知原因可能导致上架审核无法通过 18 | - 必须依赖Objective C的runtime特性。无法直接热修复Swift、C、C++代码 19 | 20 | 后来,也出现了可以支持对纯Swift App的热修复方案:[Rollout](https://www.cloudbees.com/)和[SOT](https://www.sotvm.com/) 21 | 22 | - SOT支持Swift、OC、C、C++的热修复,但收费,且不直到底层的实现原理 23 | - Rollout仅支持Swift、OC的热修复 24 | - 根据其官方介绍和自己的猜测,其热修复原理大致是这样 25 | - 接入Rollout SDK后,根据配置信息,在App编译、链接过程中就会通过解析AST的方式在代码中(如某个方法)插入一些通用热修复代码 26 | - 这些热修复代码作用就是拉取相应的JavaScript代码,并执行 27 | 28 | #### 参考 29 | - [深入理解 iOS 热修复原理](https://www.jianshu.com/p/399f4a1212e9) 30 | - [移动端热更新方案(iOS+Android)](https://www.jianshu.com/p/739c5c5160f1) 31 | - [Rollout Swift Support - Under The Hood](https://www.cloudbees.com/blog/swift-method-swizzling) -------------------------------------------------------------------------------- /iOS编译过程.md: -------------------------------------------------------------------------------- 1 | # iOS编译过程 2 | 3 | 分前端和后端,前端编译器是Clang,后端是LLVM 4 | 5 | - Clang是LVVM项目的一部分,都是由苹果开发 6 | - 前端的编译工作是生成与机器无关的中间代码 7 | - 后端的工作是对代码进行优化,根据不同架构产生不同机器码 8 | - 如果要让编译器支持更多的语言,替换前端即可;若要增加支持的机器架构,替换后端即可 9 | 10 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/iOS_build_link.jpg?raw=true) 11 | 12 | ## 前端编译过程 13 | 14 | ### 预处理 15 | 16 | 预处理主要工作是一些替换工作,比如宏替换、头文件引入替换、条件编译指令(#if等) 17 | 18 | ### 词法分析(lexical anaysis) 19 | 20 | 对代码进行切词 21 | 22 | ### 语法分析(semantic analysis) 23 | 24 | 构建抽象语法树,对语法正确性进行验证,比如是否缺少括号 25 | 26 | ### 语义分析 27 | 28 | 语法分析只能根据语法树验证简单语法错误,具体的语义错误无法确认,比如类型检查,发送了一个未知的消息 29 | 30 | ### CodeGen 31 | 32 | 生成中间代码IR(intermediate representation) 33 | 34 | ARC代码的插入、Property的自动合成在这一步进行 35 | 36 | ## 后端 37 | 38 | ### 代码优化 39 | 40 | 删除多余指令等 41 | 42 | ### 汇编(assemble) 43 | 44 | 经过汇编器,机器可以运行的机器码,产生`.o`目标文件 45 | 46 | ### 链接(link) 47 | 48 | 将所有目标文件和库文件链接,合并成可执行文件(Mach-O) 49 | 50 | ### 签名 51 | 52 | ## 运行 53 | 54 | ## 参考 55 | - [点击 Run 之后发生了什么?](https://www.jianshu.com/p/d5cf01424e92) 56 | - [iOS 编译知识小结](https://xiaozhuanlan.com/topic/2675849103) -------------------------------------------------------------------------------- /iOS难点攻关.md: -------------------------------------------------------------------------------- 1 | # iOS难点攻关 2 | 3 | ## 长期 4 | 1. Swift5 vs Swift4 5 | 6 | ## Core Animation 7 | 8 | ## Swift 9 | [Cocoa Design Patterns](https://developer.apple.com/documentation/swift/cocoa_design_patterns) 10 | 11 | ## float 12 | nan、inifinity 13 | ## webview中 14 | wkwebview 15 | ## cell嵌套使用 16 | 17 | autolayout布局有错误 18 | 19 | ## 线程安全问题 20 | 21 | - queue 22 | - synchronize 23 | - nsthread 24 | 25 | - [Concurrency Programming Guide](https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091-CH1-SW1) 26 | 27 | ## URLSession 28 | 29 | ## when to use struct 30 | 31 | ## Git 32 | - head、ref、config等基础概念的理解 33 | 34 | ## translation in gesture 35 | 36 | ## Property in OC 37 | 38 | Typically you should specify accessor method names that are key-value coding compliant (see Key-Value Coding Programming Guide)—a common reason for using the getter decorator is to adhere to the isPropertyName convention for Boolean values. 39 | 40 | - 头文件中的`@property`只能说明在**头文件**中声明了setter和getter两个或其中的一个方法。.m文件中到底有没有是不确定的 41 | 42 | - 为啥按照这个格式写`ClassName * qualifier variableName;` 43 | - 当在attribute中写(getter=isWhat)时,使用点语法时,有哪些可能 44 | - Core Foundation属性有什么注意事项 45 | 46 | [Declared Properties](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW1) 47 | 48 | ## Core Foundation 49 | 50 | 用的不多,理解不够 51 | 52 | - Note: If you install a custom callback on a Core Foundation collection you are using, including a NULL callback, its memory management behavior is undefined when accessed from Objective-C. 53 | - Casting and Object Lifetime Semantics 这一节没看完 54 | 55 | ### Core Foundation Memory Management 56 | 57 | - 内存管理原则和Foundation类似 58 | - 当方法名中有"Create"和"Copy"时,则retaincount + 1 59 | - 方法名中包含"Get"时,引用计数不变 60 | - 但是没有ARC,retain和release得自己写 61 | - 内存管理需要手动执行retain和release方法 62 | - 编译器无法知道CF对象的生命周期情况 63 | - 相比Foundation,Core Foundation缺少autorelease机制 64 | - 所以通常如果需要通过Get方法获取数据时,最好retain一把 65 | - 当将object当做参数传递时,方法中为了避免对象被释放,使用前最好也retain一把 66 | - 其实Foundation中也有类似问题,只是ARC下编译器会帮我们加上代码;MRR下,我们也是可以先retain一把的 67 | - 由于编译器不清楚CF对象的生命周期,而编译器又可以通过ARC管理Foundation对象的生命周期,那CF对象和Foundation对象相互cast时,需要指明内存管理标示 68 | - \_\_bridge,可以用在两种对象互相转换时,ownership所有权没有任何改变 69 | - Foundataion下创建的对象转到CF时,告知CF,CF并没有拥有对象的所有权。相当于CF中Get方法获取对象 70 | - 反之亦然 71 | 72 | ``` 73 | NSURL *url = [[NSURL alloc] initWithString:"http://fuck.com"]; 74 | CFURLRef urlRef = (__bridge CFURLRef)url; 75 | ``` 76 | url的所有权没有发生任何转义,还是ARC下管理,所以无需执行CFRelease释放urlRef 77 | - \_\_bridge\_retained或CFBridgingRetain,在Foundation对象转换到CF对象时使用,相当于CF在用create或copy方法获得一个对象,所以CF下要记得CFRelease 78 | ``` 79 | CFURLRef urlRef = (__bridge_retained CFURLRef)url; 80 | // do with urlRef 81 | CFRelease(urlRef); 82 | ``` 83 | urlRef拥有对url数据的所有权,所以要记得CFRelease(urlRef)释放 84 | 85 | - \_\_bridge_transfer或CFBridgingRelease,CF对象转为Foundation对象时使用,CF放弃了对象的所有权,无需通过CFRelease再次释放 86 | 87 | [Memory Management Programming Guide for Core Foundation](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html#//apple_ref/doc/uid/10000127i) 88 | 89 | 90 | [Core Foundation Design Concepts](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFDesignConcepts/CFDesignConcepts.html#//apple_ref/doc/uid/10000122-SW1) 91 | 92 | 93 | ## Sharing Access to keychain 94 | 95 | ## OC Runtime Programing Guide 96 | 97 | 98 | ## IndexSet 99 | 100 | ## window.level 101 | - top window dertimine statusbar appearence 102 | 103 | [window programing guide](https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/WindowAndScreenGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40012555-CH1-SW1) 104 | 105 | ## new feature in Swift5 106 | 107 | ## Toll-Free Bridging 108 | 109 | ## OutputStream 110 | 111 | ## CoreAnimation完整Guide及核心概念联系 112 | 113 | 1. layer的zPosition可以决定先展示哪个吗 114 | 2. 哪些animatable的属性 115 | 116 | ## xib、storyboard 117 | 118 | ## 短期 119 | ### NSCoding 120 | -------------------------------------------------------------------------------- /iPhone设备信息参考.md: -------------------------------------------------------------------------------- 1 | ### iPhone屏幕尺寸 2 | 3 | |机型|逻辑尺寸(point)|屏幕分辨率|scale factor|长宽比(近似值)|英寸| 4 | |:-:|:-:|:-:|:-:|:-:|:-:| 5 | |iPhonoe 4/4s|320*480|640*960|@2x|1.5|3.5| 6 | |iPhone 5/5c/5s/SE|320*568|640*1136|@2x|1.775(16:9)|4| 7 | |iPhone 6/6s/7/8|375*667|750*1334|@2x|1.779(16:9)|4.7| 8 | |iPhone 6plus/6s plus/7plus/8plus|414*736|1080*1920|@3x|1.777(16:9)|5.5| 9 | |iPhone X/XS|375*812|1125*2436|@3x|2.165|5.8| 10 | |iPhone XS Max/11 Pro Max|414*896|1242*2688|@3x|2.164|6.5| 11 | |iPhone XR/11|414*896|828*1792|@2x|2.164|6.1| 12 | |iPhone 11 Pro|375*812|1125*2436|@3x|2.165|5.8| 13 | 14 | 15 | ### 架构与机型 16 | 17 | |架构|机型|备注| 18 | |:-:|:-|:-:| 19 | |armv6|iPhone、iPhone 3G|| 20 | |armv7|iPhone 3GS、iPhone 4|| 21 | |armv7s|iPhone 5、iPhone 5C|| 22 | |arm64|iPhone 5S、iPhone 6、iPhone 6Plus|| 23 | 24 | ### 机型与内存 25 | 26 | |内存|机型| 27 | |:-:|:-:| 28 | |1G|5、5c、5s、6、6 plus| 29 | |2G|6s、6s plus、SE、7、8| 30 | |3G|7 plus、8 plus、X| 31 | 32 | ### 参考 33 | - [iOS Resolution](https://ios-resolution.com/) 34 | - [iPhone 机型比较](https://www.apple.com.cn/iphone/compare/) 35 | - [Device Screen Sizes and Orientations](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/) 36 | - [list of iPhones](https://www.theiphonewiki.com/wiki/List_of_iPhones) 37 | -------------------------------------------------------------------------------- /内存/Autoreleasepool in iOS.md: -------------------------------------------------------------------------------- 1 | # Autoreleasepool in iOS 2 | 3 | 之所以要写点关于Autoreleasepool的内容,其实来源于一次面试 4 | 5 | > 面试官:说一下Autoreleasepool的底层实现 6 | 7 | > 我:双向链表,每个节点存储对象引用,balabala 8 | 9 | > 面试官:每个节点占多大内存? 10 | 11 | > 我:。。。 12 | 13 | Autoreleasepool的实现在[NSObject.mm](https://opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm.auto.html)中,是开源的 14 | 15 | 但个人由于C++基础不好,且并无太大兴趣详细研究,于是认为: 16 | 17 | - 了解Autoreleasepool的使用场景和工作原理对日常工作更有帮助 18 | - 深挖Apple为何使用双链表、哨兵、4096byte的page来设计Autoreleasepool,比只是单纯知道这些内容更优意义,这考察的是架构、数据结构的能力。但Apple并没说为何这样设计 19 | 20 | ## 工作原理 21 | 22 | 站在高一点的角度来看Autoreleasepool,其实它只是iOS中内存管理中的组成部分之一,没有它有些工作是无法正常完成的 23 | 24 | 简单回顾iOS内存管理机制:关键就是Reference Count,大于0就不释放,等于0就释放 25 | 26 | 有的情况下,单靠系统提供的retain、release操作无法满足需要,典型案例是:通过一个方法获取一个对象 27 | 28 | ``` 29 | - (NSObject *)objectWithXXX:(params) { 30 | NSObject *obj = [NSObject new]; 31 | return obj; 32 | } 33 | ``` 34 | 35 | - 通过这种方式获取到的对象,开发者不知道是否应该由自己来控制该对象的内存(MRR时代),也不知道这个方法内部到底有没有retain过这个对象 36 | - 苹果官方早就想到这一点,所以就做好了约定了:alloc/init、copy产生的对象,其内部是做过retain操作的,需要调用者来负责释放掉它;其他方法产生的对象,调用者只管使用就好,不需要主动释放(延迟释放,即由Autoreleasepool来释放) 37 | 38 | 具体的释放规则很简洁: 39 | - 给对象发送autorelease消息时,加入到Autoreleasepool中 40 | - Autoreasepool销毁时,对其中的所有对象发送release消息释放对象内存 41 | 42 | ## 应用场景 43 | 当下的iOS编程领域,需要开发者主动创建Autoreleasepool的时机并不多,因为系统会在合适时机自动创建,如: 44 | 45 | - runloop开始时会创建,每次loop结束时会销毁。为的就是回收过程中产生的临时对象 46 | 47 | 除此之外,需要开发者主动使用的经典的应用场景就是`Reduce Peak Memory Footprint` 48 | 49 | 拿官方例子来说明下 50 | 51 | ``` 52 | for (NSURL *url in urls) { 53 | 54 | @autoreleasepool { 55 | NSError *error; 56 | NSString *fileContents = [NSString stringWithContentsOfURL:url 57 | encoding:NSUTF8StringEncoding error:&error]; 58 | /* Process the string, creating and autoreleasing more objects. */ 59 | } 60 | } 61 | ``` 62 | 63 | - for循环中可能要创建一些比较占内存的、临时的对象 64 | - 为了避免一次for-loop中汇集太多这样的对象导致内存吃紧,可以适时地使用Autoreleasepool 65 | 66 | ## 底层实现 67 | 68 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/autoreleasepool_linkedlist.jpg?raw=true) 69 | 70 | - autoreleasepool底层是有一个个的`AutoreleasePoolPage`结构体构成 71 | - 这些结构体通过`parent`和`child`建立一个**双向链表**结构 72 | 73 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/autoreleasepool_push.jpg?raw=true) 74 | 75 | - 上图展示了创建autoreleasepool时和在autoreleasepool执行`[obj autorelease]`的操作 76 | - 首先这两个操作最终的结果都是要在`AutoreleasePoolPage`中添加一个对象,每个page是有大小限制的,如果满了无法添加对象,则需要创建一个新的page,通过`parent`和`child`进行关联 77 | - 如果是创建autoreleasepool,则添加一个`boundary`,这是一个哨兵,表示一个新的自动释放池开始了 78 | - 如果是`[obj autorelease]`,则将`obj`加入其中 79 | - 其实看得出来,每个page内部添加对象时是一个栈结构,有一个`next`指针指向栈顶 80 | 81 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/autoreleasepool_pop.jpg?raw=true) 82 | 83 | - pop的时候没有太多可以讲的 84 | - 为每个对象发送`release`消息 85 | - 如果当前释放池中对象都没了,那就移除`boundary`哨兵对象 86 | 87 | 88 | # 参考 89 | 90 | - [AutoreleasePool底层实现原理](https://juejin.im/post/5b052282f265da0b7156a2aa) 91 | - [Using Autorelease Pool Blocks](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html#//apple_ref/doc/uid/20000047-CJBFBEDI) 92 | - [autoreleasepool uses in Swift](https://swiftrocks.com/autoreleasepool-in-swift) -------------------------------------------------------------------------------- /内存/Memory Leak in iOS.md: -------------------------------------------------------------------------------- 1 | # Memory Leak in iOS 2 | 3 | This article will list several memory leak senarios and respective solutions. 4 | 5 | ## Closure(Block) 6 | 7 | `capture` in Closure may cause a strong reference 8 | 9 | such as 10 | 11 | `objectA` -> `closure` -> `objectA` 12 | 13 | ## Timer 14 | 15 | - Runloop has a strong reference to timer until call `invalidate()` 16 | - Timer has a strong reference to target if use these methods `init(timeInterval ti: TimeInterval, 17 | target aTarget: Any, xxxx` 18 | - So it will cause a strong reference cycles 19 | 20 | `Runloop` -> `Timer` -> `Target` 21 | 22 | ## Delegation 23 | 24 | - A is the delegation of B, A also has a property(B type) 25 | - When the delegation property attribute of B is `strong`, it will cause strong reference cycle 26 | 27 | `Delegation` -> `B` -> `Delegation` 28 | 29 | ## NotificationCenter 30 | 31 | - when use the api below, it may cause strong reference cycle 32 | 33 | ``` 34 | func addObserver(forName name: NSNotification.Name?, 35 | object obj: Any?, 36 | queue: OperationQueue?, 37 | using block: @escaping (Notification) -> Void) -> NSObjectProtocol 38 | ``` 39 | - To avoid a retain cycle, use a weak reference to self inside the block when self contains the observer as a strong reference 40 | 41 | > when use `addObserver(observer:selector:xxx)`, 42 | If your app targets iOS 9.0 and later or macOS 10.11 and later, you do not need to unregister an observer that you created with this function. If you forget or are unable to remove an observer, the system cleans up the next time it would have posted to it. -------------------------------------------------------------------------------- /内存/OOM in iOS.md: -------------------------------------------------------------------------------- 1 | # Memory Usage Performance in iOS 2 | 3 | > Efficient memory management is an important aspect of writing high performance code in both OS X and iOS. In order to properly tune your code though, you need to understand something about how the underlying system manages memory. 4 | 5 | 6 | ### 提高内存使用效率 7 | 8 | 9 | ### SDWebImage中内存处理 10 | 11 | 12 | ## 参考 13 | - [14 | 临近 OOM,如何获取详细内存分配信息,分析内存问题?](https://time.geekbang.org/column/article/89845) 14 | - [Memory Usage Performance Guidelines](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/ManagingMemory.html#//apple_ref/doc/uid/10000160i) 15 | - [iOS 内存管理研究](https://zhuanlan.zhihu.com/p/49829766) 16 | - [iOS 性能优化实践:头条抖音如何实现 OOM 崩溃率下降 50%+](https://www.infoq.cn/article/ox7u3ymwiwzamt1vgm7m) -------------------------------------------------------------------------------- /内存/《WWDC 2024-- Analyze heap memory》学习笔记.md: -------------------------------------------------------------------------------- 1 | # 《WWDC2024 Session-Analyze heap memory》学习笔记 2 | 3 | 该Session主要对于堆上开辟的内存可能遇到的几种常见问题进行分析,同时介绍了一下可以使用的分析工具以及开发建议 4 | 5 | ## Transient growth 6 | 7 | 翻译为“瞬时的内存增长” 8 | 9 | 该文中给出了一个例子: 10 | 11 | - 一个for-loop(循环)代码,每次loop接收一个本地图片的url,读取图片数据,转为缩略图 12 | 13 | ![](https://cdn.jsdelivr.net/gh/songgeb/picx-images-hosting@master/20240930-120456.1sf0je5hd0.webp) 14 | 15 | 创建缩略图的部分代码如下所示: 16 | 17 | ![](https://cdn.jsdelivr.net/gh/songgeb/picx-images-hosting@master/20240930-120524.8l025uvori.webp) 18 | 19 | - 可见107行代码耗费了6.7G内存,内存占用过大 20 | - `render.faultThumbnail`中仅仅是使用了`PhotoThumbnail.image`即最终的缩略图结果 21 | 22 | 这样会带来什么问题? 23 | 24 | - 通过`makeThumbnail`的实现可以知道最终返回的`PhotoThumbnail`对象是要通过AutoreleasePool管理的 25 | - 所以对象的释放时机要等到AutoreleasePool释放时,至少不是在for-loop过程中 26 | - 所以在for-loop过程中将会产生多个内存占用很大的临时对象(`PhotoThumbnail`),进而持有着大量的比较耗内存的`imageData` 27 | - 于是就会出现瞬时内存占用的峰值,从Xcode内存可视化监控上来看如下图所示: 28 | 29 | ![](https://cdn.jsdelivr.net/gh/songgeb/picx-images-hosting@master/20240930-120549.2doo5ozxnd.webp) 30 | 31 | 通过主动添加`Autoreleasepool`方式优化后: 32 | 33 | ``` 34 | for loadThumnails(with render: ThumbnailRender) { 35 | for photoURL in urls { 36 | autoreleasepool { 37 | render.faultThumbnail(from: photoURL) 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | - 将`PhotoThumbnail`对象释放时机提前,效果如下: 44 | 45 | ![](https://cdn.jsdelivr.net/gh/songgeb/picx-images-hosting@master/20240930-120544.2rv3wk88ik.webp) 46 | 47 | ### 经验 48 | 49 | - 在编写loop代码时,要注意过程中是否会产生autoreleased的对象,导致瞬时内存增长 50 | 51 | ## Persistent growth 52 | 53 | 翻译为内存“持久的增长”,正如字面意思,app中可能会出现问题导致内存占用在一直增加。使得内存的走势呈现阶梯状 54 | 55 | 可以通过`Instrument`中的`Allocation`工具,通过打点(`Mark Generation`)方式,记录问题发生过程中,新开辟了哪些对象 56 | 57 | Session当中,Apple工作人员演示了一个有意思的事情: 58 | 59 | - 通过`Mark Generation`找到可疑对象 60 | - 通过对象地址,在Xcode Memory Graph中找到对应对象,分析其被引用关系,最终确定原因:缓存失效,导致每次加载缩略图都会将图片放入缓存,导致内存持久增长 61 | - 我发现Xcode Memory Graph中,选中对象后能够看到对应到代码中的属性名等具体信息,相比以前也丰富了 62 | 63 | ![](https://github.com/songgeb/picx-images-hosting/raw/master/20240930-173129.6pnhdk5w5w.webp) 64 | 65 | ## MallocStackLogging 66 | Apple介绍了Xcode的`MallocStackLogging`功能 67 | 68 | 在`Diagnostics`中开启`MallocStackLogging`后,在`Xcode Memory Graph`中便能看到每个对象alloc时的call stack 69 | 70 | ![](https://github.com/songgeb/picx-images-hosting/raw/master/20240930-172124.6pnhdjsyy2.webp) 71 | 72 | ## Memory leak 73 | 74 | 可以通过Xcode Memory Graph中检测到内存泄漏 75 | 76 | - Xcode Memory Graph无法检测所有的内存泄漏 77 | - 因为有些不确定类型不确定用途的指针引用是允许的 78 | - 比如使用Unsafe形式主动开辟的控件 79 | 80 | ## Perfermance 81 | 82 | 该小节列举几个可以降低内存占用的技巧 83 | 84 | - weak和unown的在内存占用和性能方面有所差异,可以根据情况选择 85 | - 定义类型时少用reference type、any、copy on write类型 86 | 87 | ![](https://github.com/songgeb/picx-images-hosting/raw/master/20240930-170351.6t73b91yvc.webp) 88 | 89 | ## 其他 90 | 91 | - 尽管在进行`Profile`时推荐使用Release环境+真机设备,但对于heap内存的调试,模拟器环境和真机也是比较接近的,可以使用模拟器代替 92 | 93 | ## 参考 94 | - [Analyze heap memory](https://developer.apple.com/videos/play/wwdc2024/10173/) -------------------------------------------------------------------------------- /原生Log框架 in iOS.md: -------------------------------------------------------------------------------- 1 | # 原生Log框架 in iOS 2 | 3 | - SwiftLog和OSLog区别 4 | - 各个框架能做哪些事情 5 | - 使用原生日志框架能否满足产品需求 6 | - os_log方法是哪个框架的 7 | 8 | 9 | ## 参考 10 | - [SwiftLog 和 OSLog:选择、使用以及坑](https://onevcat.com/2024/04/swift-log/) 11 | - [How to Create, Categorize and Filter iOS Logs](https://www.testdevlab.com/blog/how-to-create-categorize-and-filter-ios-logs) -------------------------------------------------------------------------------- /命令备忘.md: -------------------------------------------------------------------------------- 1 | # 命令备忘 2 | 3 | ## 代码量统计 4 | 5 | 推荐使用[cloc](https://github.com/AlDanial/cloc) 6 | 7 | 安装 8 | 9 | `brew install cloc` 10 | 11 | 统计某个目录中代码行数 12 | 13 | `cloc ./` 14 | 15 | 排除某些目录 16 | 17 | `cloc ./ --exclude-dir=Pods` 18 | 19 | 统计某个文件 20 | 21 | `cloc xxx` 22 | -------------------------------------------------------------------------------- /多线程/Synchronization Tools in iOS.md: -------------------------------------------------------------------------------- 1 | # Synchronization Tools in iOS 2 | 3 | 4 | 多线程中解决共享资源竞争的同步工具有lock, condition, atomic operation, etc. 5 | 6 | 锁是解决多线程安全问题的一个常见手段 7 | 8 | ## 自旋锁 9 | 10 | `OSSpinLock` 11 | 12 | - 特点是,当尝试获取锁失败时,线程并不会进入休眠,而是循环等待 13 | - 好处是,当锁释放时可以理解获取锁并执行逻辑,相比线程状态之间的切换要快些。这对于需要短时间等待的情况很高效 14 | - iOS 10之后,`OSSpinLock`被标记为废弃了。原因在下面优先级反转中有介绍 15 | 16 | ### 优先级反转 17 | 18 | 关于优先级反转,参考资料中《优先级反转那点事儿》讲的比较清晰。此处直接贴过来 19 | 20 | ![](https://pic3.zhimg.com/80/v2-39f5dabe2fdc9d411f654ab2fa86e94a_1440w.jpg) 21 | 22 | - 线程A在一个比较低的优先级上工作, 假设是10吧。然后在时间点T1的时候,线程A锁定了一把互斥锁,并开始操作互斥数据。 23 | - 这时有个高优线级线程C(比如优先级20)在时间点T2被唤醒,它也也需要操作互斥数据。当它加锁互斥锁时,因为互斥锁在T1被线程A锁掉了,所以线程C放弃CPU进入阻塞状态,而线程A得以占据CPU,继续执行。 24 | - 事情到这一步还是正确的,虽然优先级10的A线程看上去抢了优先级20的C线程的时间,但因为程序逻辑,C确实需要退出CPU等完成互斥数据操作后,才能获得CPU。 25 | - 但是,假设我们有个线程B在优先级15上,在T3时间点上醒了过来,因为他比当前执行的线程A优先级高,所以它会立即抢占CPU。而线程A被迫进入READY状态等待。 26 | - 一直到时间点T4,线程B放弃CPU,这时优先级10的线程A是唯一READY线程,它再次占据CPU继续执行,最后在T5解锁了互斥锁。 27 | - 在T5,线程A解锁的瞬间,线程C立即获取互斥锁,并在优先级20上等待CPU。因为它比线程A的优先级高,系统立刻调度线程C执行,而线程A再次进入READY状态。 28 | 29 | 上面这个时序里,线程B从T3到T4占据CPU运行的行为,就是事实上的优先级反转。一个优先级15的线程B,通过压制优线级10的线程A,而事实上导致高优先级线程C无法正确得到CPU。这段时间是不可控的,因为线程B可以长时间占据CPU(即使轮转时间片到时,线程A和B都处于可执行态,但是因为B的优先级高,它依然可以占据CPU),其结果就是高优先级线程C可能长时间无法得到 CPU。 30 | 31 | 32 | ### 优先级反转 vs 自旋锁 33 | 34 | - 优先级反转问题的出现跟自旋锁没有关系 35 | - 不使用自旋锁时也可能出现优先级反转问题。只要是线程或任务有多个优先级,理论上就可能有反转问题 36 | - 操作系统在优先级反转发生时通常都会有自动的解决方案,比如提高低优先级线程的优先级等 37 | - 在使用iOS中的`OSSpinLock`时 38 | - 由于这种锁不会记录持有它的线程信息,所有当发生优先级反转时,系统找不到低优先级的线程,可能因此无法通过提高优先级解决优先级反转问题 39 | - 再加上,高优先级线程使用自旋锁进行轮训等待锁时在一直占用CPU时间片,使得低优先级线程拿到时间片的概率降低 40 | - 总结下来是 41 | - 优先级反转问题的出现跟自旋锁没有关系 42 | - 但一旦出现优先级反转问题,自旋锁会让优先级反转问题不容易解决,甚至造成更严重的线程等待问题 43 | 44 | 45 | ### atomic和os\_unfair_lock 46 | 47 | - OSSpinLock被废弃后,官方建议使用os_unfair_lock代替; 48 | - os_unfair_lock其实是互斥锁(参考资料中有提到) 49 | - 在老版本中,atomic内部也是用自旋锁实现的,但后续也改成互斥锁了 50 | 51 | 52 | ### 疑惑 53 | 1. iOS系统中优先级反转问题是如何解决的?--参考资料中的苹果官方文档有提到 54 | 55 | 56 | ## 递归锁 57 | 58 | 递归锁允许同一个线程多次获取锁,但当前线程获取锁之后,其他线程就无法获得该锁了 59 | 60 | - `@synchronized(obj)` 61 | - `pthread_mutex` 62 | - `@synchronized底层使用pthread_mutex` 63 | - `NSRecursiveLock` 64 | 65 | ``` 66 | - (void)testLock{ 67 | if(_count>0){ 68 | @synchronized (obj) { 69 | _count = _count - 1; 70 | [self testLock]; 71 | } 72 | } 73 | } 74 | ``` 75 | 76 | - 上面使用递归锁,被锁住的代码递归的执行,不会导致死锁 77 | - 非递归锁则会导致死锁 78 | - `@synchronized(obj)`使用时要注意obj的地址不能是无效的,而且运行过程中不能改变 79 | 80 | ### 非递归锁 81 | 82 | `NSLock` 83 | 84 | ### 其他 85 | 86 | - `dispatch_semerphore`信号量值为1时也可以当做锁来用 87 | 88 | ## 疑问 89 | 1. iOS中锁是如何分类的,每种的特点、限制和应用场景是什么 90 | 2. 为何要有递归锁,什么场景下会用到递归锁 91 | 3. 为啥这么多种锁,不同的应用场景是啥 92 | 2. NSConditionLock、NSCondition、NSLock、NSRecursiveLock 93 | 94 | ### 参考 95 | - [About Threaded Programming](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/AboutThreads/AboutThreads.html#//apple_ref/doc/uid/10000057i-CH6-SW2) 96 | - [谈 iOS 的锁](http://zenonhuang.me/2018/03/08/technology/2018-03-01-LockForiOS/) 97 | - [优先级反转那点事儿](https://zhuanlan.zhihu.com/p/146132061) 98 | - [不再安全的 OSSpinLock](https://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios/) 99 | - [dispatch_semaphore 会造成优先级反转,慎用!](https://blog.51cto.com/u_15064655/2573045) 100 | - [Prioritize Work with Quality of Service Classes](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/PrioritizeWorkWithQoS.html#//apple_ref/doc/uid/TP40015243-CH39) 101 | - [ObjC 多线程简析(二)- os_unfair_lock的类型和自旋锁与互斥锁的比较](https://juejin.cn/post/6844903778328510471) 102 | - [iOS——GCD的死锁案例](https://cloud.tencent.com/developer/article/1198721) 103 | - 里面有几个死锁的demo 104 | - [Thread Safety in Swift](https://swiftrocks.com/thread-safety-in-swift) 105 | - [白夜追凶,揭开iOS锁的秘密](https://mp.weixin.qq.com/s?__biz=MzUyMDAxMjQ3Ng==&mid=2247489265&idx=1&sn=2de47017e2862817456c48df211787fa&source=41#wechat_redirect) -------------------------------------------------------------------------------- /多线程/iOS思考之控制并发任务数.md: -------------------------------------------------------------------------------- 1 | # iOS多线程题目之控制并发任务数 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | ## 题目描述 6 | 7 | 有这样一个有意思的题目,可能很多面试题中也考察到过: 8 | 9 | 10 | - 有一个比较耗时的处理数据的方法,比如叫做`func processData(data: Int)` 11 | - 数据可以是任意类型,此处使用Int来简化问题 12 | - 现在要求实现另一个批量处理数据的方法`func processDatas(data: [Int], completion: ([Int]) -> Void)` 13 | - 该方法异步处理数据,所有任务完成后通过completion进行回调 14 | - 要求同时处理的数据的任务数最多4个 15 | 16 | 本文尝试探索有哪些实现方式 17 | 18 | ## DispatchGroup+DispatchSemaphore 19 | 20 | 核心思想: 21 | 22 | - 通过DispatchGroup来控制多任务完成后才通过completion回调 23 | - 通过DispatchSemaphore来控制最大并发任务 24 | 25 | 代码思路: 26 | 27 | - 初始化 28 | - 创建并发队列queue,可以是自己创建的也可以是系统global的 29 | - 创建DispatchGroup对象group 30 | - 创建DispatchSemaphore对象semaphore,默认值是4 31 | - 异步方式向queue提交数据处理任务,数据处理任务如下 32 | - 遍历每个输入的任务数据,先执行group.enter,表示任务即将执行 33 | - 再信号量值是否满足要求(semaphore.wait())即先减一后判断是否大于等于0,大于等于0就继续执行,否则等待 34 | - 为了让4个任务同时执行,此处仍然要通过异步方式向queue中提交具体的任务内容 35 | - 任务结束后,执行semaphore.signal()和group.leave() 36 | - group.notify,任务结束后通过completion回调 37 | 38 | 代码实现如下 39 | 40 | ``` 41 | let semaphore = DispatchSemaphore(value: 4) 42 | let group = DispatchGroup() 43 | let queue = DispatchQueue(label: "queue", attributes: .concurrent) 44 | 45 | var results: [Int] = [] 46 | queue.async { 47 | for data in datas { 48 | group.enter() 49 | semaphore.wait() 50 | queue.async { 51 | let result = self.processData(data) 52 | results.append(result) 53 | semaphore.signal() 54 | group.leave() 55 | } 56 | } 57 | ``` 58 | 59 | ## OperationQueue 60 | 61 | OperationQueue支持配置任务并发数,所以核心要解决的事情是所有任务完成后如何回调 62 | 63 | 请看代码 64 | 65 | ``` 66 | var results: [Int] = [] 67 | let completionOperation = BlockOperation { 68 | completion(results) 69 | } 70 | let lock = NSLock() 71 | let operationQueue = OperationQueue() 72 | operationQueue.maxConcurrentOperationCount = 4 73 | for data in datas { 74 | let operation = BlockOperation { 75 | print("当前执行中任务为--\(data)") 76 | lock.lock() 77 | results.append(self.processData(data)) 78 | lock.unlock() 79 | } 80 | completionOperation.addDependency(operation) 81 | operationQueue.addOperation(operation) 82 | } 83 | operationQueue.addOperation(completionOperation) 84 | ``` 85 | 86 | ## 疑问 87 | 1. 上面两个代码中,OperationQueue方式中使用NSLock来保护results,但GCD的实现却没有,难道不会有线程安全问题吗 88 | 89 | ## 总结 90 | - 单纯就控制并发任务数来说的话可以考虑信号量和OperationQueue.maxConcurrentOperationCount 91 | - OperationQueue的思路更简单 92 | 93 | 94 | ## 参考 95 | - [Wait for all Operations in queue to finish before performing task](https://stackoverflow.com/questions/42495794/wait-for-all-operations-in-queue-to-finish-before-performing-task) 96 | - [iOS实录16:GCD小结之控制最大并发数](https://www.jianshu.com/p/5d51a367ed62) -------------------------------------------------------------------------------- /性能卡顿启动速度优化/Pre-main优化.md: -------------------------------------------------------------------------------- 1 | # Pre-main优化 2 | 3 | > 深入理解代替单纯记忆 4 | 5 | ## WWDC2016-406-Optimizing App Startup Time 6 | 7 | ### pre-main 8 | 9 | `main`方法之前都干了些什么事情,可以分为四步 10 | 11 | > 12 | 1. dylib loading 13 | 2. rebase/binding 14 | 3. ObjC setup 15 | 4. initializer 16 | 17 | - 内核将app和dyld加载内存 18 | - 第一步从dyld加载dylib(动态库)开始,Dyld(dynamic loader)dylibs从app的header文件,然后递归的找到所有依赖的dylibs,然后将他们加载到内存中 19 | - `Rebasing`和`Binding` 20 | - 是通过修改每个dylib内部区域,将dylib内部或与其他dylib依赖关系,建立连接 21 | - ObjC setup 22 | - 通知OC运行时,建立类名和类的全局映射表,更新类的实例变量信息、添加category的方法 23 | - initializer,执行`+load` 24 | - 所以`+load`方法在main之前就执行了 25 | - 但由于可能存在耗时任务影响启动速度,所以建议使用后执行的`+initializer`方法 26 | - `main`执行 27 | 28 | ### 优化方案 29 | 30 | 官方建议,启动时间应该控制在`400ms`以内 31 | 32 | 这个时间标准不止包含`pre-main`的时间,也包括了从`main`方法到`AppDelegate.didFinishLaunch`方法的时间 33 | 34 | - 通过`DYLD_PRINT_STATISTICS`和`DYLD_PRINT_STATISTICS_DETAILS`这两个环境变量可以查看pre-main用时 35 | 36 | > `main`到`didFinishLaunch`这个过程加载了`nib`资源等工作 37 | 38 | #### dylib loading 39 | 40 | - 减少使用dylibs 41 | - 可以合并多个功能类似的dylib 42 | 43 | #### rebasing/bingding 44 | 45 | - 可以通过`dyldinfo`查看绑定信息 46 | - 这一步中主要是在dylib的`__DATA`的区域增加依赖dylib调用的指针 47 | - 可以通过减少类数量来减少耗时 48 | - 减少C++虚函数使用,因为也需要类似上面的操作 49 | - 多使用Swift struct 50 | 51 | #### initializer 52 | 53 | - `+load`方法的操作移到`+initializer`中 54 | 55 | ## WWDC2017-413-App Startup Time:Past,Present,and Future 56 | 57 | 重写了dyld,从dyld2升级到dyld3 58 | 59 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/dyld3.png?raw=true) 60 | 61 | - 将一些耗时工作分离到了单独的守护进程(`dameon`)中去做 62 | - dyld2中这些工作是在启动过程中做的 63 | - 这些耗时工作的成果就是`launch closure`,并写入磁盘 64 | - 应用程序启动时直接拿来用,无需重复做 65 | - 对于系统应用,这些耗时工作会准备好数据并存入缓存中 66 | - 对于其他应用,在应用安装或更新时,这些准备工作会做好,启动时直接用 67 | - instruments新增了检测静态初始化方法的item-static initializer 68 | 69 | ## 参考 70 | - [Optimizing App Startup Time](https://developer.apple.com/videos/play/wwdc2016/406) 71 | - [App Startup Time: Past, Present, and Future](https://developer.apple.com/videos/play/wwdc2017/413) -------------------------------------------------------------------------------- /性能卡顿启动速度优化/《iOS开发高手课》笔记-App启动速度如何优化与监控.md: -------------------------------------------------------------------------------- 1 | # 《iOS开发高手课》Note-App启动速度如何优化与监控 2 | 3 | 4 | ## Launch Process 5 | 6 | From the WWDC, there are three iOS launch types: Cold、Warm、Resume 7 | 8 | - Cold: App is not in memory. No App process exists. 9 | - Warm: App is partly in memory. No App process exists. 10 | - Resume: App is in memory. App process exists. 11 | 12 | > Launch Process below is referred to Cold Launch Process 13 | 14 | The Cold launch process can be divided into three parts in sequence: 15 | 16 | - pre-main 17 | - from main method to welcome page shown 18 | - after welcome page shown 19 | 20 | ### pre-main 21 | 22 | pre-main refers to the time period from the time when the user taps the App icon to the execution of `main` method 23 | 24 | - load executable file 25 | - load dynamic libraries 26 | - initialize runtime(Objective C and Swift) 27 | - `+load` method 28 | 29 | What can we do to optimize this part? 30 | 31 | - avoid linking unused frameworks 32 | - limit the number of dynamic library 33 | - Apple 34 | - avoid unecessary `+load` codes or move codes from `load` to `initialize` 35 | - `initialize` is called after `main` 36 | 37 | ### Main to Welcome Page Shown 38 | 39 | - system creates UIApplication, UIAppDelegate and call relative callback method, for example, `didFinishLaunching` 40 | - prepare welcome page data 41 | - display welcome page 42 | 43 | How to optimize? 44 | 45 | - only prepare for welcome page, avoid other tasks 46 | 47 | ### After welcome page 48 | 49 | The main task is initialization, such as initializing SDK, database, etc 50 | 51 | In order to optimize the time 52 | 53 | - defer these tasks as much as possible 54 | 55 | ## How to monitor or measure the time of launch 56 | 57 | - Time Profiler 58 | 59 | 60 | ## Q&A 61 | - 什么是动态库?启动时都要加载哪些动态库? 62 | - 如何合并动态库,合并就能提高速度了? 63 | 64 | ## References 65 | - [02 | App 启动速度怎么做优化与监控?](https://time.geekbang.org/column/article/85331) 66 | - [Optimizing App Launch](https://developer.apple.com/videos/play/wwdc2019/423/) 67 | - [grab 68 | / 69 | cocoapods-pod-merge](https://github.com/grab/cocoapods-pod-merge) 70 | - [iOS 性能优化 - TimeProfiler分析代码耗时](https://blog.csdn.net/Hello_Hwc/article/details/84311933) 71 | - [如何精确度量 iOS App 的启动时间](https://www.jianshu.com/p/c14987eee107) 72 | - [美团外卖iOS App冷启动治理](https://tech.meituan.com/2018/12/06/waimai-ios-optimizing-startup.html) -------------------------------------------------------------------------------- /性能卡顿启动速度优化/移动端应用监控(APM)探索.md: -------------------------------------------------------------------------------- 1 | # 移动端应用监控体系探索 2 | 3 | ## 崩溃率 4 | 5 | 查阅了一些资料,崩溃率有几种表述形式 6 | 7 | - PV崩溃率 = 应用发生崩溃次数 / 启动次数 8 | - 来自友盟 9 | - crash users = 应用发生崩溃的用户数 / 总用户数 10 | - 来自Firebase Crashlytics 11 | - 友盟SDK中也称称为UV崩溃率 12 | 13 | > 也搜到更详细的崩溃指标,比如参考文档中《一款代码质量好iOS应用,其崩溃率应该在多少之下?》提到通过更多角度的统计数据,可以得到用户更精确的感受 14 | 15 | ### 崩溃率达到多少才算合格呢? 16 | 17 | 根据有限的查阅,将崩溃率保持在万分之十(千分之一)以内,是比较优秀的 18 | 19 | > 此处所说的崩溃率是指PV崩溃率 20 | 21 | ### 八卦 22 | 23 | - 游戏应用崩溃率要明显高于其他行业应用 24 | - 阅读类崩溃较少 25 | 26 | ### 参考 27 | - [Crashlytics 故障排除和常见问题解答](https://firebase.google.com/docs/crashlytics/troubleshooting?platform=android#cfu-calculation) 28 | - [扫盲贴|如何评价一款App的稳定性和质量?](https://info.umeng.com/detail?id=430&cateId=1) 29 | - [一款代码质量好的iOS应用,其崩溃率应该在多少之下?](https://www.zhihu.com/question/46919352/answer/105375897) 30 | 31 | ## 参考 32 | - [微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化](http://www.52im.net/thread-921-1-1.html) 33 | - [微信读书 iOS 质量保证及性能监控](http://wereadteam.github.io/2016/12/12/Monitor/) 34 | - [移动端 APM 性能监控](https://my.oschina.net/u/4582626/blog/4384997) 35 | - [Tencent/matrix](https://github.com/Tencent/matrix) -------------------------------------------------------------------------------- /技术成长.md: -------------------------------------------------------------------------------- 1 | # 技术成长 2 | 3 | ## iOS截图大小多大 4 | 5 | ### iOS有哪些截图方法 6 | 7 | ``` 8 | //1 绘制视图内容到当前context中 9 | UIGraphicsBeginImageContext(self.frame.size); 10 | [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; 11 | UIImage *image1 = UIGraphicsGetImageFromCurrentImageContext(); 12 | UIGraphicsEndImageContext(); 13 | 14 | //2 15 | UIView *snapshot = [self snapshotViewAfterScreenUpdates:YES]; 16 | 17 | //3 截图 18 | UIGraphicsBeginImageContext(self.frame.size); 19 | [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 20 | UIImage *image2 = UIGraphicsGetImageFromCurrentImageContext(); 21 | UIGraphicsEndImageContext(); 22 | ``` 23 | 24 | ### 如何衡量图片大小 25 | 26 | 其实如果对图片有一定的基础的话就知道 27 | 28 | - 图片由多个像素组成的像素矩阵构成 29 | - 每个像素点由RGBA四个颜色通道组成(有些情况并非四个通道,比如没有alpha通道,比如灰度图可能只有一个通道) 30 | - 每个通道的颜色值可以用0-255之间的数来表示 31 | - 所以每个通道的颜色值都占用一定的字节数,对于iOS来说每个通道占1个字节 32 | 33 | 那基于上面的认知,一张未压缩的图片的实际大小,就是所有像素点占用的字节数了 34 | 35 | - 当然像png、jpeg这种格式的图片其实都是压缩过的数据了,所以大小会小一些 36 | 37 | ## 38 | 39 | ## 如何自定义一个视图 40 | 41 | ### 布局时机 42 | 43 | - [iOS自定义View声明周期和设置布局约束的时机](http://blog.hudongdong.com/ios/1034.html) 44 | - [UIView系列之---如何写一个自定义View](http://hchong.net/2017/07/15/UIView%E7%B3%BB%E5%88%97%E4%B9%8B---%E5%A6%82%E4%BD%95%E5%86%99%E4%B8%80%E4%B8%AA%E8%87%AA%E5%AE%9A%E4%B9%89View/) 45 | 46 | ### 如何实现动态布局 -------------------------------------------------------------------------------- /正则表达式 in iOS.md: -------------------------------------------------------------------------------- 1 | # 正则表达式 in iOS 2 | 3 | - 正则表达式用`NSRegularExpression`表示 4 | - `NSRegularExpression(pattern: "id=\\(d.)", options: .caseInsensitive)` 5 | - 匹配的结果用`NSTextCheckingResult`表示 6 | - 包括匹配的结果的range信息 7 | - `range(at: index)`方法来获取每个group的信息 8 | 9 | ## 举例 10 | ``` 11 | //String string = http://www.xxx.com?id=10086&media=xxxx 12 | //获取上面网址中的id值 13 | do { 14 | let regularExp = try NSRegularExpression(pattern: "id=(\\d+)", options: .caseInsensitive) 15 | let firstResult = regularExp.firstMatch(in: string, options: [], range: NSRange(location: 0, length: string.count)) 16 | if let range = firstResult?.range(at: 1) { 17 | let meipaiFeedId = (string as NSString).substring(with: range) 18 | } 19 | } catch { 20 | printIfDebug(error) 21 | } 22 | ``` 23 | 24 | ## 参考 25 | - [nsregularexpression](https://developer.apple.com/documentation/foundation/nsregularexpression) -------------------------------------------------------------------------------- /源码阅读/AFNetworking源码笔记.md: -------------------------------------------------------------------------------- 1 | # AFNetworking源码笔记 2 | 3 | > 本文参照AFNetworking的4.0.1 4 | 5 | - 整体架构是怎样的 6 | - 主要解决什么问题,应用场景是什么 7 | - 重要的模块是怎样实现的 8 | - 有哪些优雅的技巧、设计 9 | 10 | ## Architecture 11 | 12 | 核心的类如下所示:(不是所有类) 13 | 14 | ![](https://github.com/songgeb/I-Love-iOS/blob/master/Images/AFNetworking_architecture.jpg?raw=true) 15 | 16 | - Objective C实现的网络请求库 17 | - 提供基于HTTP协议和其他数据协议(如ftp等)的网络数据请求支持 18 | - AFNetworking是对NSURLSession的高度封装,所以NSURLSession支持的所有能力也都支持,比如multipart数据上传、下载进度跟踪 19 | - AFNetworking将复杂的网络请求工作封装为一个个独立的API,只需要传递数据的URL和数据返回后进行处理的block即可 20 | - 提供了一个外部也可使用的网络监控的工具--AFNetworkReachabilityManager 21 | - 提供了几个UI层的扩展功能,监听网络状态 22 | - 更新status bar中ActivityIndicator 23 | - 更新UIRefreshControl 24 | - 完善的单元测试 25 | 26 | ## AFURLSessionManager 27 | 28 | - 框架最核心的类就是AFURLSssionManager,负责session、数据请求任务的创建、管理工作 29 | - AFURLSessionManager中持有一个session;同时为每个task创建了一个AFURLSessionManagerTaskDelegate对象,用于管理任务的回调事件 30 | 31 | ### AFURLSessionManagerTaskDelegate 32 | 33 | - 负责每一个NSURLSessionTask进度管理、任务结束回调工作 34 | - 其中,使用了两个NSProgress的实例来存储上传、下载进度,通过KVO NSProgress的属性监听进度的变更 35 | - 任务结束后都会回调到主线程中 36 | 37 | ## AFHTTPResponseSerializer 38 | 未完待续 39 | 40 | 41 | ## Good Practice 42 | 未完待续 43 | 44 | ## QA 45 | 1. AFHTTPSessionManager为何要遵循NSSecureCoding协议 46 | - 因为NSSecureCoding更安全,参考[NSSecure​Coding](https://nshipster.com/nssecurecoding/) 47 | 2. AFURLSessionManager中NSLock类型的lock干啥的? 48 | - AFURLSessionManager内部要用字典存储多个任务对应的taskdelegate,因为外部调用方可能在任何线程下使用该框架,所以lock是为了防止多线程写数据错误问题 49 | 3. 代码中发那么多通知干啥? 50 | - 在请求任务任何中断、完成、失败的情况下都会发送通知 51 | - 此举主要为了方便AFNetworking的调用方根据网络任务转台做一些工作,比如框架中UI层的扩展功能都是通过通知来完成 52 | 53 | ## References 54 | - [AFNetworking 概述(一)](https://github.com/draveness/analyze/blob/master/contents/AFNetworking/AFNetworking%20%E6%A6%82%E8%BF%B0%EF%BC%88%E4%B8%80%EF%BC%89.md) -------------------------------------------------------------------------------- /源码阅读/Masonry源码阅读笔记.md: -------------------------------------------------------------------------------- 1 | # Masonry源码阅读笔记 2 | 3 | > 本文参照[Masonry](https://github.com/SnapKit/Masonry)版本为1.1.0 4 | 5 | 读音:英 [ˈmeɪsənri],来自有道词典 6 | 7 | ## MASConstraintMaker 8 | 9 | - constraints,存放着向某视图添加的所有约束对象,对象类型为MASConstraint 10 | - 也是该框架工作的入口 11 | 12 | ``` 13 | [redView mas_makeConstraints:^(MASConstraintMaker *make) { 14 | make.top.equalTo(superview.mas_top) 15 | }]; 16 | ``` 17 | 18 | 上面语句会触发如下代码同步执行 19 | 20 | 1. 首先会创建一个MASConstraintMaker的对象,就是这里的make 21 | 2. make.top这一句,会产生一个MASViewConstraint对象constraint(或者MASCompositeConstraint对象),该对象就是约束中的first item(当然,对应的attribute为top) 22 | 3. 执行MASViewConstraint对象的equalTo方法,会创建对应的second item,同时将second item存入constraint中 23 | 4. `mas_make...`的block中所有语句执行完毕后,会产生多个constraint,make都会保存这些约束信息 24 | 5. 紧接着会执行MASConstraintMaker的install方法,install方法中会: 25 | - 对make中每一个constraint,执行install方法,即将约束添加到视图中 26 | - 如果有重复的约束,则会更新Constant 27 | 28 | ## MASViewAttribute 29 | 表示一条约束中某个item的attribute。 30 | 31 | first item和second item创建时都会创建对应的MASViewAttribute 32 | 33 | ## MASConstraint 34 | 一个抽象类,有两个子类:MASViewConstraint、MASCompositeConstraint 35 | 36 | - MASViewConstraint存储着一条约束所需的所有信息,如first item、second item、attributes、constant等 37 | - MASCompositeConstraint用于包装多个MASViewConstraint,比如`make.size.equalTo(CGSizeMake(10, 10));`,这里执行完`make.size`之后所产生的的constraint就是MASCompositeConstraint类型,包含了width和top两个constraint 38 | 39 | ## MASLayoutConstraint 40 | 41 | 系统类`NSLayoutConstraint`的子类,表示一条约束。 42 | ## 学到什么 43 | 44 | ### inset是怎样工作的 45 | 以前使用inset时总是不太明白 46 | 47 | - inset在Masnory会转成 { inset, inset, -inset, -inset }的UIEdgeInsets的数据类型 48 | - 而且仅会对first item's attribute起作用;并且只有它是top、left(leading)、bottom、right(trailing)时起作用 49 | 50 | ### 语言技巧 51 | 52 | 代码中有不少可以提高代码易用性、可读性、减少警告的技巧 53 | 54 | - equal是对mas_equal方法的宏定义 55 | - mas_equal()中的参数可以是多个类型 56 | - mas\_equalTo(40)、mas\_equalTo(view)、mas\_equalTo(view.mas_right) 57 | - 其实内部会将不同的值统一转为MASViewAttribute 58 | - __unused、noescape等attribute的使用 59 | 60 | ## 结语 61 | 62 | - 代码中包含较多的block调用,对可读性有一定影响,但由此带来的易用性(链式调用)很赞 63 | - 并无太多架构设计思想在其中,有一处抽象类的使用;使用分类(Category)比较多 64 | - 只要对Autolayout原理比较了解,还是比较容易读懂源码,尤其是一些专业名词,如first item、attribute、relation等 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /源码阅读/YYDispatchQueuePool源码笔记.md: -------------------------------------------------------------------------------- 1 | # YYDispatchQueuePool源码笔记 2 | 3 | 工具作者在实际开发中,由于开了很多线程去做异步绘制、下载等工作,而且有的线程可能因为资源锁等待的原因,可能导致开更多的线程。当线程过多时,线程占用了过多资源,可能导致主线程受影响,出现卡顿问题 4 | 5 | 而iOS框架中有最大并发数概念的目测只有`NSOpeartionQueue`了,但GCD的代码却无法使用该特性 6 | 7 | 于是写了该工具,可以方便地创建一个`队列池`,类似于线程池的概念,可以避免开辟线程过多的问题 8 | 9 | ## Feature 10 | - 内部使用串行队列来管理线程 11 | - 最多串行队列数不超过32个,所以线程数也不会超过该值 12 | - 提供两种获取队列池的方式 13 | - 全局方法,获取一个队列池 14 | - 自己创建一个队列池管理类,管理串行队列 15 | 16 | ## 原理 17 | 18 | 核心工作就两步骤 19 | 20 | 1. 根据qos、当前CPU情况以及所需的输入创建多个串行队列 21 | - 队列信息存储在`YYDispatchContext`结构体中 22 | 2. 结构体中有一个`counter`,每次调用`YYDispatchQueueGetForQOS`,`counter`加一,同时使用`counter % queueCount`作为下标来轮询地到context中获取一个queue 23 | 24 | `YYDispatchContext`结构体如下 25 | 26 | ``` 27 | typedef struct { 28 | const char *name; 29 | void **queues; 30 | uint32_t queueCount; 31 | int32_t counter; 32 | } YYDispatchContext; 33 | ``` 34 | 35 | ## 参考 36 | - [YYDispatchQueuePool](https://github.com/ibireme/YYDispatchQueuePool) 37 | - [iOS 保持界面流畅的技巧](https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/) -------------------------------------------------------------------------------- /组件化学习.md: -------------------------------------------------------------------------------- 1 | # iOS组件化学习 2 | 3 | - 什么是组件化 4 | - 组件化要考虑哪些问题 5 | - 组件化方案有哪些,他们的场景有哪些 6 | - 实践一下 7 | - Swift有什么组件化方案 8 | 9 | ## 什么是组件化 10 | 11 | 组件化,从我看到的材料来看,并非舶来品,而是国内的特色 12 | 13 | 或许叫做业务模块化更能表达含义 14 | 15 | App功能越加越多,项目会异常庞大,多人协同开发难度变大。组件化即为了解决该问题出现的 16 | 17 | - 说白了,就是想办法将业务功能划分多个模块,每个模块由单独的团队开发 18 | - 每个模块之间通过低耦合的方案进行通信,尽可能降低高耦合的情况(比如原始工程中可能直接引用另一个模块的类名的方式) 19 | 20 | 此处要进行模块化的业务,范围可大可小 21 | 22 | - 小到可以一些可复用的组件,如UI控件 23 | - 大到一些列完整的功能集合,如账号系统 24 | 25 | ## 组件化要考虑哪些问题 26 | 27 | 我这里尽量列出需要关注的问题: 28 | 29 | 现实开发当中这些问题经常困扰我们 30 | 31 | - 字符串硬编码导致易错 32 | - 使用字典传参导致数量类型不匹配问题 33 | 34 | ## 如何组件化 35 | 36 | 常见的组件化技术方案有:基于中介模式(Mediator)的方案和基于协议的方案 37 | 38 | 39 | - URL Router 40 | - 所有基于字符串的解耦方案其实都可以说是伪解耦,它们只是放弃了编译依赖,但是当代码变化之后,即便能够编译运行,逻辑仍然是错误的。 41 | 42 | 43 | 44 | ## 组件化与路由之间的关系 45 | 46 | 47 | ## 疑问 48 | 1. 路由、target-action和阿里的behavior,有什么本质区别 49 | 50 | ## 参考 51 | - [打造完备的iOS组件化方案:如何面向接口进行模块解耦](https://zuikyo.github.io/2019/07/15/iOS_inrerface_orientation_modularization/) 52 | - [iOS应用架构谈 组件化方案](https://casatwy.com/iOS-Modulization.html) 53 | - [在现有工程中实施基于CTMediator的组件化方案](https://casatwy.com/modulization_in_action.html) 54 | - [移动端 iOS 组件化](https://xie.infoq.cn/article/350cb241ebf8546d4ef2e55c7) 55 | - [有赞移动 iOS 组件化(模块化)架构设计实践](https://tech.youzan.com/you-zan-ioszu-jian-hua-jia-gou-she-ji-shi-jian/) 56 | - [73 | 中介模式:什么时候用中介模式?什么时候用观察者模式?](https://time.geekbang.org/column/article/226710) 57 | - [ZIKRouter](https://github.com/Zuikyo/ZIKRouter) -------------------------------------------------------------------------------- /跨平台技术调研.md: -------------------------------------------------------------------------------- 1 | # 跨平台技术调研 2 | 3 | ## RN 4 | 5 | - Reactive Native是由Facebook研发 6 | - RN支持使用JavaScript开发 7 | - 因为Android和iOS系统都支持与JavaScriptCore交互,所以可以做到使用JavaScript代码通过JavaScriptCore来执行native逻辑 8 | - 这就是RN可以做到跨平台的基本原理 9 | 10 | 11 | ## Flutter 12 | 13 | - Flutter是Google研发的,推出时间比RN晚一些 14 | - Flutter支持使用Dart语言进行跨平台开发 15 | - Flutter的工作原理与RN有本质区别 16 | - RN的代码执行顺序是JavaScript->Native,然后渲染绘制工作都是在iOS/Android平台中完成的 17 | - 而Flutter则完全使用自己的渲染绘制逻辑,即基于Skia框架 18 | - 比如iOS平台上,最终UI的绘制是由CoreAnimation提交到GPU侧 19 | - 而Flutter则是将Dart程序通过Skia提交到GPU侧 20 | 21 | 22 | ## 参考 23 | - [Flutter 与 React Native 怎么选择?全面PK看看谁能取胜](https://zhuanlan.zhihu.com/p/517619058) 24 | - [从 React Native 到 Flutter,移动跨平台方案的真相](https://leancloudblog.com/cong-react-native-dao-flutter-yi-dong-kua-ping-tai-fang-an-de-zhen-xiang/) -------------------------------------------------------------------------------- /轮询、SSE、WebSocket区别.md: -------------------------------------------------------------------------------- 1 | # 轮询、SSE、WebSocket区别 2 | 3 | ## 轮询 4 | 5 | - 轮询本质上还是拉数据模式 6 | - 使用HTTP请求定时向服务器发送请求 7 | 8 | ### 长轮询 vs 短轮询 9 | 10 | - 短轮询是,一次请求结束后,连接就关闭 11 | - 这种方案既不容易选择轮询时间间隔,又会导致浪费流量,两者不好调和 12 | - 所以有了长轮询 13 | - 长轮询,同样是发送一次HTTP请求,只是如果本次请求的数据和上一次请求的数据没有变化时,服务端会保持这个连接,暂不返回 14 | - 直到有新数据变化或到达超时时间时 15 | 16 | 17 | ## 参考 18 | - [认识长轮询:配置中心是如何实现推送的?](https://developer.aliyun.com/article/781914) --------------------------------------------------------------------------------