尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

iOS开发入门:从Xcode到真机调试,第一行代码的完整闭环

iOS开发入门:从Xcode到真机调试,第一行代码的完整闭环 很多零基础的朋友拿到Xcode的第一反应是找一个能敲代码的窗口然后郑重其事地打出“Hello, World”。但以我这些年看过的真实情况新手的第一行代码往往不是被语法卡住而是卡在环境、模板、模拟器这些“看起来跟代码无关”的地方。这篇文章我想从最贴近新手的角度把从创建工程到真机运行这条完整的链路讲清楚同时把iOS开发里一些高频的热门概念比如开发者模式、模拟器、UIView和CALayer的区别、原生分享、Universal Link这些穿插在“第一行代码”应该出现的场景里。这不是一篇教科书式的概括而是一份我能想到的最实在的上手记录。适合刚装好Xcode、对iOS开发一窍不通的新手也适合用过跨平台框架但没写过原生代码、想补一补基础的朋友。1. 写第一行代码之前先把Xcode和工程结构这关过了1.1 准备一台Mac装好XcodeiOS开发的第一道门槛不是Swift语法而是操作系统。Xcode只能运行在macOS上这就意味着你需要一台Mac或者是能稳定运行macOS的机器。别指望在Windows上直接装XcodeHackintosh和云Mac虽然存在但对新手来说会引入大量乱七八糟的环境问题你很难分清楚到底是代码写错了还是环境坏了排查成本非常高。Xcode的安装很简单打开App Store搜索Xcode点击安装。但它的安装包有几个GB首次启动时还要完成组件初始化速度慢得让人怀疑电脑是不是坏了。我第一次装的时候从下载到真正能新建工程差不多等了一整个下午。这里劝你一句等的时候别反复点“检查更新”或者强行退出重开反而容易出问题。Xcode版本尽量保持最新因为它要跟iOS系统版本配套版本差太多会让后面真机调试变得很难受。另外从一开始就注册并登录一个Apple ID免费的就行。不要纠结什么付费开发者账号的事后面讲真机调试的时候你会发现免费账号足够支撑完整的学习过程。1.2 新建工程时界面选Storyboard还是SwiftUI打开Xcode欢迎页点击“Create a new Xcode project”在模板列表里选择iOS页签下的“App”然后进入配置界面。这里有几个字段要特别注意Product Name是项目名显示给用户的App名称会用到它Organization Identifier是一个反域名标识比如com.example这两个字段拼起来就是Bundle Identifier它是App的唯一身份ID真机调试和上架都靠它区分。真正让新手懵的是Interface这个选项SwiftUI、Storyboard、XIB。我的建议是新手阶段先选StoryboardLanguage选Swift。为什么因为SwiftUI确实是苹果现在主推的方向但现阶段很多公司存量项目还是UIKit Storyboard的技术栈iOS面试里大量问题也都是围绕UIKit展开的。更关键的是UIKit里UIView、UIViewController、CALayer这些概念更容易帮你建立“视图层级”“渲染机制”的心智模型理解这些东西之后再切SwiftUI会快很多。反过来一上来就SwiftUI容易变成“只知道写界面不知道屏幕背后发生了什么”的状态。1.3 找到那行代码到底写在哪里工程创建好后你会看到一堆文件对新手来说最该关心的只有两个Main.storyboard和ViewController.swift。Main.storyboard是界面描述文件ViewController.swift是我们的控制器代码文件。选中ViewController.swift你会看到系统自动生成了一个ViewController类里面已经带好了viewDidLoad方法。对很多新手来说这才是真正的“第一行代码”位置import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 从这里开始写你的第一行代码 print(Hello, iOS) } }按Command R运行如果一切正常模拟器会启动Xcode底部调试区会出现“Hello, iOS”。注意这行字只会出现在Xcode的控制台里不会出现在手机屏幕上。很多新手在这里会有点失落觉得“我写了代码怎么屏幕没动静”。别急这很正常。print是把内容输出到调试控制台的想在屏幕上看到东西需要创建UI控件这是我们后面章节要干的事。不过在这之前你得先搞清楚Xcode自动生成的这些代码是谁、在什么时候帮你干活的。2. AppDelegate和SceneDelegate系统在你写代码之前干了什么2.1 程序的起点不在ViewController传统C语言程序的起点是main函数iOS也一样只是被封装起来了。在Swift里你会在AppDelegate类上看到一个main标记它告诉编译器这个类就是程序入口。编译时系统会自动生成真正的main函数调用UIApplicationMain来启动App。你可以这么理解你写的ViewController是“舞台上的演员”而AppDelegate是“剧务组”负责在观众进场前把舞台、灯光、调度全部安排好。很多新手习惯把业务逻辑一股脑写在ViewController里这本身没错但如果你连AppDelegate是干什么的都不清楚后面排查启动问题会非常吃力。2.2 AppDelegate的生命周期方法AppDelegate里最有名的方法是 didFinishLaunchingWithOptionsApp启动完成、界面还没展示之前系统会调用它。很多SDK的初始化、全局UI配置都会写在这里。但新手要警惕一件事不要在这个方法里做耗时操作比如同步加载大文件、发起阻塞式网络请求。iOS有看门狗机制启动时间过长系统会杀死进程表现出来就是App一启动就闪退并且没什么正经报错。除了启动方法AppDelegate还负责前后台切换的回调。iOS应用生命周期大致有五个状态状态含义Not Running进程不存在或已被系统清理Inactive正在进入前台或被控制中心等系统界面打断不接收触摸事件Active前台运行正常接收事件Background退到后台还能短暂执行一些代码Suspended被挂起不执行任何代码随时可能被系统清理背这张表是iOS面试的高频考点但我更推荐你亲手验证在AppDelegate的各个方法里打上print然后启动、按Home键返回桌面、再切回来观察打印顺序。自己眼睛见过的执行顺序比死记硬背牢固得多。2.3 SceneDelegate是什么回事如果偶尔翻到老教程你会发现里面只有AppDelegate没有SceneDelegate于是怀疑是不是自己的工程少了文件。不是的。从iOS 13开始苹果为了支持iPad多窗口把原来AppDelegate里管理界面的职责拆分了一部分给SceneDelegate。新工程默认启用Scene生命周期所以多了这个文件。对新手来说不需要把这些方法的每个参数都研究透但你要知道SceneDelegate里的willConnectTo方法负责创建window并让window可见它是App界面从“无”到“有”的关键环节。以后如果遇到一个问题App启动后是黑屏ViewController没被加载排查方向之一就是检查SceneDelegate是否设置了rootViewController或者storyboard是否正确引用。2.4 打上断点看清调用顺序这里分享一个我特别推荐新手的动作在didFinishLaunchingWithOptions、viewDidLoad、viewDidAppear里各打一个断点然后重新运行。你会看到执行顺序大概是AppDelegate启动方法 → 加载Main.storyboard → ViewController的viewDidLoad → viewDidAppear。这个故事告诉我们一个App从点击图标到屏幕真正显示出内容系统帮你做了非常多前置工作你写的代码只是其中一小环。理解了这一层你才算真正“踏入”了iOS开发的领域。接下来我们可以让第一行代码变得有画面感了。3. 第一行界面代码不拖控件直接用代码写Label和Button3.1 为什么建议你用代码写界面现在新手的普遍习惯是打开Storyboard从控件库里拖一个Label到界面上再双击改文字运行就能看到效果。这样入门确实快但我更建议你至少用代码写一遍再回去用Storyboard。原因是拖控件的方式把很多底层逻辑藏起来了。你可能拖得很爽却不知道这个Label其实是一个UILabel对象不知道它被添加到了哪个视图层级里不知道它是有frame坐标的也不知道它是靠Auto Layout来约束位置的。而用代码创建控件你必须自己写下每一步创建对象、设置属性、设置位置、添加父视图。整个过程把“面向对象”和“视图层级”这两件最重要的概念用最直接的方式摊在你面前。3.2 一个UILabel是怎么出现的先看最基础的一段代码import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let helloLabel UILabel(frame: CGRect(x: 50, y: 100, width: 200, height: 40)) helloLabel.text 第一行代码 helloLabel.textColor .systemBlue helloLabel.textAlignment .center helloLabel.font UIFont.systemFont(ofSize: 20, weight: .bold) view.addSubview(helloLabel) } }看着简单但这里面至少有四个概念值得新手抠清楚。第一UIKit坐标系的原点在屏幕左上角x轴向右为正y轴向下为正跟我们中学学的数学坐标系正好反过来。CGRect(x:y:width:height:)里的x、y指的是这个控件左上角相对于父视图左上角的位置。第二UILabel本身是没有画面感的它只是内存里的一个对象。你必须调用view.addSubview(helloLabel)把它添加到视图层级里它才会被渲染到屏幕上。我见过不少新手漏了这行代码然后问我“为什么程序不报错但屏幕上什么都没有”。原因就在这里对象创建了但没有挂到界面树上。第三UIKit里frame用的单位是point不是pixel。iPhone的屏幕有很多分辨率但逻辑坐标用point统一了。反正新手阶段你只要记住别纠结像素苹果已经帮你处理好了缩放。第四如果你真的什么都不写UILabel默认是没有任何文字的。所以text属性必须设置。这点看起来废话但真的有人忘了然后运行起来看半天找不到一个Label。3.3 Button能按下去才算有了交互有了显示自然想尝试交互。按钮是我们往下走的关键override func viewDidLoad() { super.viewDidLoad() let button UIButton(type: .system) button.frame CGRect(x: 50, y: 200, width: 200, height: 50) button.setTitle(点我, for: .normal) button.backgroundColor .systemOrange button.setTitleColor(.white, for: .normal) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) view.addSubview(button) } objc func buttonTapped() { print(按钮被点击了) }这里就出现了一个新手非常容易卡住的问题为什么方法前面要加objcaction参数里还要写#selector解释一下。iOS的按钮事件机制调用链本质上是Objective-C时代的消息机制。你在Swift里写的方法默认不会被暴露给Objective-C运行时而addTarget又是一个基于OC消息机制的接口。objc的作用就是把Swift方法暴露给OC运行时让系统能找到它。#selector则是告诉编译器“我要拿这个方法的名字去注册一个对象”。这两个东西是配套出现的少一个程序可能直接崩溃报“unrecognized selector sent to instance”之类的错误。理解了这层以后你看到各种objc就不会发怵了。3.4 用Auto Layout改掉写死的坐标刚才用frame写位置非常直观但它有一个致命问题不同尺寸的屏幕下控件的绝对坐标和宽高都不会自适应。iPhone SE上居中偏上的位置到了iPhone 15 Pro Max上可能偏左横屏时更乱甚至会被刘海遮住。所以从开始习惯Auto Layout是必要的。一个最省心也最经典的写法是用约束让Label居中而不是靠坐标去猜位置helloLabel.translatesAutoresizingMaskIntoConstraints false NSLayoutConstraint.activate([ helloLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), helloLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor) ])这里有一个“硬性前提”我强调过很多次给用代码创建的视图添加Auto Layout约束之前必须先设置translatesAutoresizingMaskIntoConstraints false。如果不设系统会自动把frame转换成一堆约束跟你的手动约束打架结果就是控制台疯狂输出约束冲突日志UI布局错乱。新手最容易对这个机制产生误解的地方在于以为设置了这个属性代码就会自动布局。实际上它只是关掉了autoresizing mask的自动转换真正的布局是由接下来的那些constraint约束决定的。把这两段代码拆开理解和记忆后面学Auto Layout会顺畅很多。4. 模拟器跑通只是开始真机调试、开发者模式和免费签名的坑4.1 模拟器不等于虚拟机它能做的事有限很多新手会以为“模拟器就是iPhone的虚拟环境”其实不对。iOS模拟器运行在Mac的CPU架构上它把Mac的硬件能力直接映射给App使用所以在模拟器上你无法真正测试相机、蓝牙、运动传感器、真机推送等一系列依赖硬件的功能。你说“我App在模拟器里一切正常”这话只能代表基础逻辑没问题。所以我一直强调第一行代码在模拟器上跑通只是一个阶段性的成功。如果你要做一个真实的产品或者参加面试对真机的适配能力是避不开的。4.2 第一次把App装到iPhone的完整流程准备一根靠谱的数据线把iPhone连上Mac。第一次连接时手机上会弹出“是否信任此电脑”必须点信任否则Xcode根本识别不了设备。在Xcode顶部把运行目标从模拟器切换成你的iPhone设备名。然后要注意项目签名设置进入Target的Signing Capabilities页签把Team选成你自己的Apple ID。免费账号就够了真机调试通常不需要付费开发者账号。iOS 16之后的版本还有一个拦路虎开发者模式。如果你不提前打开Xcode运行时会提示需要开启Developer Mode。打开路径是“设置-隐私与安全性-开发者模式”打开后手机会要求重启一次。这一步是新手的常见卡点很多人运行半天都到不了这一步其实是系统安全策略在拦着。安装完成后如果桌面出现了App图标但点开提示“不受信任的开发者”这是正常的。去“设置-通用-描述文件与设备管理”里找到你的Apple ID对应的描述文件点“信任”然后再打开App就正常了。4.3 免费签名、真机报错把我踩过的雷提前告诉你免费签名的核心限制是有效期通常只有7天左右。过期后App会打不开解决方法是把手机连回Mac在Xcode里重新运行一次相当于重新签名覆盖安装。这是很多新手学习到一半突然“App打不开”的终极原因别慌不是你代码写坏了。真机调试过程中下面这些报错出现的频率极高报错/现象通常原因解决办法Could not find Developer Disk Image SupportXcode版本太老不识别当前iOS版本升级Xcode或升级到对应版本的系统Unable to Install “App”Bundle Identifier冲突或签名不一致删除设备上旧App检查Bundle ID和TeamUntrusted Developer没在设置里信任证书找到描述文件并信任开发者App启动后马上闪退日志里有 KERN_INVALID_ADDRESS常见于访问了已被释放的对象或强制解包nil打开崩溃日志看调用栈最顶部的当前类方法关于崩溃日志新手一看到一堆地址和线程信息就容易懵。判断崩溃位置最简单的办法就是看Xcode Debug Console里最开头的错误信息然后点击调用栈上属于自己代码的那一行Xcode会直接帮你跳到出错的那行代码。我见过很多新手学到强解包也就是隐式解包可选项并用!取值的时候随手写了一个会导致nil的强解包App运行起来秒崩。这其实是件好事崩溃一次你对可选类型和内存管理的敬畏就会多一分。5. 第一段交互代码从点击按钮到系统原生分享5.1 点击按钮弹出一个提示框前面我们写过一个buttonTapped方法里面只有一行print。现在我们给它加一点可见的反馈比如弹窗。弹窗在iOS里用的是UIAlertControllerobjc func buttonTapped() { let alert UIAlertController(title: 提示, message: 这是你的第一行交互代码, preferredStyle: .alert) let okAction UIAlertAction(title: 知道了, style: .default) { _ in print(用户点击了确定) } alert.addAction(okAction) present(alert, animated: true, completion: nil) }代码本身不复杂但新手要理解present这个动作到底做了什么。present是UIViewController提供的方法作用是在当前页面上面“再盖一层”新的视图控制器这是一套完整的模态展示机制。所以UIAlertController虽然是弹窗本质上也属于一个特殊的视图控制器只不过苹果帮我们渲染好了样式和转场动画。5.2 调起系统原生分享面板热搜词里有一条“iOS系统原生分享实现”其实对应的是UIActivityViewController。这是苹果给开发者最慷慨的福利之一不需要集成任何第三方SDK一行代码就能调出系统级的分享面板支持微信、短信、邮件、保存到相册以及系统里已经安装的、注册了对应分享能力的App。objc func buttonTapped() { let text 我的第一个iOS程序 let url URL(string: https://www.apple.com)! let activityVC UIActivityViewController(activityItems: [text, url], applicationActivities: nil) if let popover activityVC.popoverPresentationController { popover.sourceView button popover.sourceRect button.bounds } present(activityVC, animated: true) }注意我特意加了一段iPad的适配代码。新手常在iPhone上跑没问题一到iPad上就崩溃原因就是iPad的弹出样式是popover苹果要求它必须有一个“指向来源”的锚点否则会断言崩溃。这个坑在论坛里提问频率非常高实际就是忘设置sourceView。养成“凡是用present展示内容先考虑iPad适配”的习惯会帮你在面试和工作中省下很多麻烦。5.3 从URL Scheme到Universal Link的跳转认知新手第一次听到Universal Link这个概念往往是在做分享、登录、拉起App的业务里。iOS里App跳转有两种经典方式URL Scheme和Universal Link。URL Scheme的做法是在App的Info.plist里注册一个自定义协议比如weixin://然后在代码里通过UIApplication.shared.open(URL(string:weixin://))来打开另一个App。但这种方式有一个明显的信任问题任何App都可以声明相同的Scheme所以系统对这种跳转的限制越来越严。Universal Link是苹果更推荐的方案它使用标准的https域名系统通过apple-app-site-association文件来校验域名和App的绑定关系安全性和体验都更好。它不依赖Scheme字符串的私有注册同一个链接在没装App时打开网页装了App时直接唤起App。对新手来说你不需要马上去配置这个文件但需要知道这个名词在这个领域里的位置。以后你跟别人聊“分享和唤起”能分清URL Scheme和Universal Link分别解决什么问题就已经比大多数同期新手强了。5.4 window.open()在iOS里为什么经常没反应这个话题最好放在“第一行代码”的延伸知识里。无论以后你做的是纯原生iOS还是用H5嵌进WKWebView都会遇到一个问题前端页面里的window.open(xxx)在iOS上经常点了没反应。原因不在于window.open这个API不存在了而是iOS对非用户手势触发的弹窗拦截得非常严格。尤其常见的一个场景是点击按钮后先发了一个网络请求请求成功后在回调里再调用window.open系统会认为这个打开行为不是直接由用户手势触发的从而拦截掉。常见的替代方案是改用location.href跳转或者在用户点击时同步创建一个a标签并调用click。如果想跳回App内部那就涉及到我们前面说的URL Scheme或Universal Link了。这个问题既是原生开发的话题也是前端适配的坑你提前知道这个结论以后在混合开发里面会少走很多弯路。6. 新手最容易误会的几个iOS机制渲染、热更与跨平台6.1 UIView和CALayer一个管交互一个管画图“UIView和CALayer的区别和联系”是iOS面试高频题但我觉得新手第一天就该对这两个概念有基本印象因为它能帮你解释很多“玄学”现象。比如为什么UIView设置圆角不生效、为什么边框的颜色和UIView的背景色表现不一样。通俗地讲UIView是一个“管家”负责接收触摸事件、参与布局和视图层级、管理交互逻辑CALayer则是一个“画师”负责屏幕上像素级别的渲染比如背景颜色、圆角、阴影、边框本质都发生在layer上。UIView内部持有一个layer属性你可以print(view.layer)看看它是真实存在的。所以对新手来说记住一个最直观的类比维度UIViewCALayer属于什么框架UIKitQuartzCore主要职责事件响应、布局、视图管理渲染、动画、视觉样式能响应触摸吗能默认不能圆角、阴影、边框通常要设置layer相关属性原生能力直接支持单独使用场景几乎不会脱离layer存在可以脱离UIView独立存在举个例子你想让一个UILabel变成圆角标签helloLabel.layer.cornerRadius 8 helloLabel.layer.masksToBounds true第一行设置圆角半径但如果你不加第二行系统不会裁剪Label内部的背景出来的效果可能仍然是方形的。原因就是UIView默认允许子视图内容超出layer的边界maskToBounds true的意思是把边界以内的内容保留、以外的裁掉。这个话题新手期不深入但遇到圆角失效时回来翻这一段马上就能对症下药。6.2 iOS为什么不像安卓那样可以随便“热更”“iOS代码热更限制”这个话题很多新手第一次听到是在别人吹某个跨平台方案能“动态更新”的时候。这里需要把预期捋直由于App Store审核规范的限制iOS应用不允许从网络下载可执行的动态代码并在本地运行。也就是说你不能像在部分Android生态里那样上线之后又拉取一段代码来“热更新”业务逻辑。这个限制的直接影响是iOS发版后的错误修复只能通过提审、上架、用户更新来完成。为了这个约束你需要更认真地做真机测试、兼容测试尤其是小版本迭代频繁的产品。新手如果看到某些教程或工具宣称能在iOS上“绕过审核做热更”最好先想想合规风险别因为图省事断送了自己的开发者账号。6.3 跨平台工具能帮你省事但替代不了原生基本功很多朋友会问既然uni-app、Flutter这些跨平台框架能一套代码跑两端为什么还要从原生iOS入手我的态度是跨平台方案当然值得学但新手用它来代替原生底层知识后面会吃大亏。以uni-app打包iOS为例你依然避不开Xcode、签名、证书、描述文件这些原生链路如果你要用到健康数据、NFC、原生地图之类的能力还是得写原生插件或者调用原生SDK。还记得热搜里的“uniapp原生ios健康插件”吗这类需求在真实项目里非常普遍而一个完全不懂原生iOS的人面对这种问题时连排查方向都找不准。我的建议是把第一门语言和第一套开发流程选成原生Swift UIKit建立好“生命周期、视图层级、签名打包、权限配置”这些底层概念之后再去拥抱跨平台等于是在有地基的楼房里做装修。顺序反了你会始终觉得自己在“空中造楼阁”。如果你走完了上面这些步骤模拟器上能看到自己写的Label和Button真机上也能通过点击按钮触发分享面板那么恭喜你的第一行代码已经远远不是“Hello World”了它已经跑通了iOS开发最重要的闭环。接下来的路线其实很清晰把Auto Layout学踏实然后去啃UITableView和UICollectionView这两个最高频的列表控件再跟着URLSession把网络请求玩熟配合抓包工具看请求和响应最后补充数据持久化能力。组件化、模块化、自动化测试这些名词虽然听起来很专业但都是在基础能力之上生长出来的不用急着第一天就啃。我自己带过的不少同级学习者基本都是在这个闭环跑通之后突然从“跟着教程抄”变成了“自己上手做东西”这种状态上的变化才是“第一行代码”真正送给你的礼物。
返回列表