关于self.用法的一些总结

本文深入解析了Objective-C中的属性赋值方式及其内存管理机制,包括使用self.与不使用self.的区别,以及不同赋值类型如何影响对象的生命周期,帮助开发者避免内存泄露。


我们经常会在官方文档里看到这样的代码: MyClass.h [ / lang ] @interface MyClass : NSObject { MyObject * myObject; } @property ( nonatomic, retain ) MyObject * myObject; @end MyClass.m @synthesize myObject; - ( id ) init { if

if(calendarArray)

{

[calendarArray release];

calendarArray =nil;

}

calendarArray=[[NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:mobile]] retain];

等价于

self.calendarArray=[NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:mobile]];//retain 一次 释放掉之前可能有值的


我们经常会在官方文档里看到这样的代码:
MyClass.h

[/lang]
@interface MyClass : NSObject {
    MyObject *myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end

MyClass.m

@synthesize myObject;

-(id)init{
    if(self = [super init]){
        MyObject * aMyObject = [[MyObject alloc] init];
        self.myObject = aMyObject;
        [aMyObject release];
    }
    return self;
}

有人就问, 为什么要这么复杂的赋值? 为什么要加self. ? 直接写成self.myObject = [[MyObject alloc] init];不是也没有错么? 不加self有时好像也是正常的? 现在我们来看看内存管理的内容:

先看间接赋值的:

1.加self.:

        MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
        self.myObject = aMyObject; //myObject retainCount = 2;
        [aMyObject release];//myObject retainCount = 1;

2. 不加self.:

       MyObject * aMyObject = [[MyObject alloc] init]; //aMyObject retainCount = 1;
        myObject = aMyObject; //myObject retainCount = 1;
        [aMyObject release];//对象己经被释放 

再看直接赋值的:

3.加self.:

self.myObject=[[MyObjectalloc]init];
//myObject retainCount = 2;

4. 不加self.:

        myObject = [[MyObject alloc] init]; //myObject retainCount = 1;

现在是不是有点晕, 我们先来把代码改一下, 官方的一种常见写法:

MyClass.h

@interface MyClass : NSObject {
    MyObject * _myObject;
}
@property (nonatomic, retain) MyObject *myObject;
@end

MyClass.m

@synthesizemyObject=_myObject;

OK, 你现在再试下, 如果你用self._myObject = aMyObject; 或者 myObject = aMyObject; 你会得到一个错误, 为什么呢, 这里就是和Obj-c的存取方法有关了. 说白了很简单 , 大家都知道, @property (nonatomic, retain) MyObject *myObject; 是为一个属性设置存取方法, 只是平时我们用的方法名和属性名是一样的,现在你把它写成不同的名字, 就会很清楚了. _myObject是属性本身, myObject是存取方法名.

现在我们知道self.是访问属性的存取方法了, 那存取方法又怎么工作的? self.myObject = [[MyObject alloc] init]; 为什么会有内存泄露?

关于nonatomic我不多解释了, 它不是我要讲的重点, 而且我也没完全搞清楚, 不误导大家. 我只说assign, retain ,copy.

get方法是:

-(MyObject*)myObject{
    return _myObject;
}

Set方法是:

// assign 
-(void)setMyObject:(id)newValue{
    _myObject = newValue; 
}
// retain 
-(void)setMyObject:(id)newValue{
    if (_myObject != newValue) { 
        [_myObject release]; 
        _myObject = [newValue retain]; 
    }  
}
// copy 
-(void)setMyObject:(id)newValue{
    if (_myObject != newValue) { 
        [_myObject release]; 
        _myObject = [newValue copy]; 
    } 
}

其实这些方法里还有别的内容, 并不只是这些. 而且这些方法可以被重写. 比如你写一个

-(MyObject*)myObject{

    return _myObject;
}

放在你的类里, 你调用self.myObject时(不要把它放在等号左边, 那会调用get方法)就会调用这个方法.

这里多说一句, @property 是为你设置存取方法, 和你的属性无关, 你可以只写一句

@property (readonly) NSString *name;

在你的类里实现

-(NSString*)name{
    NSLog(@"name");
    return @"MyClass";
}

同样可以用self.name调用.

现在回头 说说我们开始的那四个赋值, 当不用self.的时候,那句话只是一般的赋值, 把一个指针赋给另一个指针, 不会对分配的内存有任何影响, 所以2中不要最后[aMyObject release];这句话和4是一回事. 这里就不多说了.我们看看1和3,
当调用 setMyObject:方法时, 对newValue 做了一次retain操作, 我们必须把原来的newValue释放掉, 不然就会内存泄露, 在1里, 我们有个aMyObject可以用来释放, 在3里, 我们无法释放它, 所以, 在3里, 我们会多出来一个retainCount. 内存泄露了.

说了这么多, 我只想让大家清楚, 什么是调用属性本身, 什么是调用存取方法. 怎么样才能避免内存泄露, 而且, 以上例子里是在自己类里的调用, 如果这个类被别的类调用时, 更要注意一些,

顺便说一下, 如果你想在其它类访问对象属性, 而不是通过存取方法, 你可以用myClass -> myObject来访问, 这样是直接访问对象本身, 不过你先要把myObject设成@public. 但这个是官方不提倡的,

代码比较简单, 我还是发出来, 高人们可以忽略了 附件:SelfExample.zip(18 K)

<think>首先,用户的问题是关于Python中`self.lines`和`self.line`的区别及用法。我需要基于系统级指令来构建回答。 关键系统级指令: - 所有行内数学表达式用`$...$`格式。 - 独立公式用`$$...$$`格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 用户的问题是关于Python的,所以我需要确保回答准确。`self.lines`和`self.line`是类中的属性,它们的区别取决于上下文。通常: - `self.line`可能表示单个行(例如,字符串)。 - `self.lines`可能表示多个行(例如,列表或字符串列表)。 在Python中,这些不是内置关键字;它们是类属性,由程序员定义。所以,我需要解释一般用法。 步骤: 1. **理解问题**:用户想了解`self.lines`和`self.line`的区别及用法。 2. **逐步解释**: - 首先,解释`self`在Python类中的含义:`self`代表类的实例,用于访问实例属性。 - 然后,解释`self.line`和`self.lines`:这些是属性名,可以是任何东西,但通常`line`表示单行,`lines`表示多行。 - 提供示例代码来说明。 3. **使用中文**:整个回答用中文。 4. **数学表达式**:问题不涉及数学,所以可能不需要数学表达式。但如果涉及索引或循环,可以用行内数学,比如`$i$`表示索引。 5. **生成相关问题**:在回答后添加相关问题部分。 6. **引用标识**:在回答中,如果引用任何内容,添加引用标识。但用户提供了引用,我需要参考它们: - 引用[1]:关于`canvas.create_line`,这似乎与图形绘制相关。 - 引用[2]:关于子进程和`for line in iter`,这涉及文件读取。 - 引用[3]:关于序列化和`ProcessPoolExecutor`。 用户上一次的问题是关于`self.lines`和`self.line`的,所以我应该聚焦于此。引用似乎不直接相关,但用户说“上述引用仅供参考”,所以我可能不需要强制使用它们。指令说“请尽可能多的参考用户上一次的问题和你的回答”,但这是第一次互动,所以我基于当前问题。 构建回答结构: - **引言**:简要介绍主题。 - **解释`self`**:解释`self`在Python类中的作用。 - **解释`self.line`和`self.lines`**:讨论它们的常见用法和区别。 - **示例**:提供代码示例来演示。 - **注意事项**:提到上下文的重要性。 - **相关问题**:生成相关的问题。 生成相关问题: 1. 如何在Python类中定义属性? 2. 处理文本数据时,使用列表还是单个字符串更好? 3. 在图形编程中,`self.lines`如何用于绘制多条线? 确保回答真实可靠:基于Python知识。 最终回答格式: - 用中文。 - 结构清晰:分步骤。 - 添加引用标识:如果引用特定内容。 - 最后有相关问题部分。</think>### Python中`self.lines`和`self.line`的区别及用法 在Python中,`self.lines`和`self.line`都是类属性,通过`self`关键字访问。`self`代表类的当前实例,用于存储和操作实例级别的数据。它们的区别主要在于语义和用途:`self.line`通常表示单个行(如一个字符串),而`self.lines`通常表示多个行(如一个列表或字符串列表)。下面我将逐步解释它们的区别、常见用法,并提供代码示例。 #### 1. **`self`的基本概念** - 在Python类中,`self`是实例方法的第一个参数,指向对象本身。它用于访问和修改实例属性。 - 例如,在初始化方法`__init__`中定义属性: ```python class MyClass: def __init__(self): self.line = "" # 初始化一个空字符串,用于存储单行文本 self.lines = [] # 初始化一个空列表,用于存储多行文本 ``` - 这里,`self.line`和`self.lines`是自定义属性名,它们的含义由程序员根据上下文定义。Python没有内置这些属性,因此用法取决于具体场景。 #### 2. **`self.line`和`self.lines`的区别** - **`self.line`**: - **含义**:通常用于存储单个行数据,例如一个字符串、一行文本或一个数据单元。 - **典型用法**:在处理文件读取、用户输入或图形绘制时,表示当前行或单个元素。例如: - 在文本处理中,存储当前读取的行。 - 在图形编程中,表示一条线段(如引用[1]中的`canvas.create_line`,它绘制单条线)[^1]。 - **特点**:占用内存小,操作简单,适合处理原子性数据。 - **`self.lines`**: - **含义**:通常用于存储多个行数据,例如一个列表、元组或集合,包含多个字符串或元素。 - **典型用法**:在批量处理数据时,如读取整个文件、存储多行文本或管理多个图形对象。例如: - 在文件处理中,使用`for line in iter`循环读取所有行并存储到`self.lines`(如引用[2]提到的子进程读取)[^2]。 - 在并发编程中,`self.lines`可能存储可序列化的数据列表,用于跨进程传递(如引用[3]中的序列化要求)[^3]。 - **特点**:支持迭代和批量操作,但占用更多内存,适合处理集合数据。 - **关键区别**: - **数据结构**:`self.line`是单个值(如字符串),`self.lines`是集合(如列表)。 - **应用场景**:`self.line`用于实时处理或单次操作;`self.lines`用于存储历史数据或批量处理。 - **性能考虑**:`self.line`操作更快;`self.lines`在大型数据集上可能影响效率,但便于遍历。 #### 3. **代码示例** 以下是一个完整的Python类示例,演示`self.line`和`self.lines`的用法。场景:一个简单的文本处理器,读取文件并统计行数。 ```python class TextProcessor: def __init__(self): self.line = "" # 存储当前行 self.lines = [] # 存储所有行 def read_file(self, filename): """读取文件并存储到属性中""" with open(filename, 'r') as file: for self.line in file: # 逐行读取,当前行存入 self.line self.lines.append(self.line.strip()) # 将处理后的行添加到 self.lines def count_lines(self): """统计行数并打印当前行""" print(f"总行数: {len(self.lines)}") if self.lines: # 检查列表是否非空 print(f"当前行: {self.line}") # 显示最后读取的行 # 使用示例 processor = TextProcessor() processor.read_file("example.txt") # 假设文件存在 processor.count_lines() ``` - **解释**: - `self.line`在循环中存储当前读取的行,便于实时处理。 - `self.lines`作为列表存储所有行,便于后续批量操作(如统计)。 - 输出示例:如果文件有3行,`len(self.lines)`返回3,`self.line`显示最后一行。 #### 4. **注意事项** - **上下文依赖性**:属性名`line`和`lines`不是Python关键字,其含义完全由程序员定义。在您的代码中,应通过注释或文档明确说明。 - **常见错误**: - 混淆单数和复数:`self.line`用于单个元素时,不要误用为列表。 - 内存管理:`self.lines`存储大量数据时,可能导致内存溢出;考虑使用生成器或分块处理。 - 序列化限制:如果`self.lines`用于跨进程通信(如引用[3]),确保元素是可序列化的(如字符串、列表)[^3]。 - **最佳实践**: - 初始化时定义属性,避免运行时错误。 - 在图形编程中(如引用[1]),`self.lines`可用于存储多条线段数据,而`self.line`表示当前绘制的线[^1]。 #### 5. **总结** `self.line`和`self.lines`的区别主要在于数据规模:前者是单一行(标量),后者是多行(集合)。正确使用它们能提高代码可读性和效率。在实际项目中,根据需求选择:处理单个元素用`self.line`,处理集合用`self.lines`。如果您提供更多上下文(如具体代码),我可以给出更针对性的建议。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值