objective-c 语法快速过(4)

时间:2021-04-15 06:44:00

oc 里的字符串

字符串的快速创建(最简单的方法)

NSStirng *str = @“Hello”;//oc的字符串都是@“”形式的

oc的字符串也是类的对象,是NSString类的对象,创建没有那么麻烦不用[ ],使用面向对象的思想来操纵字符串。

 char *name = "xxxxx";//c风格字符串

oc使用 %@ 输出字符串,不是%s

NSString *name = @”dashuai”;

NSLog(@“我的名字是%@”,  name);

c 风格字符串输出解析的用法是%s

NSLog(@"%s", name);//%s是c风格的格式化解析

OC字符串的另一方式,Foundation 框架里的 NSString 类

NSString *newStr = [NSString stringWithFormat:@"My age is %d and no is %d and name is %@", age, no, name];

NSLog(@"---- %ld", [newStr length]); 
length方法,返回unsigned long类型
 NSString *name = @"哈哈达到";
// length方法算的是字数总数,包含的文字的数目!不是求字符个数!!
int size = [name length];

不懂就去查询文档!百度!

//  main.m
#import <Foundation/Foundation.h>
/*
NSString : 不可变字符串
NSMutableString : 可变字符串
*/
int main()
{
NSMutableString *s1 = [NSMutableString stringWithFormat:@"my age is 10"];
// 拼接内容到s1的后面
[s1 appendString:@" 11 12"]; // 获取is的范围
NSRange range = [s1 rangeOfString:@"is"];
[s1 deleteCharactersInRange:range]; NSString *s2 = [NSString stringWithFormat:@"age is 10"]; //新建字符串,拼接好的字符串到新建的字符串里,但是原来的字符串没有变!两个方法不一样。
NSString *s3 = [s2 stringByAppendingString:@" 11 12"];
NSLog(@"s1=%@, s2=%@", s1, s2); return ;
} void stringExport()
{
// 字符串的导出
[@"Jack\nJack" writeToFile:@"/Users/apple/Desktop/my.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil]; NSString *str = @""; NSURL *url = [NSURL fileURLWithPath:@"/Users/apple/Desktop/my2.txt"]; [str writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:nil];
} void stringCreate()
{
/*
1.字符串的创建
*/
NSString *s1 = @"jack"; //NSString *s2 = [[NSString alloc] initWithString:@"jack"];这种方法不推荐! NSString *s3 = [[NSString alloc] initWithFormat:@"age is %d", ]; // C字符串 --> OC字符串
NSString *s4 = [[NSString alloc] initWithUTF8String:"jack"];
// OC字符串 --> C字符串
const char *cs = [s4 UTF8String]; // NSUTF8StringEncoding 用到中文就可以用这种编码
NSString *s5 = [[NSString alloc] initWithContentsOfFile:@"/Users/apple/Desktop/1.txt" encoding:NSUTF8StringEncoding error:nil]; // URL : 资源路径
// 协议头://路径
// 本地文件 file://
// ftp://
// 网页资源 http://weibo.com/a.png
// http://www.baidu.com // NSURL *url = [[NSURL alloc] initWithString:@"file:///Users/apple/Desktop/1.txt"];
NSURL *url = [NSURL fileURLWithPath:@"/Users/apple/Desktop/1.txt"]; NSString *s6 = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSLog(@"s6=\n%@", s6); /*
一般都会有一个类方法跟对象方法配对
[NSURL URLWithString:<#(NSString *)#>];
[NSString stringWithFormat:@""];
[NSString stringWithContentsOfFile:<#(NSString *)#> encoding:<#(NSStringEncoding)#> error:<#(NSError *__autoreleasing *)#>];
*/
}

OC的 set 和 get 方法

类的成员变量尽量不要@public,保持数据的隐蔽性和封装性,使用对象直接访问类的成员变量也不推荐,因为这样类的成员变量可以被随意的修改,不安全,而是应该间接的调用。并且直接调用会让别人一下子就看到了类的内部结构和信息,失去了封装的意义。

set方法

1)   作用:用来设置成员变量,可以在方法里面过滤掉一些不合理的值

2)   命名规范:

  • 方法名都是以set开头,而且后面跟上成员变量名,成员变量名的首字母必须大写
  • 一定接受参数,且形参名称不要跟成员变量同名
  • 返回值void

get方法

1)   作用:返回对象内部的成员变量

2)   命名规范:get方法的名称一般就跟成员变量同名

/*成员变量的命名规范:一定要以下划线 _ 开头
@interface Student : NSObject
{
1.让成员变量和get方法的名称区分开
2.可以跟局部变量区分开,一看到下划线开头的变量,一般都是成员变量
*/
int _no;
Sex _sex;
}
// sex的set和get方法
- (void)setSex:(Sex)sex;
- (Sex)sex;
// no的set和get方法
- (void)setNo:(int)no;
- (int)no;
@end

oc特有的语法—兼容c系列的点语法

可以使用点语法来代替oc古怪的对象调用的形式。这是oc特有的让其他(主要是习惯了 c 系列编程语言的程序员,比如 c,c++,java等)程序员快速看懂的一个做法。

 // 点语法的本质还是方法调用
p.age = ; // [p setAge:10];

objective-c 语法快速过(4)

objective-c 语法快速过(4)

这里还是古怪,到底是get还是set方法,要看是给它赋值了(set),还是被赋值了(get)。还是和java或c++的点语法区分。

oc 中类的成员变量的作用于

@public : 在任何地方都能直接访问对象的成员变量

@private : 只能在当前类的对象方法中直接访问。私有成员只能在本类被直接访问,子类不可见,只能是set和get方法访问(@implementation中默认是@private,因为一般不会包含实现文件,都是包含头文件,故实现文件默认是私有的)

@protected : 可以在当前类及其子类的对象方法中直接访问  (@interface中默认就是@protected)

@package : 只要处在同一个框架中,就能直接访问对象的成员变量,不常用

@interface和@implementation中不能声明同名的成员变量,同名冲突,很好理解

Xcode的一个特性@property和@synthesize

sɪnθɪˌsaɪz v综合合成;(通过化学或生物)合成;(音响)合成

@property

自动生成set和get方法,类似java的eclipse的 setter 和 getter选项

#import <Foundation/Foundation.h>
@interface Person : NSObject
{
int _age;
int _height;
double _weight;
NSString *_name;
}
// @property:只能用在声明里,可以自动生成某个成员变量的setter和getter声明,并且和java还不一样,xcode把自动的声明隐藏,不可见。这是编译器的特性!
下面这一句,代表注释的那两句。
@property int age;
//- (void)setAge:(int)age;
//- (int)age;
@property int height;//先生成set再get
//- (void)setHeight:(int)height;
//- (int)height;
- (void)test;
@property double weight;
//如果成员变量类型一样,可以合并在一起写,但是实际开发不这样,分开比较好
@property NSString *name;
@end

注意:@property里的变量age如果没有被提前声明,那么也是可以运行的,编译器会自动生成_age成员变量,xcode4.4版本之后有的。这样生成的成员变量,在类声明里是@private。若类声明里存此成员变量,如果@property还是那么写,就不会再自动生成。

@synthesize

只写在实现里。可以自动去类声明里找@property,然后根据它去生成类的成员变量的setter和getter方法的实现,并且会访问这个成员变量

@synthesize age = _age;

objective-c 语法快速过(4)

如果这样写,默认会访问age这个成员变量,如果没有age,就会自动生成@private类型的age变量,都是自动存在类声明里。

注意:xcode4.4之后,为了让程序员把精力放在业务逻辑上,而不是无谓的垃圾代码的书写上,xcode变得越来越方便,做的东西也越多,现在可以省略类的成员变量的声明,直接@property就可以自动生成变量和配套的set与get方法的声明。同样,4.4之后,类的实现里,@synthesize也不用写了。也就是说,set和get的实现不用写。

意味着,现在的@property 声明,类声明里的@property默认做两个事情,即生成set和get的声明,也生成set和get的实现。当然,如果类声明里没有变量声明,那么还要做第三个事情,自动生成私有的成员变量。

Xcode原则就是:没有,我替你生成,有我就直接用。

这里就可以解释为什么苹果建议类的成员变量前面加下划线?

因为@property 的新特性:自从 xcode4.4之后,@property 就独揽了@synthesize 的功能,也就说,@proeprty 可以同时生成成员变量的 set,get 方法的声明和实现,两个都能。默认的情况下,set 和 get 方法的实现,会去访问下划线的成员变量,故要求类的声明里使用下划线的变量命名方式命名成员变量。说到底:就是因为新特性的@property自动生成set,get方法的声明和实现,默认访问的都是前面带下划线的成员变量。

oc 里的 id 类型(万能指针)

id 是一种类型

类型不能做变量名,好比 int  int;是错误的,一个道理。

Id 是万能指针

能指向(操纵)任何的 oc 对象,而且不能加*,只写id就代表指针(因为 id内部已经包含*,见官方文档)。

objective-c 语法快速过(4)

 Person *p = [Person new];//传统的操作人的对象,人类型指针p 指向人的对象

万能指针,id能指向 or 操作任何OC对象,只适合用在 oc 对象上

id d = [Person new];//id 指向人的对象      

id 的缺点:

如果同样是指向(调用)类不存在的对象方法,那么编译器直接报错!不会弱语法了

千万别加*

oc 的构造方法和重写

构造方法:用来初始化对象的方法,oc里是个对象方法,-开头  -init

面向对象的语言,都包含的东西---构造方法,用和 c++类似的new 这个类方法创建对象,其实不好。

// Person *p = [Person new];不推荐这样创建对象

new 其实是类方法,是 NSObject 类的方法,可以创建对象,分配内存,但是事实上,我们不常用,因为太死板,做的事情很单调,就是完整的创建一可用对象。

完整地创建一个可用的对象其实分两步:

1.分配存储空间  +alloc

2.初始化 -init

new 方法做这两个事情,new 方法内部,调用两个方法(一个类方法,一个对象方法)完成这个步骤,细节为:

1.调用+alloc分配存储空间

Person *p1 = [Person alloc];//此时对象不可用,没有初始化,返回 id(oc 对象)

2.调用-init进行初始化

Person *p2 = [p1 init];反回 oc 对象(已经初始化之后)本身

说明 new 是两个方法的联合,开发不常用,比较死板,一般分两步做,这样后果是方便选择。因为初始化不一定必须是 init对象方法初始化。。

两步合并的简单写法:掌握,开发常用这个写法,从此不用 new 了。

Person *p4 = [[Person alloc] init];

Oc的构造方法就是-init 方法,用来初始化对象的方法。对象方法,每个对象内部的成员变量默认都初始化为0,

创建 person 对象,保证她的_age 默认都是10,不是0,怎么做?

需要重写 –init方法,覆盖父类 –init方法

重写构造方法的目的:

为了让对象创建出来,成员变量就会有一些固定的值

重写构造方法的注意点

1.先调用父类的构造方法([super init])

2.再进行子类内部成员变量的初始化,和 c++类似。

Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;
@end Person.m
#import "Person.h"
@implementation Person
// 重写-init方法,其实 id 有些类似 c++的模板的应用。
//- (id)init
//{
// 1、一定要先调用super的init方法:先初始化父类中声明的一些成员变量和其他属性
self = [super init]; // 返回的是当前oc对象,一定用 self接受,这里需要好好研究,先知道。
// 2.如果父类对象初始化成功,才有必要进行接下来的初始化
// if (self != nil)
// {
// _age = 10;
// }
//
// 3.返回一个已经初始化完毕的oc对象
// return self;
//}
//综合的写法,熟练之后如下这样写:
- (id)init
{
if ( self = [super init] )
{
_age = ;
}
return self;
}
@end

要求:

学生类的学生对象,初始化完毕之后,年龄都是10,学号都是1.还是重写构造方法-init

Student.h继承 person 类
#import "Person.h"
@interface Student : Person
@property int no;
@end Student.m
#import "Student.h"
@implementation Student
// 学生对象初始化完毕后,年龄就是10,学号就是1
- (id)init
{
if ( self = [super init] )
{
_no = ;
}
return self;
}
@end main.h
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
int main()
{ // 每个stu对象创建出来,他的_age都是10,学生学号都是1,isa 指向本身
每个 p4对象创建出来,她的 _age 都是10,isa 指向本身类
Person *p4 = [[Person alloc] init];
Student *stu = [[Student alloc] init];
return ;
}

student 继承了 person ,而 person继承了 NSObject,比如 student 对象内部有三个成员变量,分别为: student 自己的_no,和继承自父类 person 类的_age,还有 person 最开始就继承的父类NSObject 的 isa指针,这个三个成员变量,都是 student 对象 stu 包含的!

objective-c 语法快速过(4)

因为student 类 创建 student 对象的时候,先进入 student 类查找-init,(因为 oc 里规定,如果父类和子类有同名方法,子类对象优先调用子类同名方法),然后 在 student 类里发现- (id)init方法,之后又发现:

{

    if ( self = [super init] )这句话,那么编译器会率先进入到父类 person,因为 super 的作用

    {

        _no = ;

    }

    return self;

}

然后先调用直接父类 person 的 –init,而 person 的-init为:

- (id)init

{

    if ( self = [super init] )还是执行到此,我们发现调试就进不去了,因为 super 调用的是 NSObject,苹果是不开源的,但是事实还是进去偷着执行 NSobject 的-init 方法,初始化isa 指针, 把 isa 初始化为指向student 类本身,记住是当前的类,初始化完毕,程序执行回到 person 类,继续向下执行_age=。(同时student 的成员变量 age 变为10)

    {

        _age = ;

    }

    return self;最后返回的 self 指向的是 当前student 的对象

}

当person的-init 调用完毕,最后才回到 子类student 类,继续执行 student 的-init, 执行_no=1,继承来的 isa 初始化为指向本身 student 类,这才初始化完毕。

person 对象初始化的时候,同理,先在本类查找初始化方法,发现 super 语句,然后再去父类 NSObject 类 里查找-init方法,(这里先记住 self=[super init]的写法,下来再深入),NSObject 做一件事,那就是初始化 isa,把 isa 指向 person 类本身,(指向当前类,这个类是毕源的,看不到内部代码)然后回到 person类,_age=10。执行完毕。

objective-c 语法快速过(4)

再看一看 student 对象,创建的时候,先初始化,同理在本类,然后 发现 super,那么去父类 person 执行-init,还是发现 super,还是去 NSObject执行-init,把 isa 指向 student 类本身,(当前类不是 person,是 student 了)然后回到 person,_age=10.回到student,_no=1,执行完毕,再回main 函数,初始化,完毕。

oc构造方法的自定义

oc自定义构造方法的规范

1.一定是对象方法, 故一定以 - 开头

2.返回值一般是id类型

3.方法名一般以initWith开头

这让我想起了,c++构造函数的含参构造和默认构造,和不带参数的构造,意义是类似的。其实就是带参构造。和 c++大同小异。

Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property NSString *name;
@property int age;
- (id)initWithName:(NSString *)name;
- (id)initWithAge:(int)age;
- (id)initWithName:(NSString *)name andAge:(int)age;
@end Person.m
#import "Person.h"
@implementation Person
- (id)initWithName:(NSString *)name
{
if ( self = [super init] )固定写法,记住
{
_name = name;
}
return self;
}
- (id)initWithAge:(int)age
{
if ( self = [super init] )
{
_age = age;
}
return self;
}
- (id)initWithName:(NSString *)name andAge:(int)age
{
if ( self = [super init] )
{
_name = name;
_age = age;
}
return self;
}
@end Student.h继承 person 类
#import "Person.h"
@interface Student : Person
@property int no;//_no 是子类 student 自己的属性
- (id)initWithNo:(int)no;
- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no;
@end student.m
#import "Student.h"
@implementation Student
- (id)initWithNo:(int)no
{
if ( self = [super init] )
{
_no = no;
}
return self;
}
// 要理解和接受这个做法和思想:
// 父类的属性交给父类方法去处理,子类方法处理子类自己的属性
- (id)initWithName:(NSString *)name andAge:(int)age andNo:(int)no
{
// 将name、age传递到父类方法中进行初始化,no 在本类初始化
使用了自定义的构造方法
if ( self = [super initWithName:name andAge:age])
{
_no = no;
}
return self;
}
@end main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Student *p = [[Student alloc] initWithName:@"Jim" andAge: andNo:];
}
return ;
}

每个对象创建之后,都有一个默认名,年龄,和学号,但是每个人的结果不一样。此时,需要使用自定义构造方法初始化。否则重写构造方法初始化的话(不带参数)导致名称都是一样的。不符合程序要求。

首先,创建 student 对象,初始化对象指针 p(指向对象),先在本类找 initWithName 方法,确实有,然后进入执行,将name和age(都是继承来的属性)传到父类方法中进行初始化:这样分工明确(其实和 c++一样的思路),自己做自己的事情,体现了良好的架构思想,还有这样做的好处是,如果我们都统一在子类初始化,那么一旦父类的成员变量名称改变了,会导致子类的代码出错,需要修改,一旦工程较大,后果不堪设想,而分工明确,自己做自己的事情,把父类的成员让父类去初始化,子类只管自己的,就能做到不变应万变。

objective-c 语法快速过(4)

欢迎关注

dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

objective-c 语法快速过(4)