React-Native结构

一:了解index.iOS.js

大家都清楚,React-Native就是在开发效率和用户体验间做的一种权衡。React-native是使用JS开发,开发效率高、发布能力强,不仅拥有hybrid的开发效率,同时拥有native app相媲美的用户体验。目前天猫也在这块开始试水。

用编辑器打开index.ios.js文件,分析代码结构:

  1. 第一句:var React = require('react-native');

    有Node.js开发经验的同学都清楚,require可以引入其他模块。如果没有node.js
    开发经验的同学,可以脑补下java的import和c++的#include。
    
  2. 第二句代码,批量定义组件:

    1
    2
    3
    4
    5
    6
    var {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    } = React;

其实,这只是一个语法糖而已,比如AppRegistry我们可以这样定义:var AppRegistry = React.AppRegistry;

  1. 构建Heollo World入口类。

    React提供了React.createClass的方法创建一个类。里面的render方法就是渲染视图用的。return返回的是视图的模板代码。其实这是JSX的模板语法,可以提前学习下。
    
  2. 相对于web开发,我们需要提供视图的样式,那么StyleSheet.create就是干这件事的,只是用JS的自面量表达了css样式。

  3. 如何引入css样式?

    其实在render方法返回的视图模板里已经体现出来了,即style={styles.container}.
    其中style是视图的一个属性,styles是我们定义的样式表,container是样式表中的一个样式。
    
  4. 注册应用入口,这个一定不能少,否则模拟器会提示报错:
    AppRegistry.registerComponent('HelloWorld', () => HelloWorld);

二:需要的知识


对于React-Native开发,仅仅有基础前端开发的知识是不够的,你还需要了解和掌握的有:

Node.js基础 JSX语法基础
* Flexbox布局

需要关注的文件

  1. 目前阶段有几个文件时需要注意下的:
1
2
3
4
5
6
1> 在xcode项目代码中AppDelegate.m会标识入口文件,例如:jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"];
如果是网上下载别人的源码,注意此处的ip和端口是否有被修改。

2>闪屏界面在哪修改?在xcode项目中找到LaunchScreen.xib文件,点击,你会看到界面,这个就是启动界面,你手动添加组件或者修改文本即可,最好了解下xcode的使用。

3>文本编辑器打开index.ios.js文件,是js代码的入口文件,所有的代码编写从这开始,可以定义自己的模块和引入第三方模块。

四:修改文件index.ios.js

  1. 修改启动界面,如下图
    启动界面

  2. 添加图片和修改样式.

    我们在第一篇的demo基础上修改。去掉第二个和第三个,增加我们需要的图片,因为图片更具表达力,就像最近的图片社交应用很火一样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    1> 添加Image组件,将代码修改成如下即可:
    var {
    StyleSheet,
    Text,
    View,
    Image,
    } = React;

    2> 将render返回中的模版增加Image组件视图,具体如下:
    render: function() {
    return (
    <View style={styles.container}>
    <Text style={styles.welcome}>
    React-Native入门学习
    </Text>
    <Image style={styles.pic} source={{uri: 'https://avatars3.githubusercontent.com/u/6133685?v=3&s=460'}}>
    </Image>
    </View>
    );
    }
    其中,Image标签的source的第一个大括号是模板,第二个大括号是js对象,js对象里面有个key是uri,表示图片的地址。

    3> 修改图片视图的样式,删除多余的样式,增加pic样式:
    var styles = StyleSheet.create({
    container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
    },
    welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
    color: 'red',
    },
    pic: {
    width:100,
    height:100,
    }
    });
    4> 可以cmd + Q 停止模拟器,然后再cmd + R开启模拟器,你会发现启动界面和首页都你想要的样子:

首页

React-Native初识

React-Native初识

前提


Node.js基础 JSX语法基础
* Flexbox布局


环境配置

  1. 安装node.js下载mac版
  2. 获取Homebrew.打开终端输入下列命令:
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    若提示缺少组件,输入 brew install wget
  3. 建议安装watchman,终端命令:brew install watchman
  4. 安装flow,终端命令:brew install flow

通过以上步骤可配置好环境

第一个React-Native终端项目

  1. 安装命令行工具,终端:sudo npm install -g react-native-cli
  2. 进入你想要的目录,创建项目: react-native init <项目名> 如:react-native init HelloWorld ca,第一次真慢,尼玛10多分钟了
  3. 进入目录打开<项目名>.xcodeproj
    运行 如下图:

1_1

FMDB

FMDB

FMDB学习还是直接看源码更好,这里只是做一点简单的学习和翻译工作FMDB GIT 源码;

FMDB有三个主要的类

  • <FMDatabase> 是用来执行SQL语句的类,是一个单一的SQLite数据库

  • <FMResultSet> 是一个用FMDatabase类执行查询语句后返回的结果集

  • <FMDatabaseQueue> 在多线程执行查询和更新时使用的类.

创建数据库

创建数据库需要指定一个唯一路径作为SQLite数据库的目录.
这个路径可以是下面三种情况之一:

  • 文件路径,这个路径可以不存在,如果不存在则会创建一个给你
  • 可以是空字符串<@"">.传入空字符串会在临时目录创建一个空数据库,当FMDatabase连接关闭时,会自动删除这个数据库
  • 可以传nil,会在内存中创建一个数据库.当FMDatabase连接关闭时,会自动删除这个数据库.
路径
1
2
3
4
5
6
7
8
9
10
11
12
13
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

NSString *dbPath = [docsPath stringByAppendingPathComponent:@"test.db"];

FMDatabase *db = [FMDatabase databaseWithPath:dbPath];

//访问修改数据库必须保证数据库是打开的,这样才能保证数据库的读写.

//打开数据库,如果没有,会创建一个数据库.
if(![db open]) {

return;
}

Demo

Demo链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
//创建单表,简单demo.若数据庞大有依赖关系,通常为了方便管理,会创建多个table,如订单和订单详情这两个table.
//
// Created by 李泽宇 on 16/3/21.
// Copyright © 2016年 丶信步沧桑. All rights reserved.

#import "XBViewController.h"
#import "FMDB.h"
#import "XBStatusBarHUD.h"
#import "XBDatabase.h"
#import <objc/runtime.h>


@interface XBViewController ()

// 属性名 对应 字段名. 因下面想练习一下runtime,顾数据库中的表的字段名与属性名保持一致,来保证条件查询时,sql语句的拼接
@property (weak, nonatomic) IBOutlet UITextField *name;
@property (weak, nonatomic) IBOutlet UITextField *age;
@property (weak, nonatomic) IBOutlet UITextField *height;


// 单例FMDatabase
@property (nonatomic, strong) XBDatabase *db;

@end

@implementation XBViewController

/// 懒加载
- (FMDatabase *)db {

if (!_db) {

_db = [XBDatabase sharedDatabaseManager];

}

return _db;
}


- (void)viewDidLoad {

[super viewDidLoad];

//访问修改数据库必须保证数据库是打开的,这样才能保证数据库的读写.
//打开数据库,如果没有,会创建一个数据库.
if (![self.db open]) {
NSLog(@"失败");
return;
}

// 拼接创建表的sql语句, 字段名与属性名一致.
NSString *tableString = @"CREATE TABLE IF NOT EXISTS T_Person ('Id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 'name' TEXT, 'age' INTEGER, 'height' REAL)";

// executeUpdate: 此方法用于创建, 更新, 插入..返回值代表成功与否
BOOL isSuccess = [self.db executeUpdate:tableString];

// 判断成功失败
if (!isSuccess) {
[XBStatusBarHUD message:@"创表失败"];
return;
}

// XBStatusBarHUD: 还未完成的小小指示器,会在statusBar弹出一个window显示提示内容.
[XBStatusBarHUD message:@"创表成功"];

}

#pragma -Mark - 监听事件
/// 保存/插入/添加按钮
- (IBAction)saveButtonClick:(id)sender {

// 插入数据
BOOL isSave = [self.db executeUpdate:@"INSERT INTO T_Person (name, age, height) VALUES (?, ?, ?)",self.name.text,self.age.text,self.height.text];

// 判断成功与否
if (!isSave) {
// 回顾一下 UIAlertView, UIAlertView在iOS_9.0过期. UIAlertController iOS_8.0开始
if ([UIDevice currentDevice].systemVersion.doubleValue >= 9.0) {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"友情提示" message:@"保存失败" preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];

[alert addAction:action];

[self presentViewController:alert animated:YES completion:nil];

} else {

UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"友情提示" message:@"保存失败" delegate:self cancelButtonTitle:@"确定" otherButtonTitles: nil];

[alertView show];
}
}

[XBStatusBarHUD message:@"保存成功"];

}


/// 全部查询
- (IBAction)queryButtonClick:(id)sender {

NSString *selectSQL = [NSString stringWithFormat:@"SELECT id, name, age, height FROM T_Person;"];

// executeQuery: 返回一个FMResultSet类型的结果集合.
FMResultSet *resultAll = [self.db executeQuery:selectSQL];

// 创表时id为 自增长 主键; 数据库若有数据,id可取值. 通过返回的resultAll调用intForColumn: 传入字段名(列名) 来查询这一列的值
if (![resultAll intForColumn:@"id"]) {

[XBStatusBarHUD message:@"数据库无数据"];

return;
}

// 可通过循环调用 next 函数, 来遍历查询结果;
while ([resultAll next]) {

int Id = [resultAll intForColumn:@"id"];

NSString *name = [resultAll stringForColumn:@"name"];

int age = [resultAll intForColumn:@"age"];

double height = [resultAll doubleForColumn:@"height"];

NSLog(@"ID----%d, name----%@, age----%d, height----%.2f",Id, name, age, height);
}

}

/// 条件查询
- (IBAction)criteriaqueriesButtonClick:(id)sender {

// if (![self.db open]) {
// return;
// }

// 拼接SQL语句, 此处拼接[self sqlString]使用运行时
NSString *SQL =[NSString stringWithFormat:@"SELECT * %@", [self sqlString]];

// 查看拼接的SQL语句
NSLog(@"%@",SQL);

FMResultSet *resultAll = [self.db executeQuery:SQL];

// 判断是否查询到数据
if (![resultAll intForColumn:@"id"]) {
[XBStatusBarHUD message:@"查无此项"];
return;
}

while ([resultAll next]) {

int Id = [resultAll intForColumn:@"id"];

NSString *name = [resultAll stringForColumn:@"name"];

int age = [resultAll intForColumn:@"age"];

double height = [resultAll doubleForColumn:@"height"];

NSLog(@"ID----%d, name----%@, age----%d, height----%.2f",Id, name, age, height);
}

[XBStatusBarHUD message:@"查询成功"];
}

/// 删除语句.
- (IBAction)deleteButtonClick:(id)sender {

// if (![self.db open]) {
// return;
// }

// 字段若为TEXT = '' 一定要加单引号,不然会造成 text类型找不到错误.而INTEGER,REAL类型不会,为防止错误,所有字段的值都加''
NSString *deleteSQL = [NSString stringWithFormat:@"DELETE %@",[self sqlString]];

BOOL isDelete = [self.db executeUpdate:deleteSQL];

if (!isDelete) {

[XBStatusBarHUD message:@"删除失败"];

return;
}

[XBStatusBarHUD message:@"删除成功"];
}


#pragma -Mark - Runtime 拼接字符串
- (NSString *)sqlString {


unsigned int outCount;


Ivar *ivars = class_copyIvarList([XBViewController class], &outCount);


NSMutableDictionary *dictM = [[NSMutableDictionary alloc]init];



for (int i=0; i<outCount; i++) {

Ivar ivar = ivars[i];

const char *type = ivar_getTypeEncoding(ivar);

NSString *typeOc = [[NSString stringWithUTF8String:type] substringWithRange:NSMakeRange(2, 11)];

if (![typeOc isEqualToString:@"UITextField"]) {
continue;
}


const char *field = ivar_getName(ivar);

// NSLog(@"%s",field);

NSString *fieldString = [[NSString stringWithUTF8String:field] substringFromIndex:1];


UITextField *value = [self valueForKey:fieldString];



if ([value.text isEqualToString:@""]) {
continue;
}

// NSLog(@"-----%@",value.text);

[dictM setValue:value.text forKey:fieldString];

}

NSMutableString *sqlM = [NSMutableString stringWithString:@"FROM T_Person WHERE "];

[dictM enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// NSLog(@"key-%@: value-%@",key,obj);

[sqlM appendFormat: @"%@ = '%@' AND ",key ,obj];

}];


NSUInteger lenth = sqlM.length;

NSRange range = NSMakeRange(0, lenth - 5);

NSString * sql = [NSString stringWithFormat:@"%@;",[sqlM substringWithRange:range]];



return sql;
}

Runtime链接

Runtime整理

Classes

1
2
3
4
5
6
7
8
9
10
11

1.class_getName

作用:获得一个类的类名;

声明:const char * class_getName(Class cls)

参数:类对象(Class cls)

return: 返回一个char类型类对象的name, 如果参数是空,则返回
Nil;
1
2
3
4
5
6
7
8
9
10
11

2.class_getSuperclass

声明:Class class_getSuperclass(Class cls)

作用:获得一个类的父类

参数:类对象(Class cls);

return:返回父类类对象,如果参数是基类(如NSObject)或者Nil,返
回Nil;
1
2
3
4
5
6
7
8
9
10

3.class_setSuperclass

声明:Class class_setSuperclass(Class cls, Class newSuper)

作用:给传入的类设置父类;

参数:想要更改父类的类对象(Class cls), 传入的类对象(Class cls)的新的父类

return:返回传入对象的原来的父类.
1
2
3
4
5
6
7
8
9
4.class_isMetaClass

声明:BOOL class_isMetaClass(Class cls)

作用:判断一个对象是否是元类

参数:类对象

return: 若参数类对象是元类返回YES;若不是或参数类对象为Nil,返回Nil
1
2
3
4
5
6
7
8
9
5.class_getInstanceSize

声明:size_t class_getInstanceSize(Class cls)

作用:返回一个实例对象的大小 单位是byte

参数:类对象

return:返回类对象的大小,单位为byte.若参数为Nil,返回0
1
2
3
4
5
6
7
8
9
6.class_getInstanceVariable

声明:Ivar class_getInstanceVariable(Class cls, const char* name)

作用:获得传入的类对象的指定name的实例变量;

参数:类对象(Class cls), 传入类对象的实例变量的名称(const char* name)

return: 返回一个指针,指向传入类对象的指定name的实例变量.Ivar指针包含有变量的信息,名字,类型,及距离一个对象实例本身地址的偏移.
1
2
3
7.class_getClassVariable --- OC中好像没有类实例变量?

声明:Ivar class_getClassVariable(Class cls, const char* name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
8.class_addIvar

声明:BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types)

作用:给一个类添加一个新的实例变量

官方文档:This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

这个函数只能用在objc_allocateClassPair 和 before objc_registerClassPair之间,并且不能给一个已经存在的类添加实例变量;

The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.

这个类不能是元类,不支持给元类添加成员变量;

The instance variable's minimum alignment in bytes is 1<<align. The minimum alignment of an instance variable depends on the ivar's type and the machine architecture. For variables of any pointer type, pass log2(sizeof(pointer_type)).
这个实例变量的最小字节数是1<<align. 这上边写的什么玩意,特么看不懂.

参数: 1.想要动态添加新成员变量的类对象.
2.新的实例变量的name.
3.实例变量的大小
4.alignment
5.参考<a>https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html>类型编码</a>
参数3,4 可通过以下代码获得(NSGetSizeAndAlignment("*", &size, &alignment);) //"*" const char *types 见上述类型编码
NSUInteger size;
NSUInteger alignment;
NSGetSizeAndAlignment("*", &size, &alignment);
创建两个NSUInteger变量,传入地址.得到两个变量值.

return: 添加成功返回YES.失败NO.
1
2
3
4
5
6
7
8
9
9.class_copyIvarList

声明:Ivar * class_copyIvarList(Class cls, unsigned int *outCount)

作用:获取传入类的所有属性和实例变量,及实例变量总个数

参数:类对象, 传入无符号整形变量地址.返回实例变量总个数

return:返回一个指针数组,数组储存所有实例变量的Ivar指针(见6.return:)
1
2
3
4
5
6
7
8
9
10.class_getProperty

声明:objc_property_t class_getProperty(Class cls, const char *name)

作用:返回传入类的指定属性.

参数:类对象, 属性名

return:objc_property_t类型的指针

运行时常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1> <objc/runtime.h>
* Method class_getClassMethod(Class cls,SEL name)
获得某个类的类方法

* Method class_getInstanceMethod(Class cls,SEL name)
获得某个类的对象方法

* void method_exchangeImplementations(Method m1, Method m2)
交换2个方法的实现(重点,iOS 中的黑魔法Swizzle)

* void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
将值value根对象object关联起来(将值value存贮到对象object中)
参数key: 将来可以通过key去除这个存储的值
参数policy: 存贮策略(assign,copy,retain)

* id objc_getAssociatedObject(id object,sonst void *key)
利用参数key将对象object中存储的对应值取出来

* Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
获得某各类的所有成员变量(outCount会返回所有成员变量的总数)

* const char *ivar_getName(Ivar v)
获得成员变量的名字

* const char *ivar_getTypeEncoding(Ivar v)
获得成员变量的类型

* void free(void *);
释放内存
(当C语言函数名中包含了copy,create,retai,new等词语,都需要在最后释放资源)



2> <objc/message.h>
* void objc_msgSend(void)
给某个对象发送某个消息

运行时常见应用

  • 动态添加对象的成员变量和成员方法
  • 动态交换两个方法的实现(特别是交换系统自带的方法)
  • 获得某个类的所有成员方法,所有成员变量

代码

动态交换两个方法的实现(特别是交换系统自带的方法)

1
2
3
4
5

//交换方法实现
Method m1 = class_getClassMethod([Person class], @selector(run));
Method m2 = class_getClassMethod([Person class], @selector(study));
method_exchangeImplementations(m1, m2);

-

交换方法实现,兼容版本具体应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "UIImage+Extension.h"
#import <objc/runtime.h>

@implementation UIImage (Extension)
//交换方法
+(void)load{
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(ac_imageNamed:));
method_exchangeImplementations(m1, m2);
}
+(UIImage *)ac_imageNamed:(NSString *)name{
double version = [[[UIDevice currentDevice] systemVersion]doubleValue];
if (version >= 7.0) {
name = [NSString stringWithFormat:@"%@_iOS7",name];
}
return [UIImage ac_imageNamed:name];
}
@end

-

给分类增加属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#import <Foundation/Foundation.h>

@interface NSObject (Extension)
@property(nonatomic,copy)NSString *name;
@end


#import "NSObject+Extension.h"
#import <objc/runtime.h>


@implementation NSObject (Extension)


char NameKey;
-(void)setName:(NSString *)name{

objc_setAssociatedObject(self, &NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

-(NSString *)name{
return objc_getAssociatedObject(self, &NameKey);
}


@end

-

获取类的所有成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned int outCount = 0;
//获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList([Dog class], &outCount);
// 遍历所有的成员变量
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"%s %s",name,type);
}
//释放内存
free(ivars);

NSLog(@"%zd",outCount);

-

运行KVC时归档解档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#import "Dog.h"
#import <objc/runtime.h>
@interface Dog : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) double height;
@property (nonatomic, assign) double weight;
@property (nonatomic, assign) double aaa;
@property (nonatomic, assign) double bbb;
@property (nonatomic, assign) double ccc;
@end

#import "Dog.h"
#import <objc/runtime.h>
@implementation Dog

/*
这个数据的成员变量,不会被归档和解档
*/
-(NSArray *)ignoredNames{
return @[@"_aaa",@"_bbb",@"_ccc"];
}

/// 从文件中读取对象时会调用这个方法(说明需要读取的属性)
-(instancetype)initWithCoder:(NSCoder *)aDecoder{

if (self = [super init]) {

//用来存储成员变量的数量
unsigned int outCount = 0;

//获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList([self class], &outCount);

// 遍历所有的成员变量
for (int i = 0; i < outCount; i++) {
// 取出i位置对应的成员变量
Ivar ivar = ivars[i];

NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
//忽略解档对象
if ([[self ignoredNames] containsObject:key])
continue;
//获得key对应的值
id value = [aDecoder decodeObjectForKey:key];

//设置到成员变量上
[self setValue:value forKeyPath:key];
}

//释放内存
free(ivars);

}
return self;



}

/// 将对象写入文件时会调用这个方法(说明哪个属性需要存储)
-(void)encodeWithCoder:(NSCoder *)aCoder{

//用来存储成员变量的数量
unsigned int outCount = 0;
//获得Dog类的所有成员变量
Ivar *ivars = class_copyIvarList([self class], &outCount);
// 遍历所有的成员变量
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
//忽略归档对象
if ([[self ignoredNames] containsObject:key])
continue;
id value = [self valueForKeyPath:key];

[aCoder encodeObject:value forKey:key];
}

//释放内存
free(ivars);
}

//viewController点击方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

Dog *dog1 = [[Dog alloc]init];
dog1.age = 20;
dog1.height = 1.55;
dog1.name = @"wangcai";
dog1.weight = 50;
//将dog归档到桌面
[NSKeyedArchiver archiveRootObject:dog1 toFile:@"/Users/Chenzhaoshen/Desktop/dog.hm"];

//解档
Dog *dog = [NSKeyedUnarchiver unarchiveObjectWithFile:@"/Users/Chenzhaoshen/Desktop/dog.hm"];
NSLog(@"%@,%d,%f,%f",dog.name,dog.age,dog.height,dog.weight);

}

-

待续…

存了很长时间的一点随记

随记

1.ios9之后默认不支持http, 要求必须是https 并且服务器HTTP协议必须使用1.2版本及以上.
https 在传输数据时 通过二进制层级加密. 传输的是加密数据.

要禁止Ios9阻止 不安全传输.
在Info 中,app Transport Security Settings 添加一个子类 如图.设置YES

Snip20160307_3.png

或右键info.plist 用原代码的方式打开并输入

Snip20160307_6.png
Snip20160307_7.png

1
2
3
4
5
   <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

2.在Iphone中 上传 字符串形式的二进制图片,上传不成功, 解决办法:base64编码

3.封装了一个NSURLConectionDataDelegete 下载,但

4.swift 1.2中范围用 0..9    在2.0中用0...9

5.一定不要相信自己的代码是正确的,想办法测试一下.避免错误

6.在scrollView及其子类下使用 touchbegin 不会被调用.
 因为scrollView 中有手势,而手势是对touch的封装,所以往往有手势的地方会屏蔽touch的使用.

1
2
3
4
7.多线程, 完成回调在主线程执行代码的方法:
>1.  [self performSelectorOnMainThread: withObject: waitUntilDone:];
>2.  [self performSelector: OnThread:[NSThread mainThread] withObject: waitUntilDone:  ]
>3.   dispatch_async(dispatch_get_main_queue(),^{})

8.子控件的alpha值 不能比 父控件的高; 若子控件alpha值大于父控件alpha值 则以父控件为基准.

9.如果layer.contents中的像素超出了contents的边界 那么contents支持向外延伸显示.(在此感谢Jayden)

Snip20160307_10.png

大致意思是: 如果需要像素显示在长方形之外, contents image 将会支持延伸..
信步英语水平大概3.1级,请将就..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
10.消息转发机制

运行时
调用方法意味着发送消息

//导入框架 <objc/runtime.h>


1>.自己做一些处理
+(BOOL)resolveInstanceMethod:(SEL)sel{

if(self==@selector(run)){

class_addMethod(self,sell,(IMP)eat,@)
}


}


2>.实现消息转发
-(id)forwardingTargetForSelector:(SEL)aSelector{
return [[Cat alloc]init];
}


3>.完整消息转发
// 给方法加个签名

-(NSMethodSignature *)methhodSignatureForSelector:(SEL)aSelector{

}

11.UIAlerViewController 中 的style属性中 取消style  --  Cancle 只能有一个. 否则会崩溃;

12.UIWindow 的显示: 显示最后一个hidden为NO的 window;  并不是显示keyWindow;  覆盖; 创建Window,hidden属性默认为YES;

13.description方法是 NSObject的 对象方法 +(NSString *)description;   NSLog中 通过%@ 打印对象 就是调用的description方法;
 重写此方法时,不要在此方法内同时写 "%@",''self''  会死递归

14.tabBarController中 通过 自身 viewControllers 数组属性的角标 实现跳转,没有modal  show  push方法;      push是NavigationController的自有方法.

15.修改tabBar或者tabBarItem中的 setTintColor 属性 会把系统默认的两个控制条的渲染颜色都更改;

1
2
3
16.字典的枚举遍历
enumerateKeysAndObjectsWithOptions: usingBlock:
enumerateKeysAndObjectsUsingBlock:

17.分类中不能添加成员变量{NSString *sdf}     能添加属性( @property) 但是要自己实现set get方法  分类中添加属性的其实只是添加了set get方法的声明;

18.Dealloc在哪个线程执行?

一个对象的dealloc方法会在该对象的引用计数变为0 的线程被调用;

19.在.m中创建的@property 实例变量 可用KVC拿到.(运行时)

20.info.plist文件设置

不懂英文,一直在欧陆,就不翻译了…附iOS keys 官方文档


21.应用程序启动原理

这是一篇关于程序启动原理和UIApplication的博客


22.NSOperation 的依赖关系  可以根据UI界面的顺序来实现优先下载;

23.NSSeesion 可后台下载,也可在程序关闭之后下载;

1
2
24.
double version = [[UIDevice currentDevice].systemVersion doubleValue];//表示当前版本号;

25.方法 + (void)load :当某个类或者某个分类加载进内存中时 调用一次; (只会加载一次所以只会调用一次);
加载实在编译时进行的,只要程序中包含某个类的头文件,就会被加载.

26.block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”,他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。

转载..应该是ChenYilong大牛的,链接就只附到这吧…具体地址烦请自行查找,更能学习很多东西;


1
2
3
27.测试动画时间
let anim = toolbar.layer.animationForKey("position")
print("动画时长 \(anim?.duration)")

28.给uiview添加MotionEffect

就像在Home页的壁纸,随着手机角度移动,壁纸会动。

好像是iOS7出现的.
1
2
3
4
UIInterpolatingMotionEffect * xEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
xEffect.minimumRelativeValue = [NSNumber numberWithFloat:-40.0];
xEffect.maximumRelativeValue = [NSNumber numberWithFloat:40.0];
[img addMotionEffect:xEffect];

29.获取正在运行的程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#import <sys/sysctl.h>


- (NSArray *)runningProcesses {

int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
size_t miblen = 4;

size_t size;
int st = sysctl(mib, miblen, NULL, &size, NULL, 0);

struct kinfo_proc * process = NULL;
struct kinfo_proc * newprocess = NULL;

do {

size += size / 10;
newprocess = realloc(process, size);

if (!newprocess){

if (process){
free(process);
}

return nil;
}

process = newprocess;
st = sysctl(mib, miblen, process, &size, NULL, 0);

} while (st == -1 && errno == ENOMEM);if (st == 0){

if (size % sizeof(struct kinfo_proc) == 0){
int nprocess = size / sizeof(struct kinfo_proc);

if (nprocess){

NSMutableArray * array = [[NSMutableArray alloc] init];

for (int i = nprocess - 1; i >= 0; i--){

NSString * processID = [[NSString alloc]initWithFormat:
@"%d", process[i].kp_proc.p_pid];

NSString * processName = [[NSString alloc]initWithFormat:
@"%s", process[i].kp_proc.p_comm];

NSDictionary * dict = [[NSDictionary alloc]initWithObjects
:[NSArray arrayWithObjects:processID, processName
,nil] forKeys:[NSArray arrayWithObjects:
@"ProcessID",
@"ProcessName",
nil]];

[array addObject:dict];
}

free(process);
return array;
}
}
}

return nil;
}

NSArray * processes = [self runningProcesses];
for (NSDictionary * dict in processes){
NSLog(@"%@ - %@", [dict objectForKey:@"ProcessID"],
[dict objectForKey:@"ProcessName"]);
}
获取所有已经安装的app
1
2
3
4
5
6
7
8
9
10
#include <objc/runtime.h>

Class LSApplicationWorkspace_class=objc_getClass("LSApplicationWorkspace");

NSObject* workspace = [LSApplicationWorkspace_class performSelector:
@selector(defaultWorkspace)];

NSLog(@"apps: %@", [workspace performSelector:@selector(allApplications)]);

转载..当时直接copy了...链接就....

30.NSNotification必须配对出现


NSNotification有add,必须在dealloc中remove,否则会造成循环引用。另外注意 NSNotification 中的target是阻塞执行的。执行的线程和poster(发送者)一致,如果想更新UI,注意应在main_thread中执行。

31.NSTimer 的引用问题: 记得invalidate

必须清除NSTimer,否则造成循环引用

32.滑动的时候隐藏navigationbar

    navigationController.hidesBarsOnSwipe = Yes

33.多态   父类指针可以接收实例化子类  如 界面传值.  

    UIViewController = segue.destinationViewController;
    这里的segue.destinationViewController  可能是一个任意的UIViewController的子类;

1
2
3
4
5
6
7
34.重写set方法 参数可以随机, 下面是重写了UIImage *image的set方法.
- (void)setImage:(NSString *)image {
if (_image != [UIImage imageNamed: image]) {
[_image release];
_image = [[UIImage imageNamed: image] retain];
};
}

35.UIControl 的子类: UIButton UIPickerControl  segmentControl 等

36.修改tabBar或者tabBarItem中的 setTintColor 属性 会把系统默认的两个控制条的渲染颜色都更改;

37.分类可以添加属性,但系统不会帮我们自动创建带下划线的实例变量也不会帮我们实现 set get方法, 只会声明set get方法;

38.Button  的contentEdgeInsets,imageEdgeInsets,titleEdgeInsets属性 可改变Button图片的偏移位置. 甚至可超出button的范围显示,详见第9条;

39.UIImageView 中 animationImages 数组可添加 动画图片; 并通过设置动画执行时间属性animationDuration 和 动画重复次数animationRepeatCount 来设置动画. 再手动调用开始动画,即可实现动画效果 [imageView startAnimating];

1
2
3
4
5
6
7
8
9
10
11
12
13
40.设置button中 image属性图片 的拉伸形式:
1>.加载背景图片 
2>.更改模式 
3>.设置给button

  1).UIImage *image;   

//若只想把图片部分拉伸,而不是全部拉伸,就要用此函数
2).[image sretchableImageWithLiftCapWidth:  toCapHight:] 
//leftCapWidth:左侧不拉伸区域
//topCapHeight:上方不拉伸区域

3).添加

41.这是以前记下的找了一上午的Bug,留念..
使用storyBoard加载多控制器不能用alloc init  需要先加载storyBoard 然后通过storyBoard instentiate加载一个控制器. 这样创建的控制器才能加载到你搭建的界面;

42.添加子控制器的 push方法是导航控制器的方法,只有导航控制器及子类可以使用; show方法是UIViewController的,所有继承自他都可以使用.

43.modal 与 push    有关联用push   有关联指的是 UINavigationController子控制器;

1
2
3
4
5
6
7
8
9
    44.统一设置导航栏颜色或 其他全局的外观,需要在AppDelegate中 通过外观代理获取自身的对象 来设置. 外观代理: appearance   
例如统一设置导航栏外观:

UINavigationBar *bar = [UINavigationBar appearance];   

[bar setBackgroundImage:[UIImage imageNamed:@"NavBar64"] forBarMetrics:UIBarMetricsDefault];  
NSDictionary *dict = @{NSForegroundColorAttributeName:[UIColor whiteColor]};   
[bar setTitleTextAttributes:dict];
[bar setTintColor:[UIColor whiteColor]];

45.应用程序启动先执行main函数,根据第三个参数创建 UIApplication根据第四个参数创建AppDelegate并赋值给application的delegate属性,然后根据info.plist加载Main.storyboard及LaunchScreen.storyboard 若没有指定Main.storyboard 就会进入AppDelegate 执行application: didFinishLaunchingWithOptions: 

1
2
3
    46.如何保持图片最原始的效果,不要渲染

[image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
47.

// 没想到什么,暂用字典转模型随便写一下..

#import "XBModel"

@implementation XBModel
// 此为父类方法
+ (instanstype)modelWithDict {

     id obj = [[self alloc]init];
          [obj setValuesForKeysWithDictionary:dict];
     
return obj;
}

子类中: //
若子类没有重写父类方法,那么调用父类的方法,父类中不使用[self alloc]创建,直接用[XBModel alloc]
会返回一个父类对象,使子类指针指向父类.

若是只用self则会 父类指针指向子类对象. 即用id又用self则会很灵活,因为返回值是instanstype.

48.block速记符号: inline block,能够快速敲出一个block的基本结构

49.队列优先级被设置成 DISPATCH_QUEUE_PRIORITY_BACKGROUND之后,会被设置成后台级别的队列,它会等待所有比它级别高的队列中的任务执行完或CPU空闲的时候才会执行自己的任务。例如磁盘的读写操作非常耗时,如果我们不需要立即获取到磁盘的数据,我们可以把读写任务放到后台队列中,这样读写任务只会在恰当的时候去执行而不会影响需要更改优先级的其他任务,整个程序也会更加有效率。

50.viewDidLoad中 还没有进行自动布局,所以控件的Frame是设置的Frame, 而viewDidLayoutSubviews里执行完[super viewDidLayoutSubviews]之后,才是布局后的真正想要的Frame;也可在后续的viewDidApear等controller生命周期方法中获得

51.Block   本质是结构体,指向结构体的指针

52.模型继承于NSObject 为了使用KVC

53.ctr +r 缩排

54.四句话讲明白Swift闭包:

    1>闭包是一段准备好的代码块,在需要的时候执行
    2>闭包是没有名字的函数
    3>函数是有名字的闭包
    4>只要类型就可以赋值

    Swift中没有指针的概念

55.重写属性的set方法 不仅可在 赋值之后做一些事情 也可根据需求在赋值之前做一些事情.  Swift 中对应 didSet{}   willSet{}

1
2
3
4
5
56.

[[UIImage imageNamed:@"uusername"imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
//图片是白色,代码之后显示绿色,  加上
imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal后显示正常

简书链接