Object-c 基础详解

Object-C 方法传参机制 : OC 中得参数传递都是值传递, 传入参数的是参数的副本;

-- 基本类型 (值传递) : int 等基本类型直接传入 这些基本类型的的副本;

-- 指针类型 (地址传递) : 使用指针变量作为参数, 传递的也是指针变量的副本, 但是这个副本本身的值是一个地址, 地址 变量 和 地址 变量的副本 还是指向同一个地址;

方法 与 传统函数 :

-- 结构化编程语言 : 整个软件由一个函数构成, 如 C 语言, 一个 main 函数就是整个软件架构;

-- 面向对象语言 : 整个软件由 类 组成, 软件中的 方法 必须属于类, 不能独立存在;

方法定义 : 方法只能定义在类中, 不能独立定义;

-- 类方法 : 使用 "+" 标识, 这个方法属于类方法, 使用 [类 方法] 调用;

-- 实例方法 : 使用 "-" 标识, 该方法属于实例方法, 使用 [对象 方法] 调用;

方法属性 :

-- 不独立存在 : 不能独立定义, 只能在类中定义;

-- 类 对象 : 方法要么属于类, 要么属于对象;

-- 方法执行 : 方法不能独立执行, 需要使用 类 或 对象作为调用者;

形参可变方法简介 :

-- 方法定义 : 最后一个形参后增加 逗号 和 三点 ", ..." ;

-- 示例 : NSLog() 函数可以传入任意多参数, 该函数就是形参个数可变的方法;

----------    成员变量机制    -------------

变量分类 : 成员变量, 局部变量, 全局变量;

-- 局部变量 : 在函数里面定义的变量;

-- 全局变量 : 在函数外定义的变量;

-- 成员变量 : 在下面讲解 类定义接口;

成员变量 : 在类 接口 或  实现 部分定义的变量, 都是实例变量, 不支持真正的类变量;

-- 实例变量 : 类对象被创建开始存在, 系统销毁对象, 实例变量就会随之销毁;

-- 实例变量访问 : "实例->实例变量";

示例解析 :

-- 成员变量初始化 : 创建对象时系统会为为成员变量赋初始值, 基础类型 0, 指针类型 nil;

-- 初始化过程 : 执行 "OCPerson *p1 = [[OCPerson alloc] init]" 语句时 系统为 OCPerson 对象的成员变量分配内存空间, 并初始化, 并将对象赋给 p1 变量;

模拟类变量

static 关键字 : 不能修饰成员变量, 只能修饰 局部变量 和 全局变量;

-- 修饰局部变量 : 将局部变量存储到静态存储区;

-- 修饰全局变量 : 限制全局变量只能在当前文件中访问;

-- 修饰函数 : 限制该函数只能在当前源文件中访问;

实现单例 : 定义一个 static 全局变量, 该变量用于保存自己创建的 Singleton 对象, 每次程序获取该单例时, 判断 static singleton 是否为nil, 全局变量为 nil 初始化一个实例并赋值给 static 变量;

----------    隐藏 封装    ----------

封装 : 面向对象三个特性 封装, 继承, 多态;

-- 概念 : 将对象的 状态信息 隐藏在对象内部, 不允许外界 直接访问内部信息, 外部只能通过 类提供的方法 来实现对内部信息的访问 操作;

访问控制符控制级别 : @private < (@package | @protected) < @public ;

-- @private : 只能在当前类访问, 用于彻底隐藏成员变量, 类实现部分定义的成员变量默认是 @private ;

-- @package : 只能在当前映像访问, 可以在当前类 或者 当前映像的 任意位置访问, 用于部分隐藏成员变量;

-- @protected : 子类访问, 可以在当前类, 子类 任意位置访问, 类接口部分定义的成员变量默认使用 @protected 访问;

-- @public : 可以在任意位置访问;

访问控制符注意点 :

-- 注意 : 访问控制符只能控制成员变量是否可以被其它类访问, 不能用于修饰局部变量;

-- 访问控制符控制范围 : 从访问控制符出现位置开始 到 下一个访问控制符 或者 花括号之间的成员变量;

-- getter 和 setter 方法 : 去掉成员变量的下划线前缀, _name 对应 setName() name();

基本原则 :

-- 修饰成员变量方法 : 类中 99% 的变量都应该使用 @private 控制, 用于辅助实现类其它方法的工具方法也要使用 @private 修饰, 定义在实现类内部;

-- 子类访问 : 父类希望其成员变量能被子类访问, 使用 @protected 控制该成员变量;

-- 接口默认 public 方法 : 暴露给其它类自由调用的方法, 在类接口中定义, 在类实现中实现它们;

常用的访问控制符 : @private 将成员变量限制在当前类内部, @public 彻底暴露成员变量, @protected 让成员变量在子类中可以访问;

映像 : 编译后生成的框架 和 执行文件, 编译后 @package 修饰的成员变量 在这些 框架 和 可执行文件中可以被任意访问;

-- 示例 : 我们之前经常使用类似命令 clang -fobjc-arc -framework Foundation OCPerson.m , 该命令生成一个 a.out 文件, 该 a.out 就是一个映像;

assign 指示符 :

-- 作用 : 指定对属性只是简单赋值, 不更改引用计数, 主要适用于 NSInteger int short double 结构体 等数据类型;

-- 引用计数 : 对象的引用计数大于 0 时, 该对象不会被回收, 基础数据类型不存在回收问题, 可以使用 assign 指示符;

atomic (nonatomic) 指示符 :

-- 作用 : 指定合成的存取方法是否是原子操作, 即线程是否安全;

-- atomic : 合成的存取方法都是线程安全的, 一个线程调用存取方法时, 其它方法无法调用存取方法, 避免多线程并发破坏对象的数据完整性;

-- nonatomic : 用于提高存取方法的访问性能, atomic 线程安全会造成性能下降;

copy 指示符 :

-- 作用 : 如果使用 copy 指示符, 当调用 setter 方法对成员变量赋值时, 现将被赋值对象复制一个副本, 再将该副本赋给成员变量;

-- 引用计数 : copy 会将原成员变量所引用计数 -1;

-- 适用情况 : 成员变量类型是指针类型时, 被赋值的对象有可能在赋值之后被修改, 如果不想让被赋值对象被修改影响成员变量, 可以使用 copy

readonly : 系统只合成 getter 方法, 不再合成 setter 方法;

readwrite : 需要合成 setter getter 方法;

retain :

-- 作用 : retain 定义属性, 将某个对象赋值给该属性时, 该属性原来所引用的对象引用计数 -1, 被赋值对象 (成员变量) 引用计数 +1;

-- 使用场景 : 在未启用 ARC 机制情况下, 常用, 启用后不常用;

-- 源码示例 : 不能使用 @autoreleasepool ARC 机制, 需要关闭该机制;

strong 指示符 : 指定该属性对赋值对象持有强引用, 只要该强引用指向被赋值的对象, 那么该对象就不会自动回收;

weak  指示符 : 指定该属性对被赋值对象持有弱引用, 弱引用指向被赋值的对象, 该对象可能被回收;

unsafe_unretained 指示符 : 与 weak 指示符基本相似, 对于被 unsafe_unretained 指向的对象也可能会被回收; 被 unsafe_unretained 修饰的指示的指针变量, 该指针不会被赋值为 nil, 可能导致程序崩溃;

点 . 使用 :

-- 使用前提 : 使用 @property @synthesize 合成 setter 和 getter 方法; 实际上 也允许使用 . 语法访问属性 和 对属性赋值;

-- 本质 : 点语法是一种简单写法, 其本质仍然是 getter 和 setter 方法;

-- 获取属性值 : 只要对象有 getter 方法, 程序可以使用 点 语法获取属性值;

-- 设置属性值 : 只要对象 setter 方法, 程序可以使用 点 语法获取属性值;

---------    KVC    ---------

KVC 简介 :

-- 引入 : Object-C 可以通过 getter setter 方法操作属性, 还可以 以字符串形式间接操作属性, 该方式是 Key Value Coding (KVC);

-- KVC 使用前提 : 最好在接口部分使用 @property 实现类部分使用 @synthesize 合成存取方法, 也可以只定义 "_属性名" 或 "属性名" 成员变量, 之后才能成功使用 KVC;

操作属性方法 :

-- 未指定属性设定值 : "setValue : 属性值 forKey : 属性名" ;

-- 获取指定属性值 : "valueForKey : 属性名" ;

"setValue : 属性值 forKey 属性名" 执行机制 :

"valueForKey : 属性名" 执行机制 :

-- 调用 getter 方法 : 优先考虑调用 getter 方法, 即 属性名() 方法获取返回值;

问题引入 : 使用 KVC 设置对象属性, 如果属性是指针类型, 设置 nil 值完全正确, 如果为 基本类型 int short 类型设置了 nil 会出现异常;

-- 异常信息 : 为基本类型设置 nil 会爆出 NSInvalidArgumentException 异常, int 类型不能接受 nil 值;

-- "setNilValueForKey :" 方法 : 为成员变量设置 nil 时, 如果该成员变量不接受 nil 值, 系统会自动执行 setNilValueForKey 方法;

-- 定制 "setNilValueForKey :" 方法 : 重写了该方法之后, 如果再试图给 不接受 nil 值的变量赋值 nil, 就会自动调用该方法;

复合属性 : 在类 OCStudent 中 定义了 OCPerson 成员变量, 如果我们想要访问 OCPerson 中得 name 成员变量, 就需要先访问 OCPerson 成员变量, 再访问 name 成员变量;

Key 路径操作方法 :

-- " setValue : forKeyPath : " 方法 : 根据 key 路径设置属性值;

-- " valueForKeyPath : " 方法 :  根据 key 路径获取属性值;

KVC 优缺点 :

-- 缺点 : KVC 操作对象性能要比使用 getter 和 setter 方法要差;

-- 优点 : KVC 使用会使程序更加简洁, 适合提炼通用代码, 具有极高的灵活性;

--------    KVO    ---------

IOS 需求 :

-- 数据模型组件 : 负责维护应用程序的状态数据;

-- 视图组件 : 负责显示数据模型组件内部的状态数据;

-- 需求 : 数据模型组件数据发生变化时, 视图组件能动态更新;

KVO (Key Value Observing) 键值监听 :

-- 适用前提 : 只要是 NSObject 子类就可以使用 KVO;

-- 注册监听器用于监听 key 路径 : "addObserver : forKeyPath : options : context";

-- 为 Key 路径删除指定监听器 : "removeObserver : forKeyPath :" 或者 "removeObserver : forKeyPath :";

-- 回调 : 数据发生变化时, 会回调 "observerValueForKeyPath : ofObject : change : context" 方法;

KVO 编程步骤 :

-- 注册监听器 : 为被监听对象注册监听器, 使用 "addObserver : forKeyPath : options : context";

-- 重写监听器方法 : 重写 "observeValueForKeyPath : ofObject : change : contex : " 方法;

-------    Object-C 对象初始化    --------

创建对象语法 : [[类名 alloc] init] 语法, [类名 new] 语法, 每次创建对象都要调用该对象的 alloc 方法为对象分配内存空间;

-- alloc 方法 : alloc 方法 是在 NSObject 中定义的, 所有的 OC 对象都是 NSObject 的子类, 所有的类都可以调用 alloc 方法为所有的实例变量分配内存;

-- init 方法 : 来自 NSObject 中定义, 所有的对象调用 init 方法进行初始化, 将每个成员变量内存空间赋值为 0, 所有的整型变量所在空间都重置为 0, 浮点型变量 0.0, BOOL 型变量 NO, 指针型 nil;

初始化方法种类 :

-- 默认初始化 : NSObject 提供的 默认的 init 方法为所有成员变量赋值 0 初始值;

-- 常用初始化 : 重写 NSObject 的 init 方法, 可以加入任意的自定义初始化过程;

继承简介 :

-- OC 继承 : OC 继承是单继承, 一个子类只能有一个父类, 这点与 Java 相同;

-- 子类继承父类格式 : 只需要在接口部分声明类时, 在类名后面加上 ": SuperClass" 即可;

-- 子类收获 : 子类扩展父类时, 子类可以得到父类的 全部方法 和 全部成员变量;

super 关键字 :

-- 作用 : 在子类方法调用父类被覆盖的实例方法, 该关键字用于限定对象调用其从父类获得的属性 和 方法;

-- 注意 : super 关键字不能出现在 类方法中, 因为类方法执行是不依靠对象的;

-- self 对比 : self 也不能出现在类方法中;

--------    多态    ---------

指针变量类型 : 如果编译时与运行时类型不同, 就会产生多态;

-- 编译时类型 : 由声明该变量时使用的类型决定;

-- 运行时类型 : 由实际赋值给该变量的类型决定;

赋值多态 : 子类可以在任意位置替换父类 (里氏替换);

-- 多态出现 : 子类赋值给父类时, 编译时类型是父类, 运行时类型是子类;

-- 调用重写方法 : 调用子类重写父类的方法时, 执行的是父类方法;

-- 多态 : 相同类型的变量调用同一个方法, 会出现不同的特征, 这就是多态;

指针变量强制类型转换 :

-- 问题出现 : 将子类赋值给父类类型对象时, 就不能再使用父类对象调用子类的方法和属性, 此时需要将父类类型对象强制转换为子类类型对象;

-- 类型转换方法 : "(类型名称 *) 对象名",

-- 将父类转为子类 : 这种强转只是改变指针变量的编译时类型, 变量指向的实际类型不会发生改变;

-- 判断类型 : 转换时需要进行类型判断对象类型, 否则容易出错;

判断指针变量实际类型 :

-- 判断对象是否是 clazz 类对象 : "- (BOOL) isMemberOfClass : clazz";

-- 判断对象是否是 clazz 类 或 子类 实例 : "- (BOOL) isKindOfClass : clazz :";

-- 判断当前是否是 clazz 子类 : "+ (BOOL) isSubclassOfClass : clazz :"


iOS/Mac 开发博客列表(不断更新中)

作为一个iOS开发要看的网站,一定对你有用的,相信我!

提高iOS开发效率的方法和工具

推荐阅读更多精彩内容