- 浏览: 13748376 次
- 性别:
- 来自: 洛杉矶
文章分类
- 全部博客 (1994)
- Php / Pear / Mysql / Node.js (378)
- Javascript /Jquery / Bootstrap / Web (435)
- Phone / IOS / Objective-C / Swift (137)
- Ubuntu / Mac / Github / Aptana / Nginx / Shell / Linux (335)
- Perl / Koha / Ruby / Markdown (8)
- Java / Jsp (12)
- Python 2 / Wxpython (25)
- Codeigniter / CakePHP (32)
- Div / Css / XML / HTML5 (179)
- WP / Joomla! / Magento / Shopify / Drupal / Moodle / Zimbra (275)
- Apache / VPN / Software (31)
- AS3.0/2.0 / Flex / Flash (45)
- Smarty (6)
- SEO (24)
- Google / Facebook / Pinterest / SNS (80)
- Tools (22)
最新评论
-
1455975567:
xuezhongyu01 写道wocan23 写道我想问下那个 ...
Mysql: LBS实现查找附近的人 (两经纬度之间的距离) -
xuezhongyu01:
wocan23 写道我想问下那个111.1是怎么得来的我也看不 ...
Mysql: LBS实现查找附近的人 (两经纬度之间的距离) -
18335864773:
试试 pageoffice 在线打开 PDF 文件吧. pag ...
jquery在线预览PDF文件,打开PDF文件 -
青春依旧:
opacity: 0.5; 个人喜欢这种方式!关于其他css特 ...
css透明度的设置 (兼容所有浏览器) -
July01:
推荐用StratoIO打印控件,浏览器和系统的兼容性都很好,而 ...
搞定网页打印自动分页问题
7,对象的初始化以及实例变量的作用域
本系列讲座有着很强的前后相关性,如果你是第一次阅读本篇文章,为了更好的理解本章内容,笔者建议你最好从本系列讲座的第1章开始阅读,请点击这里 。
到目前为止,我们都使用的是下列方式创建对象
这种new的方式,实际上是一种简化的方式。笔者在这里总结一下前面几章里面曾经提到过关于创建对象的2个步骤:
第一步是为对象分配内存也就是我们所说的allocation,runtime会根据我们创建的类的信息来决定为对象分配多少内存。类的信息都保存 在Class里面,runtime读取Class的信息,知道了各个实例变量的类型,大小,以及他们的在内存里面的位置偏移,就会很容易的计算出需要的内 存的大小。分配内存完成之后,实际上对象里面的isa也就被初始化了,isa指向这个类的Class。类里面的各个实例变量,包括他们的超类里面的实例变 量的值都设定为零。
需要注意的是,分配内存的时候,不需要给方法分配内存的,在程序模块整体执行的时候方法部分就作为代码段的内容被放到了内存当中。对象的内容被放到了数据段当中,编译好的方法的汇编代码被放到了代码段当中。在Objective C里面,分配内存使用下列格式:
NSObject已经为我们提供了诸如计算内存空间大小以及初始化isa还有把各个实例变量清零,毫无疑问NSObject已经非常出色的完成了内存分配的工作,在一般情况下,我们不需要重写alloc方法。
第 二步是要对内存进行初始化也就是我们所说的Initialization。 初始化指的是对实例变量的初始化。虽然在alloc方法里面已经把各个实例变量给清零了,但是在很多情况下,我们的实例变量不能是零(对于指针的实例变量 而言,就是空指针)的,这样就需要我们对实例变量进行有意义的初始化。
按照Objective-C的约定,当初始化的时候不需要参数的话,就直接使用init方法来初始化:
init是一个定义在NSObject里面的一个方法,NSObject明显无法预测到派生类的实例变量是什么,所以同学们在自己的类里面需要重载一下init方法,在init方法里面把实例变量进行初始化。
但 是,需要强调的是,由于某种原因我们的init也许失败了,比如说我们需要读取CNBLOGS.COM的某个RSS,用这个RSS来初始化我们的对象,但 是由于用户的网络连接失败所以我们的init也许会失败,在手机应用当中的一些极端的情况下比如说有同学写一个读取网页内容的程序,在网页内容非常大的时 候,那么alloc也有可能会失败,为了可以方便的捕获这些失败,所以我们在程序当中需要把上面的过程写在一起:
if (对象名)
else
加上了上面的if语句我们的初始化过程就是完美的,当然我们有的时候不需要这个if语句。当我们的alloc和init永远不会失败的时候。关于初始化的时候的错误捕获,笔者将在后面的章节里面论述。
为了我们写程序方便和简洁,在创建一个从NSObject派生的类的对象的时候,苹果公司把alloc和init简化成为new,我们在程序代码当中使用任何一种方式都是可以的,具体怎么写是同学们的喜好和自由。
到这里,有同学会问,如果我们的init需要参数怎么办?按照Objective-C的约定,我们需要使用initWith...。也就是带参数的变量初始化,这个也是本章的主要内容。
本章在讲述initWith的同时,也将会顺便的给大家介绍一下实例变量的作用域。
7.1,本章程序的执行结果
在 本章里面,我们将要继续使用我们在第4章已经构筑好的类Cattle和Bull。从一般的面向对象的角度上来说,是不鼓励我们改写已经生效的代码的。但是 本章的目的是为了使同学们可以很好的理解主题,所以笔者在这里暂时违反一下规则改写了一下Cattle类,在里面追加了initWith方法,笔者也在 Cattle类里面追加了一些实例变量为了阐述实例变量的作用域的问题。由于在Cattle类里面笔者追加了一些东西,所以在Bull类里面改写了 saySomething这个函数,让我们的Bull可以说更多的内容。我们的redBull是这样说的:
图7-1,本章程序的执行结果
本章程序代码晴点击这里 下载。
再次强调 在实际的编程过程中,尤其是写大型程序多人合作的时候,除非发现BUG,否则不要改写已经生效的代码。这样会产生一些意想不到的结果,从而使其他的弟兄们或者姐妹们对你充满怨言。
7.2,实现步骤
第一步,按照我们在第2章所述的方法,新建一个项目,项目的名字叫做07-InitWithAndIvarScope。如果你是第一次看本篇文章,请到这里参看第二章的内容。
第二步,按照我们在第4章的4.2节的第二,三,四步所述的方法,把在第4章已经使用过的“Cattle.h”,“Cattle.m”,“Bull.h”还有“Bull.m”, 导入本章的项目里面。然后把第6章里面的“MyNSObject.h”也导入到项目当中。
第三步,打开“Cattle.h”,修改成为下面的代码并且保存:
#import <Foundation/Foundation.h> @interface Cattle : NSObject { int legsCount; @private bool gender; //male = YES female = NO @protected int eyesCount; @public NSString *masterName; } - (void)saySomething; - (void)setLegsCount:(int) count; - (id)initWithLegsCount:(int) theLegsCount gender:(bool) theGender eyesCount:(int) theEyesCount masterName:(NSString*)theMasterName; @end
第4步,打开“Cattle.m”,修改成下面的代码并且保存:
#import "Cattle.h" @implementation Cattle -(void) saySomething { NSLog(@"Hello, I am a cattle, I have %d legs.", legsCount); } -(void) setLegsCount:(int) count { legsCount = count; } -(id)init { [super init]; return [self initWithLegsCount:4 gender:YES eyesCount:2 masterName:@"somebody"]; } - (id)initWithLegsCount:(int) theLegsCount gender:(bool) theGender eyesCount:(int) theEyesCount masterName:(NSString*)theMasterName { legsCount = theLegsCount; gender = theGender; eyesCount = theEyesCount; masterName = theMasterName; return self; } @end
第五步,打开“Bull.m”, ,修改成下面的代码并且保存:
#import "Bull.h" @implementation Bull -(void) saySomething { NSLog(@"Hello, I am a %@ bull, I have %d legs.", [self getSkinColor],legsCount); NSLog(@"I have %d eyes, my master is %@.", eyesCount,masterName); //List below is illegal //NSLog(@"My gender is %@",gender ? @"male" : @"female"); } -(NSString*) getSkinColor { return skinColor; } - (void) setSkinColor:(NSString *) color { skinColor = color; } @end
第六步,打开“07-InitWithAndIvarScope.m”,修改成下面的代码并且保存:
#import <Foundation/Foundation.h> #import "Bull.h" #import "Cattle.h" #import "MyNSObject.h" int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Bull *redBull = [[Bull alloc] initWithLegsCount:4 gender:YES eyesCount:2 masterName:@"that cowboy"]; [redBull setSkinColor:@"red"]; [redBull saySomething]; //legal, but not good redBull->masterName = @"that cowgirl"; //legal, but bad //redBull->eyesCount = 3; //Trying to access a private ivar, VERY bad thing //MyClass bullClass = redBull->isa; bool *redBullGender = (bool *)(redBull) + 8; NSLog(@"My gender is %@",*redBullGender ? @"male" : @"female"); [pool drain]; return 0; }
第 七步,选择屏幕上方菜单里面的“Run”,然后选择“Console”,打开了Console对话框之后,选择对话框上部中央的“Build and Go”,如果不出什么意外的话,那么应该出现入图7-1所示的结果。如果出现了什么意外导致错误的话,那么请仔细检查一下你的代码。如果经过仔细检查发现 还是不能执行的话,可以到这里 下载笔者为同学们准备的代码。 如果笔者的代码还是不能执行的话,请告知笔者。
7.3,实例变量的作用域(Scope)
对于Objective-C里面的类的实例变量而言,在编译器的范围里面,是有作用域的。和其他的语言一样,Objective-C也支持public,private还有protected作用域限定。
如果一个实例变量没有任何的作用域限定的话,那么缺省就是protected。
如果一个实例变量适用于public作用域限定,那么这个实例变量对于这个类的派生类,还有类外的访问都是允许的。
如果一个实例变量适用于private作用域限定,那么仅仅在这个类里面才可以访问这个变量。
如果一个实例变量适用于protected作用域限定,那么在这个类里面和这个类的派生类里面可以访问这个变量,在类外的访问是不推荐的。
我们来看看“Cattle.h”的代码片断:
int legsCount; @private bool gender; //male = YES female = NO @protected int eyesCount; @public NSString *masterName;
第一行的legsCount 的前面没有任何作用域限定,那么它就是protected的。
第二行是在说从第二行开始的实例变量的定义为private的,和其他的关键字一样,Objective-C使用@来进行编译导向。
第三行的gender的作用域限定是private的,所以它适用于private作用域限定。
第四行是在说从第四行开始的实例变量的定义为protected的,同时第二行的private的声明作废。
第五行的eyesCount的作用域限定是protected的,所以它适用于protected作用域限定。
第六行是再说从第六行开始的实例变量的定义为public的,同时第四行的protected的声明作废。
第七行的masterName的作用域限定是public的,所以它适用于public作用域限定。
我们再来看看在派生类当中,private,protected还有public的表现。Bull类继承了Cattle类,笔者改写了一下“Bull.m”用来说明作用域的问题,请参看下面的代码:
-(void) saySomething { NSLog(@"Hello, I am a %@ bull, I have %d legs.", [self getSkinColor],legsCount); NSLog(@"I have %d eyes, my master is %@.", eyesCount,masterName); //List below is illegal //NSLog(@"My gender is %@",gender ? @"male" : @"female"); }
我们来看看第3还有第4行代码,我们可以访问legsCount,eyesCount还有masterName。
在第6行代码当中,我们试图访问gender这个Cattle的私有(private)属性,这行代码产生了编译错误,所以我们不得不注释掉第6行代码。
好的,我们再来看看类的外部private,protected还有public的表现。请同学们打开“07-InitWithAndIvarScope.m”,参考一下下面的代码:
//legal, but not good redBull->masterName = @"that cowgirl"; //legal, but bad //redBull->eyesCount = 3; //Trying to access a private ivar, VERY bad thing //MyClass bullClass = redBull->isa; bool *redBullGender = (bool *)(redBull) + 8; NSLog(@"My gender is %@",*redBullGender ? @"male" : @"female");
在 第二行里面,我们访问了masterName,由于在Cattle里面masterName是public的,Bull继承了Cattle,所以我们可以 直接访问masterName。但是这不是一种好的习惯,因为这不符合面向对象的基本思想。实际上,如果没有特殊的理由,我们不需要使用public的。
第四行,我们试图在类的外边访问protected变量eyesCount,在这里笔者的Xcode只是轻轻的给了一个警告,编译成功并且可以运行。同样,这种在类的外边访问类的protected变量是一个很糟糕的做法。
我 们还记得在Bull的saySomething里面我们曾经试图访问过gender,但是编译器无情的阻止了我们,因为gender是私有的。但是,这仅 仅是编译器阻止了我们,当我们有足够的理由需要在类的外边访问private实例变量的时候,我们还是可以通过一些强硬的方法合法的访问私有变量的,我们 的方法就是使用指针偏移。
我们首先回忆一下第6章 的6.6节的内容,isa里面保存了对象里面的实例变量相对于对象首地址的偏移量,我们得到了这个偏移量之后就可以根据对象的地址来获得我们所需要的实例 变量的地址。在正常情况下,我们需要通过访问类本身和它的超类的ivars来获得偏移量的,但是笔者在这里偷了一个懒,先使用第七行的代码MyClass bullClass = redBull->isa;通过Debugger获得gender的偏移量,数值为8。然后在第8行里面,笔者通过使用指针偏移取得了gender 的指针然后在第9行实现了输出。
由此可见,在Objective-C里面, 所谓的private还有protected只是一个Objective-C强烈推荐的一个规则,我们需要按照这个规则来编写代码,但是如果我们违反了这个规则,编译器没有任何方法阻止我们。
笔者认为在类的外部直接访问任何实例变量,不管这个实例变量是public,private还是protected都是一个糟糕的做法,这样会明显的破坏封装的效果,尽管这样对编译器来说是合法的。
7.4,initWith...
NSObject为我们准备的不带任何参数的init,我们的类里面没有实例变量,或者实例变量可以都是零的时候,我们可以使用NSObject为我们准 备的缺省的init。当我们的实例变量不能为零,并且这些实例变量的初始值可以在类的初始化的时候就可以确定的话,我们可以重写init,并且在里面为实 例变量初始化。
但是在很多时候, 我们无法预测类的初始化的时候的实例变量的初始值,同时NSObject明显无法预测到我们需要什么样的初始值,所以我们需要自己初始化类的实例变量。
请同学们打开“Cattle.m”,我们参考一下下面的代码:
-(id)init { [super init]; return [self initWithLegsCount:4 gender:YES eyesCount:2 masterName:@"somebody"]; } - (id)initWithLegsCount:(int) theLegsCount gender:(bool) theGender eyesCount:(int) theEyesCount masterName:(NSString*)theMasterName { legsCount = theLegsCount; gender = theGender; eyesCount = theEyesCount; masterName = theMasterName; return self; }
从 第3行到第7行,笔者重写了一下init。在init里面,笔者给通过调用initWith,给类的各个实例变量加上了初始值。这样写是很必要的,因为将 来的某个时候,也许有人(或者是自己)很冒失的使用init来初始化对象,然后就尝试使用这个对象,如果我们没有重写init,那么也许会出现一些意想不 到的事情。
从第9行到第19行,是我们自己定义的initWith,代码比较简单,笔者就不在这里赘述了。需要注意的一点是,笔者没有在这里调用[super init]; 。原因是Cattle的超类就是NSObject,初始化的过程就是初始化实例变量的过程,runtime已经为我们初始化好了NSObject的唯一实例变量isa,也就是Cattle的类的信息,所以我们不需要调用[super init]; 。在某些时候,超类的变量需要初始化的时候,请同学们在子类的init或者initWith里面调用[super init]; 。
请同学们再次打开“07-InitWithAndIvarScope.m”,参考下面的代码片断:
Bull *redBull = [[Bull alloc] initWithLegsCount:4 gender:YES eyesCount:2 masterName:@"that cowboy"]; [redBull setSkinColor:@"red"]; [redBull saySomething];
从第1行到第4行就是调用的initWith来初始化我们的redBull。
7.5,本章总结
非常感谢大家对笔者的支持!
我 们在本章里面介绍了2个比较轻松的话题,一个是实例变量的作用域,这个概念笔者个人认为对有一点面向对象编程经验的人来说,不是什么新鲜的概念了。但是需 要注意的是,Objective-C并没有强制我们遵守它的规则,他仍旧为我们提供了违反规则的机会,这一点上根C++比较类似。只要支持指针,就无法避 免使用者违反规则。事务都是一分为二的,当我们得到了访问任何变量的自由之后,我们必须为访问这些变量承担后果。
第二个话题就是initWith。和其他的面向对象的语言不同,Objective-C没有构造函数,它通过init还有initWith来初始化变量, 我们应该根据具体情况进行具体的分析,从而编写我们的init还有initWith方法。
来源: http://www.cnblogs.com/yaski/archive/2009/04/19/1439304.html
- 07-InitWithAndIvarScope.zip (48.1 KB)
- 下载次数: 0
发表评论
-
手机firebug查看网页代码 How to View Website Source Codes on iPad / iPhone
2016-04-26 02:41 1173You’re using your iPad to br ... -
Android模拟器genymotion的安装和使用
2016-03-18 01:16 2534Genymotion概述 Genymotion是一套完整的 ... -
编码之道:取个好名字很重要
2015-10-14 05:19 1178代码就是程序员的孩子,给“孩子”取个好听的名字很重 ... -
Genymotion - 强大好用高性能的 Android 模拟器 (在电脑流畅运行APK安卓软件游戏的利器)
2015-06-05 13:34 7398随着 Android 系统的应用和游戏越来越丰富,甚至有 ... -
收集几个移动平台浏览器的User-Agent
2014-07-23 02:33 3034List of all Mobile Browsers ... -
常用浏览器修改User-Agent的方法
2014-06-28 02:58 10993之前有介绍收集的几个移动平台浏览器的User-Agent,以 ... -
更简洁的方式修改Chrome的User Agent,轻松体验移动版网络
2014-06-28 02:53 6952国庆节在家宅着,使用3g上网卡上网,和在公司上网不一样的是 ... -
3个检测浏览器User-Agent信息的网站
2014-06-28 02:52 2565刚刚在收集各个移动平台浏览器的User-Agent(参见这 ... -
根据移动设备屏幕像素密度,给予不同分辨率的图片
2014-06-26 00:52 2976在出现iphone4之前的相当长的时间内,网站开发人员 ... -
视网膜New iPad与普通分辨率iPad页面的兼容处理
2014-06-26 00:44 1352一、这是篇经验分享 ... -
iOS8开发-Swift编程
2014-06-25 00:45 1916课时列表 章节1:第一个Swift应用 ... -
Swift中文教程(二十三) 高级运算符
2014-06-25 00:43 831除了基本操作符中所讲的运算符,Swift还有许多复杂的高级 ... -
Swift中文教程(二十二) 泛型
2014-06-25 00:35 1076泛型代码可以让你写出根据自我需求定义、适用于任何类型的,灵 ... -
Swift中文教程(二十一) 协议
2014-06-24 05:25 1465Protocol(协议)用于统一方法和属性的名称,而不实现任 ... -
Swift中文教程(二十) 扩展
2014-06-24 00:47 739扩展就是向一个已有的类、结构体或枚举类型添加新功能(fun ... -
Swift中文教程(十九) 类型嵌套
2014-06-24 00:44 935枚举类型常被用于实现特定类或结构体的功能。也能够在有多种变量 ... -
Swift中文教程(十八) 类型检查
2014-06-24 00:41 782类型检查是一种检查类实例的方式,并且或者也是让实例作为它的 ... -
Swift中文教程(十七) 可选链
2014-06-17 05:11 909可选链(Optional Chaining)是一种可以请求 ... -
Swift中文教程(十六) 自动引用计数
2014-06-17 05:05 1295Swift使用自动引用计数(ARC)来管理应用程序的内存使 ... -
Swift中文教程(十五) 析构
2014-06-17 04:57 1041在一个类的实例被释放之前,析构函数会被调用。用关键字dei ...
相关推荐
#### 第 7 章:对象的初始化以及实例变量的作用域 初始化过程对于确保对象的正确创建至关重要,而实例变量的作用域则决定了它们的生命周期。这部分内容将介绍不同类型的初始化方法,以及如何管理实例变量的可见性和...
- **块(Blocks)**:Objective-C 2.0引入了块的概念,这是一种内联函数或闭包,可以捕获并存储其作用域内的变量,增强了代码的可读性和简洁性。 - **垃圾回收(Garbage Collection)**:虽然后来被Automatic ...
第九章进一步讲解了变量和数据类型,包括对象的初始化、作用域、静态变量、枚举数据类型、typedef声明、类型转换和位运算符等。 第十章介绍了类别和协议,类扩展以及委托的概念。本章的内容对于理解如何在Objective...
5. **块(Blocks)**:Objective-C支持块(block),这是一种内联函数或者闭包,可以捕获并存储定义它的作用域内的局部变量。它们在处理异步操作、排序和过滤数组等场景下特别有用。 6. **内存管理**:Objective-C...
- **变量声明与初始化**:变量可以在函数的任何位置声明和初始化,如: ```objective-c int age = 25; ``` - **控制结构**:包括条件语句(如`if`、`else if`、`else`)和循环语句(如`for`、`while`)。 #### ...
用于全局变量时限制其作用域。 - `extern "C"` 在C++中用于告诉编译器按照C语言规则链接符号,常用于C与C++的混合编程。 ### 10. 头文件保护 标准头文件使用 `#ifndef`, `#define`, `#endif` 结构来防止头文件被...
- **Blocks和GCD**:Objective-C支持块(Block),这是一种内联函数,可以捕获并存储其所在作用域的变量。Grand Central Dispatch(GCD)利用 Blocks 实现并发编程,提高性能。 - **Foundation框架**:Objective-C...
- **作用域限制**:限制变量的作用域。 - **静态存储**:为变量分配静态存储空间。 #### 45. GCD(Grand Central Dispatch) - **并发编程**:如何使用 GCD 实现多线程编程。 - **队列**:GCD 中的任务队列模型。 ...
- **Swift**对Objective-C开发者非常友好,因为它保留了Objective-C的命名参数和动态对象模型,并且可以无缝对接到现有的Cocoa框架。 - **Swift**支持过程式编程和面向对象编程,并且对初学者来说也很友好,具有脚本...
- **闭包**:Block是一种闭包,能够捕获并存储外部作用域中的变量。 ### 10. Delegate与代理模式 - **委托模式**:这是一种常见的设计模式,通过定义一个委托协议(Delegate Protocol),让类可以通知其他对象处理...