Objective-C
介绍
OC程序的源文件后缀名为.m
。 m代表message,代表OC的消息机制。OC语言完全兼容C语言,所以在.m
文件中可以写C语言的任何代码,甚至C++的代 码也可以写在.m
文件下。
#import
指令:同一个文件,无论被#import
多少次,在预编译的时候只会包含1次。包括#import ""
和#import <>
。
.m
是Objective-C的源文件。.mm
是Objective-C++的源文件。
.h
是头文件,包含方法,属性的声明。.m
是类的实现文件,参与编译的文件,用来实现类中声明的方法。
Boolean类型数据允许取值true或false,也可以是0或非0的整数替代true和false。
@try {
//可能出错的代码块
}
@catch (NSException *exception) {
// 一旦出了错 可以的补救代码。
}
@finally {
//无论出错不出错都会执行的代
}
面向对象设计四个个主要特征: 抽象性、 封装性、 多态性、 继承性。
NSLog
NSLog 是printf函数的增强版
NSLog(@"Hello World!");
@""
这个符号表示将一个C的字符串转化为OC中的字符串对象NSString。
OC字符串必须使用@符号开头。@
符号放置的位置在双引号外面。在打印OC字符串的时候使用格式控制符%@
。NSString类型的指针变量只能存储OC字符串常量的地址。
OC中大部分的关键字都是以@
开头的,比如@interface
,@implementation
,@end
。
类
.h
文件为类的声明文件,用于声明成员变量、方法。类的声明使用关键字 @interface
和@end
。.h
中的方法只是做一个声明,并不对方法进行实现。
.m
:类的实现文件,用于实现.h
中声明的方法。类的实现使用关键字 @implementation
和@end
。
方法的声明和实现,都必须以+
或者 -
开头。 +
表示类方法(静态方法), -
表示对象方法(动态方法)。
在.h
中声明的所有方法作用域都是public类型,不能更改。
成员变量的常用作用域有3种:
@public 全局都可以访问
@protected 只能在类内部和子类中访问
@private 只能在类内部访问
创建对象
创建对象的写法
类名 * 对象名 = [类名 new];
只要是用new操作符定义的实体就会在堆内存中开辟一个新的空间。1)在堆中开辟一段存储空间 2)初始化成员变量(写在类声明大括号中的属性就叫做成员变量,也叫做实例变量) 3)返回开辟空间的首地址。
访问对象的属性可以用:
对象->对象成员;
方法
oc没有严格的方法重载. 在oc中方法名不允许相同。
OC中的方法分为:对象方法和类方法。
对象方法:对象方法以-
开头如-(void)xx
;对象方法只能由对象来调用;对象方法中可以访问当前对象的成员变量;调用格式[对象名 对象方法名]
。凡事类型都用()括起来。
类方法:以+
开头如 +(void)xxx
;类方法只能由类来调用;类方法中不能访问实例变量(成员变量),因为类方法由类来调用,并没有创建存储空间来存储类中的成员变量;调用格式:[类名 类方法名]
。
有参方法,冒号也是方法名的一部分
//返回值类型 void ; 方法名run: : :,参数有3个,都是int型的,参数的参数名 steps km times
- (void)run:(int)steps :(int)km :(int)times;
// 声明三个带参数的方法,方法名是 runWith: andWith: andWith:
- (void)runWith:(int)steps andWith:(int)km andWith:(int)times;
有参方法的调用:
//[对象名 方法名 参数]
[zhansgan eat:@"辣条"];
// [对象名 方法名:参数:参数];
[zhansgan run:3 :10 :5];
[zhansgan runWith:5 andWith:20 andWith:2];
对象的存储
类创建对象,每个对象在内存中都占据一定的存储空间,每个对象都有一份属于自己的单独的成员变量,所有的对象公用类的成员方法,方法在整个内存中只有一份,类本身在内存中占据一份存储空间,类的方法存储于此。
每一个对象都包含一个isa指针,这个指针指向当前对象所属的类。 当调用方法的时候如[p eat]
表示给p所指向的对象发送一条eat消息,表示要调用对象的eat方法,此时对象会顺着内部的isa指针找到存储于类中的方法,开始执行,此时方法所修改的所有的信息,都是对应的对象的。
pragma mark
#pragma mark -//“-”后面不能随手敲个空格
#pragma mark 分组(标识)名称
常见错误
@interface
@end
和@implementation
@end
不能嵌套包含。
只有类的声明没有类的实现,会报错。
NSString
NSString是Objective-C中核心处理字符串的类之一。
创建常量字符串,注意使用“@”符号:
NSString * str = @"hahahaha";
NSString * str2 = [[NSString alloc] initWithString:str];
创建空字符串,给予赋值:
NSString * str1 = [NSString new ];
str1 = @"啊哈哈哈";
创建格式化字符串:占位符(由一个%加一个字符组成):
NSString * str3 = [NSString stringWithFormat:@"图片 xxxx %02d- %02d.jpg",9,1];
NSLog(@"Hello, World! %@ ",str3);//图片 xxxx %09- %01.jpg
计算字符串长度
NSLog(@"字符串的长度 %ld", [str2 length]);
对象与对象之间的关系
一个对象作为另外1个对象的成员变量
//女孩有一部手机
@interface Girl :NSObject
{
Phone *_phone;
}
@end
//女孩用手机打电话
@interface Girl :NSObject
- (void)callWithPhone:(Phone *)phone;
@end
组合关系:1个对象是由多个对象组合起来的。比如:计算机对象,是由主板对象、CPU对象、内存对象、硬盘对象组合起来的。
依赖关系:就是一个对象要1件事情的时候必须有另一个对象。比如,B类是A类⽅方法的参数,我们就说A类依赖于B类。
关联关系:当一个对象拥有另外一个对象的时候, 当B对象为A对象的成员变量的时候,B对象与A对象之间存在一种关联关系。
面向对象设计原则
单一职责原则、开放封闭原则
类方法
OC中的方法分为两种
对象方法
以“-”减号开头的方法就是对象方法。对象方法的调用,必须创建对象,然后通过对象名去调用。
声明
-(返回值类型)方法名:(参数类型)参数名称;
- (void)square : (int)num;
类方法
以+
加号开头的方法叫做类方法。这个方法不依赖于对象,不需要创建对象来调用,而是直接使用类名调用。
+(返回值类型)方法名:(参数类型)参数名称;
+ (void)square : (int)num;
类方法使用注意事项: 1. 类方法中不能访问成员变量。 2. 类方法和对象方法可以同名。 3. 类方法中不能通过self调用同名方法,会死循环。 4. 对象方法当中可以通过类名直接调用类方法。 5. 在没有属性时,建议使用类方法。
匿名对象
没有名字的对象。
self关键字
self是一个指针变量,用于在方法中指向调用该方法的对象。
self的应用场景 1)用在类方法中,代表当前类。 2)用在对象方法中,代表当前对象。 3)访问成员变量:self->成员变量
。
self使用注意
在对象方法中
【self 对象方法】; // 死循环
在类方法中
【self 对象方法】; // 死循环
封装
封装:把复杂的数据或者操作进行隐藏,只操作数据或者方法的接口。封装的好处:隐藏成员变量,不让外部直接访问,提高安全性。控制外界访问成员权限。
set方法
命名规范:方法名必须以set开头。set后面跟成员变量名称,成员变量的首字母必须大写。返回值一定是void。一定要接收一个参数,而且参数类型跟成员变量类型一致。形参的名称不能跟成员变量名一样。
- (void)set成员变量名 (首字母大写,去掉下划线) : (成员变量类型)成员变量名称 (去掉下划线);
- (void)setAge:(int)age;
get方法
命名规范:肯定有返回值,返回值类型肯定与成员变量类型一致。方法名跟成员变量名一样。不需要接收任何参数
格式:
- (成员变量类型)成员变量名称(去掉下划线);
- (int) age;
使用
// Student类的声明
@interface Student : NSObject
{
int _age;
}
- (void)setAge:(int)newAge;//set方法
- (int)age;//get方法
@end
//Student类实现:
@implementation Student //setter方法实现
- (void)setAge:(int)newAge
{
_age = newAge;
}
//getter方法
- (int)age
{
return _age;
}
@end
调用:
Student *s = [Student new];
// 设置age的值
[s setAge:10];
// 取出age的值
int age = [s age];
// 输出
NSLog(@"age is %d", age);
类的继承和派生
OC中的继承是单继承:也就是说一个类只能一个父类,不能继承多个父类。子类不能定义和父类同名的成员变量,但是可以继承父类的变量。
基类的私有属性@private
能被继承,不能被使用。@public
公有成员能被继承,也能被外部方法访问。@protected
保护成员能够被继承、在子类中使用,但不能够被外部函数访问。在@interface @end
之间声明的成员变量如果不做特别的说明,那么其默认是 protected的。
在类的实现即.m
文件,@implementation
中也可以声明成员变量,是@private
的。
方法的重写:从父类继承的方法,可能这个方法并不适合子类,可以在子类中重写父类的方法。
@interface Animal : NSObject
{
int _tuiNum; //腿的个数
int _eyeNum; //眼睛个数
}
-(void)setTuiNum:(int)tuiNum;
-(int)tuiNum;
-(void)setEyeNum:(int)eyeNum;
-(int)eyeNum;
-(void)eat;
-(void)run;
@end
#import "Animal.h"
@interface Dog : Animal
{
}
-(void)eat; //覆盖父类的eat的方法
@end
重写之后,父类的对象调用父类的方法;子类的对象,调用子类的方法,不会引起冲突。从父类继承的方法,不适用于子类时,可以直接声明父类的同名方法,并定义。不用考虑父类中,方法已存在的问题。
重写后,子类可以通过super调用父类的方法。
- (void)eat
{
[super eat];
}
私有方法
OC中并没有像Java中提供的私有方法,OC中的私有,可以理解为相对私有。
方法在.m
中实现,不在.h
中声明,此时该方法被称为私有方法:私有方法不可以被子类继承
@interface MyClass
{
// 添加变量
}
- (void)PublicMethod;//公共方法,可以被继承类继承
@end
// 而在类的.m文件中,直接实现的方法:
- (void)PrivateMethod
{
////
}
@end
多态
多态:同一种行为,对于不同的事物具有不同的表现形式。多态的条件:有继承关系、有方法重写。
父类的声明变量指向子类对象。如果存在多态,父类是可以访问子类特有的方法。
类对象的用法
可以用来调用方法
Dog *d = [Dog new];
Dog *d1 = [Dog new];
Class c = [d1 class];
// 用类名调用类方法
[Dog test];
[c test];
可以用来初始化对象
Class c = [Dog class];
[c test];
// 用类对象创建对象
Dog *d = [c new];
[d eat];
SEL方法选择器
SEL类型作用:可以定义变量;可以用来作为方法的形参;可以用来作为方法的实参。
Static关键
static修饰局部变量:延长布局变量的生命周期。
static修饰全局变量:当前变量只能在当前文件中使用。
static修饰函数:函数只能在当前文件中使用。
使用static修饰实例变量是不被允许的。
使用static修饰方法也是不被允许的。
使用static@interface和@end
之间是不被允许的(写局部变量本来就不被允许)。
//狗类的声明
static int m=10; //此句话也可以放到.m中
@interface Dog:NSObject
{
int _speed;
}
-(void)run;
@end
// Dog.m文件内容
#import "Dog.h"
#pragma mark 狗类的实现
@implementation Dog
-(void)run{
//定义局部静态变量 m
static int m=10;
NSLog(@"m = %d",m);
m++;
}
@end
注意Static型全局变量的可见性,局限于当前.m文件,其他的文件中的类,无法访问到该变量。
点语法
点语法是编译器特性,当编译器看到对象使用点语法,会自动把点语法转换为调用set或get方法的形式。
Student *stu = [Student new];
// 设置age的值
stu.age = 10; // 等价于[stu setAge:10];
// 取出age的值
int age = stu.age; // 等价于int age = [stu age];
// 输出
NSLog(@"age is %i", age);
self在set方法中使用
- (void)setAge:(int)age
{
self.age = age;
}
错误用法,会死循环。
上面的代码相当于:
- (void)setAge:(int)age
{
[self setAge:age];
}
self在get方法中使用
- (int)age
{
return self.age;
}
错误用法:死循环。
相当于:
- (int)age
{
return [self age];
}
property关键字
@property
是编译器的指令。它告诉编译器,声明属性的set、get方法。好处是:免去我们手工书写get和set方法繁琐的代码。
格式:
@property 类型 方法名
@property int age;
//相当于进行了age的set和get方法的声明
-(void)setAge:(int)age;
-(int)age;
synthesize关键字
如果@synthesize
变量名要先在.h
文件中声明
@property int age;
.h
-(void)setAge:(int)age;
-(int)age;
.m
@synthesize age;展开形式如下:
-(void)setAge:(int)age
{
self->age = age;
}
-(int)age
{
return age;
}
@property和@synthesize搭配使用,用于简化set和get方法的定义和实现。
id类型
id类型应用场景
//狗的类,狗继承自动物
Dog *dog = [Dog new];
[dog run];
//定义多态类型
NSObjcet *dog1 = [Dog new];
[(Dog *)dog run];
//定义id类型
id dog2 = [Dog new];
[obj run];
id作为成员变量
// 类的声明
@interface Person : NSObject
@property int age;
@property id obj; // 可以接收任何对象
@end
// 类的实现
@implementation Person
@end
// main函数
int main()
{
Person *p = [Person new];
[p setObj:@"小王子"];
NSLog(@"%d",[p obj]);
return 0;
}
构造方法
构造方法:用来初始化对象实例变量值的方法,是个对象方法,-
开头。
完整的创建一个可用的对象:
Person *p=[Person new];
new方法的内部会分别调用两个方法来完成3件事情:使用alloc方法来分配存储空间(返回分配的对象);使用init方法来对对象进行初始化;返回对象的首地址。
可以把new方法拆开如下:调用类方法+alloc分配存储空间,返回未经初始化的对象
Person *p1=[Person alloc];
调用对象方法-init进行初始化,返回对象本身
Person *p2=[p1 init];
以上两个过程整合为一句:
Person *p=[[Person alloc] init];
init方法就是构造方法,是用来初始化对象的方法,注意这是一个对象方法,以减号开头。默认初始化完毕后,所有成员变量的值都为0。
自定义构造方法的代码实现
问题1:给Hero类定义一个构造方法,自定义名字信息。
// 声明
@property NSString *name;
- (instancetype)initWithName:(NSString *)name;
// 实现
- (instancetype)initWithName:(NSString *)name
{
if (self = [super init])
{
_name = name;
}
return self;
}
@end
问题2:给Hero类定义一个构造方法,自定义名字信息和年龄。
// 声明
@property NSString *name;
@property int age;
- (instancetype)initWithName:(NSString *)name andAge:(int)age;
// 实现
- (instancetype)initWithName:(NSString *)name andAge:(int)age
{
if (self = [super init])
{
_name = name;
_age = age;
}
return self;
}
@end
NSMutableArray
数组长度不固定,可以随便往里面添加或者删除元素。
创建可变数组
NSMutableArray *arrayM = [NSMutableArray array];
数组添加成员
- (void)addObject:(ObjectType)object;
删除数组成员
// 用成员名进行删除
- (void)removeObject:(ObjectType)object;
// 删除指定位置的元素
- (void)removeObjectAtIndex:(NSUInteger)index;
swift
处理值缺失的情况
?
代表可能值确实,可以使用if let
或者??
来处理。
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
//如果不是 nil,会将值解包并赋给 let 后面的常量
if let name = optionalName {
greeting = "Hello, \(name)"
}
print(greeting)//Hello, John Appleseed
??
前面的变量为nil
,则用??
后面的变量;??
前面的不为nil
,则用??
前面的变量。
let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"
print(informalGreeting)//Hi John Appleseed
快速上手
使用let
来声明常量,使用var
来声明变量。
int转String
let label = "The width is"
let width = 94
let widthLabel = label + String(width)
或者
let apples = 3
let appleSummary = "I have \(apples) apples."
使用方括号[]
来创建数组和字典。数组添加元素用append
。
初始化一个空数组或者空字典:
let emptyArray = [String]()
let emptyDictionary = [String: Float]()
使用 for-in
、while
和 repeat-while
来进行循环。
switch:不需要在每个子句结尾写 break。
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
字典是一个无序的集合,所以他们的键和值以任意顺序迭代结束。
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
while
var n = 2
while n < 100 {
n *= 2
}
print(n)//128
var m = 2
repeat {
m *= 2
} while m < 100
print(m)//128
..<
来表示下标范围,不包含上界。如果想包含的话需要使用 ...
:
var total = 0
for i in 0..<4 {
total += i
}
print(total)//6
条件循环:
for i in 1...10 where i % 2 == 0{
print(i)
}
倒叙遍历:
for i in (1...10).reversed(){
print(i)
}
生成随机数:
Int.random(in: 1...6)//1-6之间随机数
四舍五入double,返回double:
round(4.44) //4.0
使用func
来声明一个函数,使用->
来指定函数返回值的类型。
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
print(greet(person:"Bob", day: "Tuesday"))//Hello Bob, today is Tuesday.
使用元组来生成复合值,比如让一个函数返回多个值。该元组的元素可以用名称或数字来获取:
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let statistics = calculateStatistics(scores:[5, 3, 100, 3, 9])
print(statistics.sum)//120
print(statistics.2)//120
两个参数名时候的外部参数和内部参数:
func numSun(wai nei : Int) {
print(nei)
}
numSun(wai: 3)
func numSun(_ nei : Int) {
print(nei)
}
numSun(3)
函数可以嵌套。
函数可以作为另一个函数的返回值:
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)//8
函数也可以当做参数传入另一个函数:
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
var bol = hasAnyMatches(list: numbers, condition: lessThanTen)
print("\(bol)")
闭包:
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
//或者
var numbers = [20, 19, 7, 12]
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)//[60, 57, 21, 36]
使用 class 和类名来创建一个类。
要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法。
使用 init 来创建一个构造器。
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
如果需要在对象释放之前进行一些清理工作,使用 deinit 创建一个析构函数。
使用 enum 来创建一个枚举。
使用 struct 来创建一个结构体。结构体是传值,类是传引用。
使用 protocol 来声明一个协议。mutating。extension。
其他
给 Image View
重新设置Assets资源图片:
@IBOutlet weak var leftImage: UIImageView!
leftImage.image = UIImage(named: "show2")
摇晃手机结束:
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
}
设置view的tag,然后同一个方法中区分:
@IBAction func tagBtn(_ sender: UIButton) {
print(sender.tag)
}
播放音频:
import AVFoundation
var player: AVAudioPlayer!
func palyAV() {
let url = Bundle.main.url(forResource: "文件名", withExtension: "格式名")
do{
player = try AVAudioPlayer(contentsOf: url!)
player.play()
} catch {
}
}
弹出框:
let alter = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
alter.addAction(UIAlertAction(title: "ok", style: .default, handler: {(_) in
print("点击了ok")
}))
present(alter,animated: true,completion: nil)
动态设置宽度:
rightImage.frame.size.width = view.frame.width / 10
UI
control拖拽控件,可以选择Outlet和Action。
Autoresizing:设置上下左右宽高六个属性。
stackview
页面控件:View Controller
。输入框:Text Field
。
技巧
图标制作网站:
https://appicon.co/
快捷键
command + R 编译运行
command + m 最小化
command + z 撤销
command + d 复制粘贴出来一个选中的ui控件
command + 删除键 光标在一行的最后位置,删除整行/删除光标前的内容
局域网真机调试:
Window-Devices and Simulators
iOS
https://github.com/dequan1331/HybridPageKit