Objective-C 学习笔记 | 基础

Objective-C 学习笔记 | 基础

参考书:《Objective-C 编程(第2版)》

第1部分 入门

Objective-C语言是以C语言为基础的,但增加了对面向对象编程的支持。Objective-C语言是用来开发在苹果iOS以及OS X操作系统上运行的应用的编程语言。

第2部分 如何编程

该部分讲解了C语言编程的必要知识,这里只记录Objective-C新增内容。

NSInteger和NSUInteger

NSUInteger是无符号的整型,NSInteger是有符号的整型。

NSInteger是一个封装,它会识别当前操作系统的位数,自动返回最大的类型。
定义的代码类似于下:

#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif

这样做让NSInteger和NSUInteger变得通用,不用考虑设备是32位还是64位。

如果使用printf()来输出这两种类型的变量,需要先将NSInteger转换成long,NSUInteger转换成unsigned long。

第3部分 Objective-C与Foundation

该部分开始介绍Objective-C。编写Objective-C程序时,要使用Foundation框架。框架(framework)是由很多类(Class)组成的库。这里记录一些新的知识点。

#import和#include的区别

#include指令告诉编译器做呆板的复制粘贴,将包含的内容粘贴到目标文件中来。而#import指令则让编辑器先检查之前是否导入过这个文件,或是已经被包含到目标文件中了。因此,#import指令导入更快、更有效率。

类与实例、方法与消息

Objective-C也有类和对象的概念,Objective-C的方法和消息与函数类似。如需执行方法中的代码,首先需要发送一条消息给包含这个方法的对象或类。消息发送(指令)必须写在一对方括号中,并且必须包含接收方(receiver)和选择器(selector),如下图所示:

请添加图片描述

date是一个类方法。date方法执行后,NSDate类会在堆上new一个NSDate实例:now,并初始化为当前的日期/时间,然后返回新对象(now)的地址。

请添加图片描述

有了NSDate实例:now之后,我们可以给这个对象发一个实例方法,比如:timeIntervalSince1970。通常来说,实例方法会提供实例中实例变量的信息,或是对实例的实例变量进行操作。

测试程序:

//
//  main.m
//  TimeAfterTime
//
//  Created by 刘文晨 on 2024/6/5.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 向 NSDate 类发送 date 消息,让它执行 date 方法
        NSDate *now = [NSDate date];
        // 打印实例的地址
        NSLog(@"This NSDate object lives at %p", now);
        // %@ 会输出相应对象的“描述信息”
        NSLog(@"The date is %@", now);
        // 实例方法
        double seconds = [now timeIntervalSince1970];
        NSLog(@"It has benen %f seconds since the start of 1970.", seconds);
    }
    return 0;
}

注意:

  1. 类方法和类对应,实例方法和实例对应。receiver和selector不匹配就会出错。
  2. Objective-C是区分大小写的,方法名也是区分大小写的。
  3. Objective-C语言命名习惯为“驼峰式”或“前缀大写的驼峰式”。

alloc和init

消息可以以嵌套的形式连续发送,而唯一必须要以嵌套的形式发送的消息是alloc和init。

每个类都有一个alloc类方法,它创建一个新的对象并返回指向该对象的指针。通过alloc类方法创建出来的对象,必须要经过初始化才能使用。每个类也都有一个init实例方法,它用来初始化实例。

NSDate *now = [[NSDate alloc] init]; // 消息嵌套,和下面的效果一样
NSDate *now = [NSDate date]; // date 方法代码最少,称之为便利方法

nil

nil就是空指针,不指向任何对象。

在Objective-C中,可以向nil发送消息,是合法的,但得到的返回值没有意义。

注意,如果程序向某个对象发送了消息,但不符合预期,应该检查receiver是否为nil。

id

id 类型的含义是:可以指向任意类型的Objective-C对象的指针。‘

id delegate; // id 已经隐含了 * 的作用

类似于 auto(?)

ARC

ARC(automatic reference counting,自动引用计数)为Objective-C提供了一种自动销毁不被引用的对象的机制。当项目开启了ARC,编译器会自动给项目添加代码来计算每个对象的引用数,即每个对象都会对指向自己的指针计数。当引用数为0时, 程序会自动销毁该对象,释放内存。

类似于 shared_ptr。

在Objective-C加入ARC之前,程序员必须手动维护引用计数。

[anObject release]; // abOject 会失去一个拥有方
[anObject retain]; // abOject 会得到一个拥有方
[anObject autorelease]; // abOject 会在 autorelease 池(对象)被排干(drain)的时候收到 release 消息

虽然 ARC 会自动使用 autorelease 池,但是必须由程序创建并排空相应的 autoreleasepool 池,语法如下:

// 创建 autorelease 池(对象)
@autoreleasepool {
  ...
} // autorelease 池被排空

字面量语法

格式:

NSString *lament = @"Hello World!";
NSArray *dataList = @[now, tomorrow, yesterday]; // 这三个都是 NSDate 对象

字面量语法是Objective-C语言的一种缩写,可以以不明确发送消息的方式创建实例。

还没有字面量语法的时候,只能使用 arrayWithObjects: 类方法来创建NSArray实例:

NSArray *dataList = [NSDate arrayWithObjects:now, tomorrow, yesterday, nil]; // nil 是结束标记,让方法停止运行

self

Objective-C的方法都包含一个隐含的局部变量 self。self 是指针,指向运行当前方法的对象。当某个对象要向自己发送消息时,就需要使用 self。它有 2 个简单的用法:

  1. 调用自身的存取方法,避免直接存取实例变量。
  2. 将 self 作为实参传给其他方法,以便其访问“当前的”对象。

NSObject 协议中的 self:

#include <objc/objc.h>
#include <objc/NSObjCRuntime.h>

@class NSString, NSMethodSignature, NSInvocation;
@protocol NSObject

- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;
@property (readonly) Class superclass;
......
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'anObject.dynamicType' instead");
- (instancetype)self;
......
@end

self最终返回的结果就是instancetype类型的代理方法,它是动态类型,最终运行时才会确定,实例方法返回实例类型、静态方法返回的是Class。

super

super 的作用:直接调用父类中的某个方法,或者说从父类开始查找与之匹配的实现。

使用场合:子类重写父类的方法时想保留父类的一些行为。

结构体 objc_super 的官方解释:The compiler generates an objc_super data structure when it encounters the super keyword as the receiver of a message. It specifies the class definition of the particular superclass that should be messaged.

#include <objc/objc.h>
#include <objc/runtime.h>

#pragma GCC system_header

#ifndef OBJC_SUPER
#define OBJC_SUPER

/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained id receiver;
    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    __unsafe_unretained Class class;
#else
    __unsafe_unretained Class super_class;
#endif
};

当遇到super关键字时,编译器会生成一个objc_super结构体,作为消息的接收者,objc_super结构体使得接收消息的父类的定义被明确化。

isa指针

任何一个对象的isa指针都会指向创建该对象的类。

给对象发送消息的时候,对象就会通过isa指针找到该对象的类并查询是否有该消息名的方法。如果没有,就会继续查询它的父类,直到找到名为消息名的方法,或者到达继承链的顶端(NSObject)为止。

description方法和%@转换说明

格式说明符%@让对象描述自己,实际上在处理%@时,程序会向相应的指针变量所指的对象发送 description 消息。

description 方法会返回一个描述类实例的字符串。description 是一个 NSObject 方法,所以所有的对象都有这个方法。默认的 NSObject 实现会以字符串的形式返回该对象在内存的地址。不同的类可以重写(override)description 方法,来最有效地描述实例。

强引用循环和弱引用

两个对象互相拥有的关系将导致相关对象都无法释放,这种情况叫强引用循环,这是导致内存泄露的常见原因。

请添加图片描述

Xcode的Instruments中的Leaks组件可以找出程序中的强引用循环:

请添加图片描述

通过弱引用,可以解决该问题。弱引用是不说明所有权的指针,把 BNRAsset 的 holder 属性改成 weak,就能让 BNRAsset 对象不拥有它的 holder(BNREmployee 对象)。代码如下:

#import <Foundation/Foundation.h>
@class BNREmployee;

NS_ASSUME_NONNULL_BEGIN

@interface BNRAsset : NSObject

@property (nonatomic, copy) NSString *label;
@property (nonatomic, weak) BNREmployee *holder;
@property (nonatomic) unsigned int resaleValue;

@end

NS_ASSUME_NONNULL_END

完整程序见于:UestcXiye/Objective-C-Practice。

弱引用的自动置零特性:强引用会保留对象的拥有方,使其不被释放。而弱引用则不会保留,因此标为弱引用的实例变量与属性指向的对象可能会消失,如果发生了这种情况,那么这个实例变量或属性会被设为 nil。

Collection 类

Collection 类的实例用于保存指向其他对象的指针。主要分为三种:

  1. NSArray 及其子类 NSMutableArray
  2. NSSet/NSMutableSet
  3. NSDictionary/NSMutableDictionary

注意以下 4 点:

  1. Collection 对象只能保存对象的指针,不能保存基本类型变量或指向结构的指针,需要先将这些 C 语言基本类型封装成对象,再存入 Collection 对象。

    例如:float变量、int变量等要先转换成NSNumber再存入Collection对象,或者直接用NSNumber字面量实例。结构可以用NSValue(它是NSNumber的父类)实例来封装。

  2. 其中 NSArray、NSSet、NSDictionary 具有不可修改性。使用它们可以节约内存提高性能,因为它们的 copy 方法仅仅返回指向自己的指针,不会做拷贝之类的事情。

  3. 向可改变的 Collection 对象中加入某个对象时,Collection 对象会成为该对象的拥有方;同理,移除对象时,Collection 对象就不再是该对象的拥有方了。对于不可改变的 Collection 对象而言,创建时就拥有其中所有对象的所有权,而 Collection 对象被释放时,它就放弃其中所有对象的所有权。

  4. Collection 对象不能保存 nil,需要将“空”包装成一个对象,可以使用 NSNull 类。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/692338.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Coze入门指南:创建Bot时,如何写好人设与回复逻辑(Persona Prompt)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Coze Bot 📒📝 Persona & Prompt🌟 # Character🌟 ## Skills🌟 # Overall Rules to follow🌟 ## Workflow🌟 ## Constraints📝 通用写法与模板📝 示例🌟技巧和注意事项⚓️ 相关链接 ⚓️📖 介绍 📖…

VSCode数据库插件

Visual Studio Code (VS Code) 是一个非常流行的源代码编辑器&#xff0c;它通过丰富的插件生态系统提供了大量的功能扩展。对于数据库操作&#xff0c;VS Code 提供了几种插件&#xff0c;其中“Database Client”系列插件是比较受欢迎的选择之一&#xff0c;它包括了对多种数…

【机器学习】机器学习与医疗健康在智能诊疗中的融合应用与性能优化新探索

文章目录 引言机器学习与医疗健康的基本概念机器学习概述监督学习无监督学习强化学习 医疗健康概述疾病预测诊断辅助个性化治疗方案制定 机器学习与医疗健康的融合应用实时健康监测数据预处理特征工程 疾病预测与优化模型训练模型评估 诊断辅助与优化深度学习应用 个性化治疗方…

Vue第三方库与插件实战手册

title: Vue第三方库与插件实战手册 date: 2024/6/8 updated: 2024/6/8 excerpt: 这篇文章介绍了如何在Vue框架中实现数据的高效验证与处理&#xff0c;以及如何集成ECharts、D3.js、Chart.js等图表库优化数据可视化效果。同时&#xff0c;探讨了Progressive Web App(PWA)的接入…

性能提升70%~220%,OBKV提高事务处理效率

1. OBKV 是什么&#xff1f; OBKV&#xff0c;OceanBase的多模KV产品&#xff0c;专注低成本、大规模的结构化或半结构化数据存储&#xff0c;并提供高效访问性能的简易操作接口。 在实现层面&#xff0c;OBKV Bypass了SQL层&#xff0c;直接基于OceanBase的分布式存储构建了…

Linux内核下网卡硬件 MAC 和PHY分析笔记

1 简介 通常CPU自带的以太网接口是MAC控制器&#xff0c;为了实现完整的功能&#xff0c;外围硬件还需要增加一个PHY芯片。 PHY芯片在建立网络连接时负责协商确定网速、全双工 或者 半双工等。在正常通讯时负责在MAC控制器的MII信号 与 网线中的信号之间做转换。 本文的内核代…

Servlet-01

文章目录 Servlet创建Servlet探究Servlet的生命周期 HttpServletWebServlet注解详解 重定向与请求转发ServletContextServletContext中的接口 HttpServletRequestHttpServletResponse状态码解释Cookie Servlet Q&#xff1a;它能做什么呢&#xff1f; A&#xff1a;我们可以通…

[office] Excel教学:Excel通配符怎么用? #其他#职场发展

Excel教学&#xff1a;Excel通配符怎么用&#xff1f; 尽管Excel使用了很多年&#xff0c;但很多人都还是忽略了Excel通配符的存在&#xff0c;不知道通配符是什么&#xff0c;不知道如何使用它。今天我就完整地介绍一下通配符&#xff0c;让你彻底地认识通配符。 关于通配符…

SpringBoot2+Vue3开发课程审核流程系统

SpringBoot2Vue3开发课程审核流程系统 简介 此系统实现了课程审核全流程功能并使用了Activiti7工作流技术&#xff0c;功能包含&#xff1a;课程管理、用户管理、流程定义、课程审核&#xff08;我的申请、我的代办、我的已办&#xff09; 功能介绍 课程管理 对课程信息的管…

【JavaScript对象详解】 Day05

JavaScript对象详解 JavaScript 基础 - 第5天对象语法对象属性对象使用属性-查属性-改属性-增属性-删 &#xff08;了解&#xff09; 方法和调用遍历对象遍历数组对象null 内置对象Math属性方法生成任意范围随机数 综合案例随机点名案例猜数字游戏猜数字游戏设定次数生成随机颜…

深入解读Prometheus Adapter:云原生监控的核心组件

一、引言 Prometheus Adapter的背景与重要性 在现代的云原生架构中&#xff0c;微服务和容器化技术得到了广泛的应用。这些技术带来了系统灵活性和扩展性的提升&#xff0c;但同时也增加了系统监控和管理的复杂度。Prometheus作为一款开源的监控系统&#xff0c;因其强大的指标…

微信小程序 导航navigation-bar

属性类型默认值必填说明最低版本titlestring否导航条标题2.9.0loadingbooleanfalse否是否在导航条显示 loading 加载提示2.9.0front-colorstring否导航条前景颜色值&#xff0c;包括按钮、标题、状态栏的颜色&#xff0c;仅支持 #ffffff 和 #0000002.9.0background-colorstring…

javaweb学习(day14-ThreadLocal文件上传下载)

一、线程数据共享和安全 -ThreadLocal 1 什么是 ThreadLocal ThreadLocal 的作用&#xff0c;可以实现在同一个线程数据共享, 从而解决多线程数据安全问题. ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!] ThreadLocal 可以像 Map 一样存取数…

Steam下载游戏很慢?一个设置解决!

博主今天重装系统后&#xff0c;用steam下载发现巨慢 500MB&#xff0c;都要下载半小时。 平时下载软件&#xff0c;一般1分钟就搞定了&#xff0c;于是大致就知道&#xff0c;设置应该出问题了 于是修改了&#xff0c;如下设置之后&#xff0c;速度翻了10倍。 如下&#x…

Mysql使用中的性能优化——单次插入和批量插入的性能差异

一般Mysql的客户端和服务端不在一台机器上&#xff0c;所以它们之间的通信需要通过网络进行。我们本次实验&#xff0c;希望抛开网络的影响&#xff0c;测试不同SQL方案在Mysql服务器上的执行效率的对比。于是我们使用“存储过程”来辅助测试。 结论 先上结论&#xff1a; 批…

windows安装conda

1 Conda简介 Conda 是一个开源的软件包管理系统和环境管理系统&#xff0c;用于安装多个版本的软件包及其依赖关系&#xff0c;并在它们之间轻松切换。Conda 是为 Python 程序创建的&#xff0c;适用于 Linux&#xff0c;OS X 和Windows&#xff0c;也可以打包和分发其他软…

【Vue】如何提供访问vuex的数据

文章目录 一、提供数据二、访问Vuex中的数据通过$store访问的语法1&#xff09;模板中使用2&#xff09;组件逻辑中使用3&#xff09;js文件中使用 三、通过辅助函数 - mapState获取 state中的数据 一、提供数据 State提供唯一的公共数据源&#xff0c;所有共享的数据都要统一…

Redis进阶知识个人汇总

持久化 三种方式实现它的持久化&#xff1a; RDB持久化 全称Redis数据备份文件&#xff0c;又称Redis数据快照 这种就是将Redis内存中所有数据记录到磁盘中&#xff0c;当实例出故障后&#xff0c;从磁盘中读快照文件进行恢复数据。 一般使用bgsave指令实现 复制主线程得到一…

NVeloDocx一个基于NVelocity的word模版引擎

NVeloDocx是一个基于NVelocity的Word模版引擎&#xff0c;目前主要是用于E6低代码开发平台供用户轻松制作各种Word报告模版。 有以下优点&#xff1a; 1、完全的NVelocity语法&#xff1b; 2、直接在Word中写NVelocity脚本&#xff0c;使用非常非常方便&#xff1b; 3、完全兼…

值类型和引用类型在使用和存储上的区别

使用上的区别 //值类型 int a 10; //引用类型 int[] arr new int[] { 1, 2, 3, 4, 5 };//声明了一个b让其等于之前的a int b a; //声明了一个arr2让其等于之前的arr int[] arr2 arr; Console.WriteLine("a{0},b{1}", a, b); Console.WriteLine("arr[0]{0},…