可变不可变基础对象
- 在
Objective-C中,有很多基础对象都有可变和不可变两种版本: - 不可变:
NSString: 代表不可变的文本字符串。NSArray: 代表不可变的对象数组。NSDictionary: 代表不可变的键值对集合。NSData: 代表不可变的字节缓冲区。NSSet: 代表不可变的无序对象集合。NSNumber: 这是一个特例,它只有不可变版本,用于包装基础数据类型如int,float,bool等。NSValue: 用于包装C语言的结构体,例如CGRect,CGPoint,CGSize等。只有不可变版本NSCharacterSet: 用于搜索和匹配字符集合。NSIndexSet: 是一个用于存储唯一整数的集合。
- 可变:
NSMutableString: 代表可变的文本字符串。NSMutableArray: 代表可变的对象数组。NSMutableDictionary: 代表可变的键值对集合。NSMutableData: 代表可变的字节缓冲区。NSMutableSet: 代表可变的无序对象集合。NSMutableCharacterSet: 是NSCharacterSet的可变版本。NSMutableIndexSet: 是NSIndexSet的可变版本。
NSString- 在
Objective-C中,NSString是不可变的,意味着你不能修改其实例所持有的字符串内容。 - 但你可以改变
NSString的指针来指向一个新的NSString对象。 - 在
ARC环境中,NSString指针指向的原来的对象会被自动释放,具体是:编译器会自动插入内存管理相关的代码,从而帮助你自动管理对象的生命周期。 - 关于
ARC机制,见下文
- 在
类的继承
必须继承自NSObject吗
- 在
Objective-C中,一个类不是必须要继承自NSObject,但在实际使用中,大多数情况下都建议继承自它,原因如下:- 基础方法和功能:
NSObject为派生类提供了一系列基础方法和功能,如-isEqual:,-hash,-description, 和-performSelector:等。 - 与Objective-C运行时交互:
NSObject协议和类提供了与Objective-C运行时系统交互的基础能力。这使得其派生类能够使用Objective-C的动态特性,如动态方法解析、消息转发等。 - 内存管理: 如果你在非
ARC(Automatic Reference Counting) 的环境下编写代码,NSObject提供了-retain,-release, 和-autorelease方法来帮助内存管理。 - 与Foundation框架协同工作: 很多
Foundation框架的API预期它们与NSObject的子类一起使用。
- 基础方法和功能:
子类重写父类方法
- 当子类重写(
override)父类的方法时,可以认为它是“覆盖”了父类的那个方法。 - 具体地说,当你尝试调用那个方法的时候,运行时系统会先在子类中查找。如果子类有重写的版本,那么运行时系统会调用子类的方法而不是父类的。
- 需要注意的是,父类的方法并没有被真正“删除”或“替换”,它仍然存在于父类中,只是在子类的上下文中,子类的版本会被优先考虑。
类
定义
|
1 2 3 4 5 6 7 8 |
@interface SimpleClass : NSObject // 公共属性 @property NSString *firstName; @property NSString *lastName; @property int yearOfBirth; @end |
发消息
- 在
Objective-C术语中,一个对象通过调用另一个对象的方法来向该对象发送消息。 - 对象可以向自己发送消息
|
1 2 3 4 5 6 7 8 |
@implementation XYZPerson - (void)sayHello { [self saySomething:@"Hello, world!"]; } - (void)saySomething:(NSString *)greeting { NSLog(@"%@", greeting); } @end |
实例方法
- 方法名称前面的减号表示它是实例方法,可以在类的任何实例上调用。
- 这与类方法不同,类方法可以在类本身上调用。
- 实例方法是与类的实例关联的方法。要调用实例方法,您需要先创建该类的实例(对象),然后通过该实例调用方法。
|
1 2 3 |
@interface MyClass : NSObject - (void)myInstanceMethod; @end |
|
1 2 |
MyClass *obj = [[MyClass alloc] init]; [obj myInstanceMethod]; |
类方法
- 类方法与类本身关联,而不是与类的任何特定实例关联。您不需要创建类的实例来调用类方法。
- 在接口定义中,类方法以加号
+开头。
|
1 2 3 |
@interface MyClass : NSObject + (void)myClassMethod; @end |
|
1 |
[MyClass myClassMethod]; |
方法参数
|
1 2 3 4 5 6 |
void SomeFunction(SomeType value); - (void)someMethodWithValue:(SomeType)value; - (void)someMethodWithFirstValue:(SomeType)value1 secondValue:(AnotherType)value2; |
类的实现
- 头文件
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// // test_class.h // oc_learn // // Created by lif on 2023/9/26. // #ifndef test_class_h #define test_class_h @interface SimpleClass : NSObject { int value; } - (void)test_instance_func; + (void)test_class_func; @end #endif /* test_class_h */ |
- 源文件
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// // test_class.m // oc_learn // // Created by lif on 2023/9/26. // #import <Foundation/Foundation.h> #import "test_class.h" @implementation SimpleClass - (void)test_instance_func { NSLog(@"this is instance func"); } + (void)test_class_func { NSLog(@"this is class func"); } @end |
- 输出
|
1 2 3 4 |
Hello, World! this is instance func this is class func Program ended with exit code: 0 |
其他
- 每个类的名称在应用程序中必须是唯一的,即使在包含的库或框架中也是如此。
ARC
ARC工作的简要概述:
- 引用计数:每个
Objective-C对象都有一个关联的引用计数。当一个对象的引用计数变为0时,该对象会被销毁并释放其占用的内存。 - 增加引用计数:
- 当你创建一个新对象(如通过
alloc、new、copy或mutableCopy方法),其引用计数默认为1。 - 当你给某个对象发送
retain消息(在非ARC环境下),或者在ARC环境下有新的强引用(strong reference)指向该对象时,该对象的引用计数增加。
- 当你创建一个新对象(如通过
- 减少引用计数:
- 在非
ARC环境下,发送release或autorelease消息会减少对象的引用计数。 - 在
ARC环境下,当一个强引用(strong reference)不再指向某对象时(例如,指向了其他对象或被设置为nil),编译器会自动为你插入代码来减少该对象的引用计数。
- 在非
- 对象销毁:当对象的引用计数变为
0,该对象的dealloc方法会被调用,然后对象占用的内存被释放。
属性修饰符
ARC背后的机制实际上相对复杂,涉及到多个属性修饰符(如strong,weak,assign,copy等)和其他细节- strong (默认的修饰符)
- 这意味着属性持有对象的一个强引用。当对象的引用计数为
0时,对象会被销毁。 - 当你设置一个新的对象到这样的属性上时,新对象的引用计数增加。
- 当属性不再持有该对象或被设置为
nil时,原对象的引用计数减少。 - 常用于对象关联的大多数情况。
- 这意味着属性持有对象的一个强引用。当对象的引用计数为
- weak
- 属性持有对象的一个弱引用,不增加对象的引用计数。
- 弱引用用于避免引用循环。当对象被销毁时,所有指向它的弱引用都自动变为
nil。 - 常见的用途是委托(
delegates)和IBOutlets(Interface Builder outlets)。
- assign
- 通常用于基本数据类型(如
int,float等)。 - 对于对象类型,它持有对象的一个非保持引用。这意味着即使对象被销毁,该引用也不会自动置为
nil(这可能会导致“悬挂指针”问题)。 - 使用
assign对于对象类型并不是推荐的做法,除非你非常确信对象的生命周期。
- 通常用于基本数据类型(如
- copy
- 当对象被设置到属性上时,属性会持有该对象的一个复制版本。
- 对于字符串属性,通常建议使用
copy修饰符,因为这样可以确保该属性持有一个不可变的版本,即使外部赋给它一个可变字符串。 - 对于其他需要保持不可变状态的对象(如
NSArray或NSDictionary),也可以使用此修饰符。
- unsafe_unretained
- 类似于
assign,但适用于所有对象类型。 - 它持有对象的一个非保持引用,即使对象被销毁,该引用也不会自动置为
nil。 - 使用此修饰符有风险,因为可能导致访问已被释放的内存的问题。
- 类似于
- readonly
- 此修饰符表示属性只可读,不能通过属性来修改它的值。
- 它不是直接与内存管理相关的,但是常与上述修饰符一起使用。
- readwrite (默认的修饰符)
- 此修饰符表示属性是可读可写的。
- 同样,它不是直接与内存管理相关的,但是常与上述修饰符一起使用。
|
1 2 3 4 5 6 7 |
// 默认情况 // 实际上只需要写@property (strong) NSString *name;,因为readwrite是默认的。 @property (strong, readwrite) NSString *name; @property (copy, readonly) NSArray *items; @property (weak, readwrite) id<SomeDelegate> delegate; |
使用ARC
- 在
Xcode中,打开你的项目。 - 选择你的项目目标在
Targets列表中。 - 在
Build Settings选项卡中,搜索“Automatic Reference Counting”。 - 设置“
Objective-C Automatic Reference Counting”为“YES”。
关闭ARC
- 设置“
Objective-C Automatic Reference Counting”为“NO”。
特定文件禁用ARC
- 选择你的项目目标。
- 选择
Build Phases选项卡。 - 在
Compile Sources部分,找到你想禁用ARC的文件。 - 为该文件添加
-fno-objc-arc编译器标志。
使用ARC需要注意:
- 不要在你的代码中使用
retain,release, 和autorelease。 - 使用新的
ARC属性修饰符,如strong,weak,unsafe_unretained, 和copy。 - 注意循环引用,特别是使用闭包(
block)时。使用weak引用可以避免大多数循环引用。
ARC和autoreleasepool
Autorelease pools提供了一种延迟释放对象的机制,允许对象在稍后的时间点被释放。- 在非
ARC环境下,你可能会使用autorelease方法来避免立即释放一个对象。这个对象随后会在autoreleasepool中被释放。 - 在
ARC环境下,虽然你不会显式地调用autorelease,但ARC仍然可能在内部使用它。因此,autoreleasepool仍然是有用的,特别是在大量创建临时对象的循环中。
autorelease
- 在非
ARC环境中,autorelease是一个常见的内存管理方法,它允许你将对象的所有权交给最近的autorelease pool,该对象在稍后的时间点被释放。 - 这意味着对象在当前方法或作用域结束后不会立即被销毁,而是在外部的
autorelease pool被释放时销毁。
|
1 |
NSString *string = [[[NSString alloc] initWithString:@"Hello, World!"] autorelease]; |
|
1 2 3 4 5 |
- (NSString *)createString { NSString *string = [[NSString alloc] initWithString:@"Sample String"]; return [string autorelease]; } |
autoreleasepool
- 在非
ARC环境下,当你发送一个autorelease消息给一个对象,那个对象会被添加到当前活跃的(最近创建的)autorelease pool中。 - 然后,当该
autorelease pool被drain或释放时,所有在该pool中的对象都会接收到一个release消息。 - 在
ARC环境下,你实际上不能(也不需要)显式地发送autorelease消息给一个对象。ARC会为你自动插入这些消息,以确保内存被正确地管理。- 也是需要放到
autoreleasepool中,但是不需要发什么消息。 - 原理就是
ARC保证了在编译器自动插入一些代码从而能正确释放对象。
- 也是需要放到
|
1 2 3 4 5 |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // ... Code that creates and uses autoreleased objects ... [pool drain]; |
|
1 2 3 |
@autoreleasepool { // ... Code that creates and uses autoreleased objects ... } |
关于ARC的理解
- 编译期与运行期:在编译期间,
ARC会通过静态分析来确定何时自动插入retain、release和autorelease调用。然后,当程序在运行时执行到这些插入的代码时,才会实际执行相应的内存管理操作。 - 例如
NSString对象:如果一个NSString指针变量(比如,一个属性)从一个NSString对象切换到另一个,那么确实如此:当新对象被赋值给该指针时,新对象会被retain(增加引用计数),而原对象会被release(减少引用计数)。如果原对象的引用计数变为0,那么它将被立即释放。 - 不是"优化",而是"管理":虽然可以将ARC看作是某种优化,但更准确地说,它是一种内存管理机制。它确实优化了编写内存管理代码的过程,但其主要目的是确保对象在不再需要时被正确释放,并且在需要时持续存在。
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 包管理器:各平台安装卸载相关记述09/17
- ♥ MASM:OllyDBG二08/05
- ♥ C++_关于对象的具体初始化顺序11/30
- ♥ OpenSourceLicense 相关记述10/21
- ♥ Linux下网络及其他配置相关记述08/12
- ♥ Soui一03/17