MarkDown的CSS实现配置

灵感

起源于我们现有的博客引擎主题交互很不错,但是排版烂的要死 ,我水平有限这里只是浅显介绍实现修改我们的markdown编译器的一些排版样式

研究方向

自定义一个非标准化,有其他多元素的MarkDown解析器 如下几点:

  • 可选框
    - [ ]
  • 本地图片索引,可控制对齐及大小
    ![Alt text](http://path/to/img.jpg "optional title" 100x200)
    ![Alt text](./1449756974449.png)
  • 标签功能
    @(Share)[css, Markdown]
  • 代码高亮(不同语言)

swift

1
2
3
4
5
private var majorModel = PickMajorModel()
private var subjectModel = OFFKeyNameModel()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

objectivec

1
2
3
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kScreenW, kScreenH)];
scrollView.showsVerticalScrollIndicator = NO;
[scrollView addSubview:self.downMainView];

有一种惨不忍睹的即视感
Alt text

MarkDown来源及实现

Markdown is a plain text format for writing structured documents, based on conventions used for indicating formatting in email and usenet posts. It was developed in 2004 by John Gruber, who wrote the first markdown-to-html converter in Perl, and it soon became widely used in websites. By 2014 there were dozens of implementations in many languages.

见知乎上回答
实现一个markdown解析器需要具备那些知识

###如何简单的改善文字编排的效果

更换博客主题(简单粗暴)

我们使用的博客引擎Hexo来举例,列举下面三个主题

  • landscape
  • hexo-theme-vno-master
  • hexo-theme-yilia-master
更换MarkDown编译器的主题

Mou 举例子,它提供了多套markdown语法下的排版样式
手动新创建一个CSS文本布局配置 Blog
Alt text

CSS配置文件修改

详细配置参数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
html { font-size: 62.5%; }
html, body { height: 100%; }
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 150%;
line-height: 1.3;
color: #f6e6cc;
width: 700px;
margin: auto;
background: #27221a;
position: relative;
padding: 0 30px;
}

多级标题配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
h1 {
font-size: 28px;
color: black; }
h2 {
font-size: 24px;
border-bottom: 1px solid #cccccc;
color: black; }
h3 {
font-size: 18px; }
h4 {
font-size: 16px; }
h5 {
font-size: 14px; }
h6 {
color: #777777;
font-size: 14px; }

表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
table {
padding: 0;border-collapse: collapse; }
table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }
table tr:nth-child(2n) {
background-color: #f8f8f8; }
table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
table tr th :first-child, table tr td :first-child {
margin-top: 0; }
table tr th :last-child, table tr td :last-child {
margin-bottom: 0; }

代码高亮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
code, tt {
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px; }
pre code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent; }

IM中输入框的优化方案实现

IM中输入框的优化方案实现

以下图的输入框为例

stand

输入框的呈现方式选择

以下两个方法对比

  • Keyboard的inputAccessoryView

相对推荐的方法,有更好的丰富的交互效果

可以与scrollView的一些属性直接绑定

  • InputView的frame适应

传统的方法

优点:灵活控制显示位置

缺点:过多的计算frame

###InputAccessoryView方式

案例见GitHub的Demo

iOS的输入源都有输入源(keyboard及keyboard的配件InputAccessoryView),那通常的方法是输入源控件(Textfield、TextView等)使用一般的InputView的frame适应

解释几个技巧点

  • InputView( 输入源 )的父视图作为该InputView的InputAccessoryView,要避免相互引用
  • 巧用第三方不可见的InputView在适当时间点转移第一响应者给可见的InputView
  • 完成编辑时去除第一响应者(注意iOS9下键盘响应逻辑视图层级都发生了变化)

主要代码

simulator screen shot nov 27 2015 4 17 38 pm

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
// 单例初始化
+ (UUInputAccessoryView*)sharedView {
static dispatch_once_t once;
static UUInputAccessoryView *sharedView;
dispatch_once(&once, ^ {
sharedView = [[UUInputAccessoryView alloc] init];
sharedView->btnBack = [UIButton buttonWithType:UIButtonTypeCustom];
sharedView->btnBack.frame = CGRectMake(0, 0, UUIAV_MAIN_W, UUIAV_MAIN_H);
[sharedView->btnBack addTarget:sharedView action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
sharedView->btnBack.backgroundColor=[UIColor clearColor];
UIToolbar *toolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, UUIAV_MAIN_W, 44)];
sharedView->inputView = [[UITextField alloc]initWithFrame:CGRectMake(UUIAV_Edge_Hori, UUIAV_Edge_Vert, UUIAV_MAIN_W-UUIAV_Btn_W-4*UUIAV_Edge_Hori, UUIAV_Btn_H)];
sharedView->inputView.borderStyle = UITextBorderStyleRoundedRect;
sharedView->inputView.returnKeyType = UIReturnKeyDone;
sharedView->inputView.clearButtonMode = UITextFieldViewModeWhileEditing;
sharedView->inputView.enablesReturnKeyAutomatically = YES;
sharedView->inputView.delegate = sharedView;
[toolbar addSubview:sharedView->inputView];
sharedView->assistView = [[UITextField alloc]init];
sharedView->assistView.delegate = sharedView;
sharedView->assistView.returnKeyType = UIReturnKeyDone;
sharedView->assistView.enablesReturnKeyAutomatically = YES;
[sharedView->btnBack addSubview:sharedView->assistView];
sharedView->assistView.inputAccessoryView = toolbar;
sharedView->BtnSave = [UIButton buttonWithType:UIButtonTypeCustom];
sharedView->BtnSave.frame = CGRectMake(UUIAV_MAIN_W-UUIAV_Btn_W-2*UUIAV_Edge_Hori, UUIAV_Edge_Vert, UUIAV_Btn_W, UUIAV_Btn_H);
sharedView->BtnSave.backgroundColor = [UIColor clearColor];
[sharedView->BtnSave setTitle:@"确定" forState:UIControlStateNormal];
[sharedView->BtnSave setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[sharedView->BtnSave addTarget:sharedView action:@selector(Done) forControlEvents:UIControlEventTouchUpInside];
[toolbar addSubview:sharedView->BtnSave];
});
CGRectGetHeight([UIScreen mainScreen].bounds);
return sharedView;
}

实现逻辑代码

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
+ (void)showKeyboardType:(UIKeyboardType)type content:(NSString *)content Block:(UUInputAccessoryBlock)block
{
[[UUInputAccessoryView sharedView] show:block
keyboardType:type
content:content];
}
- (void)show:(UUInputAccessoryBlock)block keyboardType:(UIKeyboardType)type content:(NSString *)content
{
UIWindow *window=[UIApplication sharedApplication].keyWindow;
[window addSubview:btnBack];
inputBlock = block;
inputView.text = content;
inputView.keyboardType = type;
assistView.keyboardType = type;
[assistView becomeFirstResponder];
shouldDismiss = NO;
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardDidShowNotification
object:nil
queue:nil
usingBlock:^(NSNotification * _Nonnull note) {
if (!shouldDismiss) {
[inputView becomeFirstResponder];
}
}];
}
- (void)Done
{
[inputView resignFirstResponder];
!inputBlock ?: inputBlock(inputView.text);
[self dismiss];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[self Done];
return NO;
}
- (void)dismiss
{
shouldDismiss = YES;
[inputView resignFirstResponder];
[btnBack removeFromSuperview];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

InputView的frame适应

案例见GitHub的Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 键盘响应布局
@objc func keyboardFrameChanged(notification: NSNotification) {
let dict = NSDictionary(dictionary: notification.userInfo!)
let keyboardValue = dict.objectForKey(UIKeyboardFrameEndUserInfoKey) as! NSValue
let bottomDistance = mainScreenSize().height - keyboardValue.CGRectValue().origin.y
let duration = Double(dict.objectForKey(UIKeyboardAnimationDurationUserInfoKey) as! NSNumber)
UIView.animateWithDuration(duration, animations: {
self.inputViewConstraint!.constant = -bottomDistance
self.view.layoutIfNeeded()
}, completion: {
(value: Bool) in
self.chatTableView.scrollToBottom(animation: true)
})
}

ScrollView下拉动态修改keyboard(InputView)的frame

常见隐藏keyboard的一些方式

  • TouchBeigin
  • DidDrag
  • EndDrag
  • Interactive

iOS7 开始,ScrollView提供

1
2
3
4
5
6
@available(iOS 7.0, *)
public enum UIScrollViewKeyboardDismissMode : Int {
case None
case OnDrag // dismisses the keyboard when a drag begins
case Interactive // the keyboard follows the dragging touch off screen, and may be pulled upward again to cancel the dismiss
}

所以在ScrollView上添加scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissMode.Interactive就可以了。
效果详见iOS7及以上原生设备的短信滑动消失键盘的交互

周精益分享 - Swift入门专题

『图像和滤镜』 - 图像选择器

我们可以使用以下常规的图像获取方式

Alt text

图库与相册

Alt text

系统自带

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let sheet = UIAlertController(title: "图片选择", message: "简单版的三种选择", preferredStyle: .ActionSheet)
// 判断设备是否支持相机(iPod & Simulator)
if (UIImagePickerController.isSourceTypeAvailable(.Camera)) {
sheet.addAction(UIAlertAction.init(title: "Camera", style: .Default, handler: { _ in
self.showPhotoes(.Camera)
}))
}
sheet.addAction(UIAlertAction.init(title: "PhotoLibrary", style: .Default, handler: { _ in
self.showPhotoes(.PhotoLibrary)
}))
sheet.addAction(UIAlertAction.init(title: "SavedPhotosAlbum", style: .Default, handler: { _ in
self.showPhotoes(.SavedPhotosAlbum)
}))
sheet.addAction(UIAlertAction.init(title: "Cancel", style: .Cancel, handler: nil))
presentViewController(sheet, animated: true, completion: nil)
}
1
2
3
4
5
6
7
8
func showPhotoes(source: UIImagePickerControllerSourceType) {
let controller = UIImagePickerController()
controller.delegate = self
controller.sourceType = source
controller.allowsEditing = source == .SavedPhotosAlbum ? true:false
self.presentViewController(controller, animated: true, completion: nil)
}

UIImagePickerController的代理

1
2
3
4
5
6
7
8
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
// 非常坑,这个方法废弃了但代码提示只有它
// func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
// }
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
}

#####info字典介绍

1
2
3
4
5
6
7
8
9
10
UIImagePickerControllerMediaType: String
UIImagePickerControllerOriginalImage: UIImage
UIImagePickerControllerEditedImage: UIImage
UIImagePickerControllerCropRect: NSValue -> CGRect
// MediaURL只为视频提供
UIImagePickerControllerMediaURL: NSURL
// LivePhoto是一张图片,保留那个moment的前后动作和声音
UIImagePickerControllerLivePhoto: String
// 摄像摄影时返回media的信息字典
UIImagePickerControllerMediaMetadata: NSDictionary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// SavedPhotosAlbum 的 info 示例
▿ 5 elements
▿ [0] : 2 elements
- .0 : "UIImagePickerControllerCropRect"
▿ [1] : 2 elements
- .0 : "UIImagePickerControllerOriginalImage"
▿ [2] : 2 elements
- .0 : "UIImagePickerControllerReferenceURL"
- .1 : assets-library://asset/asset.JPG?id=99D53A1F-FEEF-40E1-8BB3-7DD55A43C8B7&ext=JPG
▿ [3] : 2 elements
- .0 : "UIImagePickerControllerMediaType"
- .1 : public.image
▿ [4] : 2 elements
- .0 : "UIImagePickerControllerEditedImage"

自定义

Alt text

遍历相册的所有图片

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
// AssetsLibrary.framework
// ALAssetsLibrary 的使用,但是它慢慢的被放弃了
func loadLocalPhotoes(){
var countOne = 0
//ALAssetsGroupSavedPhotos表示只读取相机胶卷(ALAssetsGroupAll则读取全部相簿)
assetsLibrary.enumerateGroupsWithTypes(ALAssetsGroupSavedPhotos, usingBlock: {
(group: ALAssetsGroup!, stop) in
print("is goin")
if group != nil {
let assetBlock : ALAssetsGroupEnumerationResultsBlock = {
(result: ALAsset!, index: Int, stop) in
if result != nil {
self.assets.append(result)
countOne++
}
}
group.enumerateAssetsUsingBlock(assetBlock)
print("assets:\(countOne)")
self.startChangeLocalImages(0)
}
}, failureBlock: { (fail) in
print(fail)
})
}
// 展现本地图片
func startChangeLocalImages(var index: Int){
if index==assets.count {
index = 0
}
let myAsset = assets[index]
let image = UIImage(CGImage:myAsset.thumbnail().takeUnretainedValue())
self.backImageView.image = image
let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(popTime, dispatch_get_main_queue()) {
self.startChangeLocalImages(index+1)
}
}

iOS9 开始使用新库
PHPhotoLibrary

周精益分享 - 程序员装逼指南

『图像和滤镜』

图像

常见的图像格式

  • png(Portable Network Graphic)便携式网络图形格式
  • tiff、tif(Tagged Image File Format)标记图像文件格式
  • jpg、jpeg(Joint Photographic Experts Group)联合摄影专家组
  • gif(Graphic Interchange Format)图形交换格式

图像实例化

  • imageNamed:

    使用频率高,内存缓存优化

  • imageWithContentsOfFile:

    单次使用,暂时不清楚如何支持ImageAssert下的图片路径

  • imageWithData:

    本地或网络的文件数据加载

  • imageWithCGImage:

    绘制生成图,代码如下

1
2
3
4
5
// 旧图局部裁剪
CGImageRef myImageRef = [oldImage CGImageRef];
CGRect subRect = CGRectMake(20, 20, 100, 100);
CGImageRef cgCrop = CGImageCreateWithImageInRect(myImageRef, subRect);
UIImage *imageCrop = [UIImage imageWithCGImage:cgCrop];

显示图像

屏幕的缩放因子,导致我们需要@2x及@3x图,对视图截图操作也同样需要针对Retina及plus优化

1
2
3
4
5
6
7
8
9
10
11
- (UIImage*)screenShotBy:(UIImageView*)imagView
{
// UIGraphicsBeginImageContext(imagView.bounds.size);
// 2表示Retina,3表示plus分辨率,1正常,0则是当前设备的缩放因子
// YES 表示不包含图像的alpha通道
UIGraphicsBeginImageContextWithOptions((imagView.bounds.size), YES, 2);
[imagView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage*image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

contentModel

图像内容展现方式

  • UIViewContentModelScaleToFill(默认)
  • UIViewContentModelScaleAspectFill(下拉放大)
  • UIViewContentModelScaleAspectFit
  • UIViewContentModelCenter (一些停靠模式)
  • UIViewContentModelTop/Bottom
  • UIViewContentModelLeft/Right
拉伸属性

代码

1
2
3
4
5
// 方法一
image=[oldIconImage stretchableImageWithLeftCapWidth:10 topCapHeight:12];
// 方法二
image = [oldIconImage resizableImageWithCapInsets:UIEdgeInsetsMake(17, 17, 17, 25)];

ImageAssert

Alt text
使用slicing,小变大无视差
Alt text


图像选择器

系统自带

自定义


滤镜

人脸识别

周精益分享 - 英语

英语,程序员

网站

  • iTalki

    语言交换及专业老师订课辅导的网站

  • YouTube一个订阅号

    比较有吸引力的老师,他的同事频道也很不错

  • rayWenderlich

    主要是iOS方面的,安卓覆盖一些。涵盖文字、视频、播客三方面

APP

  • 飞鱼口语

    国内开发的一个及时练习口语的APP,主要是方便

  • 每日英语听力(欧陆词典推出的)

    radio 模块很不错,资源不算多但比较有质量。

  • Podcast

    • 圆桌会议(RoundTable)
    • rayWenderlich (程序员职业发展及新技术点探讨,英式口语)

工具

  • Skype 和志同道合的人交流,练习口语

EF Education

  • 网站地址

    想小试牛刀的记得想我拿一下账号密码

周精益分享-谈谈未来、梦想、职业规划

兴趣主导,快乐很重要 - 杨志平

  • 兴趣

    • 在失去当前工作兴趣之前,不会转行
    • 职业规划随兴趣走
  • 肉身翻墙

    • 出去走一遭,趁年轻(工作或打工旅行)
    • 去牛逼公司镀金
  • 终极目标

    • 年轻时,向往自由工作者的工作方式,能够养活自己的游行工作方式
    • 适当年纪收心回归平常心(保持感兴趣的工作即可)

设计规范交流

#Label的一些使用规范 - 杨志平

##动态适应字体大小

iOS系统自带的常见字体样式(字号及粗细等等)

  • UIFontTextStyleHeadline
  • UIFontTextStyleSubheadline
  • UIFontTextStyleBody
  • UIFontTextStyleFootnote
  • UIFontTextStyleCaption1
  • UIFontTextStyleCaption2

###效果如下:

Font

代码

1
2
3
4
5
6
7
8
// 字体初始化
UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
// 通知需要刷新的文本字体
[[NSNotificationCenter defaultCenter]addObserver:self
selector:@selector(userTextSizeDidChange)
name:UIContentSizeCategoryDidChangeNotification
object:nil];

国际化支持(适配注意点)

####工具:genstrings

####参考:博客链接

1
2
3
先看看它的宏定义:
#define NSLocalizedString(key, comment)
[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]

本地化对象:标签和按钮上的文本,或者在运行时获取的字符串和数据动态生成的字符串。

难点

不同语言的语法问题

语序,人称不一致:
「Paul invited you」和「You invited Paul」 -> 「%@ invited %@」,看似可以
以德语为例,「Paul invited you」译为「Paul hat dich eingeladen」,
而「You invited Paul」则译为「Du hast Paul eingeladen」。
正确处理其他语言的特殊语法方案:「%@ invited you」和「You invited %@」。

一词多意

1
2
3
4
5
6
7
8
run ->
vt. & vi. 跑
移动
(使)流动
n. 跑, 奔跑
旅行, 旅程
行驶路线
时期; 一段时间

正确做法:

1
2
NSLocalizedString(@"activity-profile.title.the-run", nil)
NSLocalizedString(@"home.button.start-run", nil)

或者:(完全没有试过)

NSLocalizedString 有一些变体能够提供更多字符串本地化的操作方式。NSLocalizedStringFromTable 接收 key、table 和 comment 这三个参数,其中 table 参数表示该字符串对应的一个表格,genstrings 会为表中的每一个条目生成一个以条目名称(假设为 table-item)命名的独立字符串文件 table-item.strings。
这样你就可以把字符串文件分割成几个小一些的文件。在一个庞大的项目或者团队中工作时,这一点显得尤为重要。同时这也让合并原有的和重新生成的字符串文件变得容易一些。

1
NSLocalizedStringFromTable(@"home.button.start-run", @"ActivityTracker", @"some comment..")

###调试本地化字符串(拓展)
应用支持的语言版本越多,确保所有元素都正确显示就越难。但是这里有一些默认的用户选项和工具可以减轻你的负担。

NSDoubleLocalizedStrings
AppleTextDirection
NSForceRightToLeftWritingDirection
选项保证你的布局不会因为长字符串或者从右往左读的语言而混乱。

NSShowNonLocalizedStrings
NSShowNonLocalizableStrings
则可以帮助你找到没有翻译的字符串和根本没有制定字符串本地化宏的字符串。

浅拷贝与深拷贝

浅拷贝与深拷贝

程序中经常会遇到集合类的传值

坑: 数组操作时对于数组中的对象拷贝

###目的:
观察array1、mArrayCopy、mArrayCopy2 三者区别

1
2
3
NSArray *array1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *mArrayCopy = [[NSArray alloc] initWithArray:array1 copyItems:YES];
NSArray* mArrayCopy2 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array1]];

首先了解copy与retain的区别

OC对象引用计数器:屋里开灯规则

copy是创建一个新对象,retain是创建一个指针,引用对象计数加1。Copy属性表示两个对象内容相同,新的对象retain为1 ,与旧有对象的引用计数无关,旧有对象没有变化。

retain属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1也就是说,retain 是指针拷贝,copy 是内容拷贝。

主角:copy、mutableCopy

注意:可变和不可变对象使用copy、mutableCopy的区别

遵守NSCopying 协议的类可以发送copy消息,
遵守NSMutableCopying 协议的类才可以发送mutableCopy消息。

默认的ios类并没有遵守这两个协议
自定义copy 必须遵守NSCopying,并且实现 copyWithZone: 方法
自定义mutableCopy 那么就必须遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法

实例

系统的非容器类对象

这里指的是NSString,NSNumber等等一类的对象。

示例1:
不可变string

1
2
3
4
5
6
7
8
9
10
NSString *string = @"origionStr";
NSString *string2 = string;
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
[stringMCopy appendString:@"!!!!"];
// log
NSLog(@"string = %p %p %@ ",string,&string,string);
NSLog(@"string2 = %p %p %@ ",string2,&string2,string2);
NSLog(@"stringCopy = %p %p %@ ",stringCopy,&stringCopy,stringCopy);
NSLog(@"stringMCopy = %p %p %@ ",stringMCopy,&stringMCopy,stringMCopy);

结果:

1
2
3
4
string = 0x1064e7088 0x7fff597190a0 origionStr
string2 = 0x1064e7088 0x7fff59719098 origionStr
stringCopy = 0x1064e7088 0x7fff59719090 origionStr
stringMCopy = 0x7fafebdbb4b0 0x7fff59719088 origionStr!!!!

示例2:
可变string

1
2
3
4
5
6
7
8
9
10
NSMutableString *string = [NSMutableString stringWithString: @"origion"];
NSString *stringCopy = [string copy];
NSMutableString *mStringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
[string appendString:@" origion!"];
[stringMCopy appendString:@"!!"];
NSLog(@"string = %p %@",string,string);
NSLog(@"stringCopy = %p %@",stringCopy,stringCopy);
NSLog(@"stringMCopy = %p %@",mStringCopy,mStringCopy);
NSLog(@"stringMCopy = %p %@",stringMCopy,stringMCopy);

结果:

1
2
3
4
string = 0x7fafebdec820 origion origion!
stringCopy = 0x7fafebdcb8c0 origion
stringMCopy = 0x7fafebda4320 origion
stringMCopy = 0x7fafebd32890 origion!!

对于系统的非容器类对象,我们可以认为,如果对一不可变对象复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝)。如果是对可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。

系统的容器类对象

指NSArray,NSSet,NSDictionary等。对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。

示例1:
不可变数组

1
2
3
4
5
6
7
8
9
10
//copy返回不可变对象,mutablecopy返回可变对象
NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
NSArray *arrayCopy1 = [array1 copy];
NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
[mArrayCopy1 addObject:@"de"];
[mArrayCopy1 removeObjectAtIndex:0];
// log
NSLog(@"array1 = %p %@ %p %@",array1,array1,array1[1],array1[1]);
NSLog(@"arrayCopy1 = %p %@ %p %@",arrayCopy1,arrayCopy1,arrayCopy1[1],arrayCopy1[1]);
NSLog(@"mArrayCopy1 = %p %@ %p %@",mArrayCopy1,mArrayCopy1,mArrayCopy1[0],mArrayCopy1[0]);

结果:

1
2
3
array1 = 0x7fafebdeaed0 (a,b,c) 0x1064e7228 b
arrayCopy1 = 0x7fafebdeaed0 (a,b,c) 0x1064e7228 b
mArrayCopy1 = 0x7fafebdfc010 (b,c,de) 0x1064e7228 b

示例:
可变数组

1
2
3
4
NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
NSMutableArray *mArrayCopy = [[NSMutableArray alloc] initWithArray:array1 copyItems:YES];
NSLog(@"array1 = %p %@ %p %@ %p %@",array1, array1, array1[0], array1[0], array1[1], array1[1]);
NSLog(@"arrayCopy1 = %p %@ %p %@ %p %@",mArrayCopy,mArrayCopy, mArrayCopy[0], mArrayCopy[0], mArrayCopy[1], mArrayCopy[1]);

结果:

1
2
array1 = 0x7fafebcb5ae0 (a,b,c) 0x10afe1208 a 0x10afe1228 b
arrayCopy1 = 0x7fafebcb5b10 (a,b,c) 0x10afe1208 a 0x10afe1228 b

示例2:

1
2
3
NSArray *array1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSMutableArray *mArrayCopy = [[NSMutableArray alloc] initWithArray:array1 copyItems:YES];
NSArray* mArrayCopy2 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array1]];

mArrayCopy2是完全意义上的深拷贝,而mArrayCopy则不是

对于mArrayCopy内的不可变元素其还是指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。

那些年我们用过的第三方库

SnapKit(Masonry) 的使用 - 杨志平

这两个库的用法都是差不多的,只是由两个不同的人来主导开源

SnapKit是Swift版

Masonry是OC版

自动布局及交互式编程是iOS开发的趋势,同时Swift也会在不久将来替换OC语言。所以现在的iOS开发者可以开始学习Swift2.0 以及应用 Autolayout 来编程

代码对比(概况了解)

开始前OC原生布局代码
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
UIView *superview = self;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
使用Masonry

精简

1
2
3
4
5
6
7
8
9
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(- padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];

更加精简

1
2
3
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
同理使用SnapKit

精简

1
2
3
4
5
6
7
8
9
10
let box = UIView()
superview.addSubview(box)
box.snp_makeConstraints { (make) -> Void in
make.top.equalTo(superview).offset(20)
make.left.equalTo(superview).offset(20)
make.bottom.equalTo(superview).offset(-20)
make.right.equalTo(superview).offset(-20)
}

更加精简

1
2
3
box.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(superview).inset(UIEdgeInsetsMake(20, 20, 20, 20))
}

如何使用 && 原理

常见的约束类型对比

ViewAttribute NSLayoutAttribute
view.snp_left NSLayoutAttribute.Left
view.snp_right NSLayoutAttribute.Right
view.snp_top NSLayoutAttribute.Top
view.snp_bottom NSLayoutAttribute.Bottom
view.snp_leading NSLayoutAttribute.Leading
view.snp_trailing NSLayoutAttribute.Trailing
view.snp_width NSLayoutAttribute.Width
view.snp_height NSLayoutAttribute.Height
view.snp_centerX NSLayoutAttribute.CenterX
view.snp_centerY NSLayoutAttribute.CenterY
view.snp_baseline NSLayoutAttribute.Baseline

常见的用法

1
2
3
4
5
6
7
8
9
10
make.top.equalTo(42)
make.lessThanOrEqualTo.equalTo(SuperView)
make.top.equalTo(SuperView)
make.size.equalTo(CGSizeMake(50, 100))
make.edges.equalTo(UIEdgeInsetsMake(10, 0, 10, 0))
make.left.equalTo(view).offset(UIEdgeInsetsMake(10, 0, 10, 0))
make.height.equalTo(OtherView).offset(10)
make.trailing.equalTo(OtherView.snp_trailing).offset(10)
make.bottom.equalTo(-20).priority(250)

对比交互式编程的约束布局

image

[上海] Android 小伙伴一起来 51offer 玩吧

国内留学行业整体上还是一个传统中介行业,51offer 致力于让人们更方便的通过互联网留学。
留学学生和留学学校虽然五花八门,但既有的留学流程大体一致,相应的盈利模式也比较清晰。但与之对应的如何切实通过产品降低留学门槛,在更多的学生与学校之间建立便利的联系,还有很多好玩的事情可以做。
在互联网留学这一块,51offer 目前可以算国内第一,同时身为一个互联网从业者客观来说,这个行业各公司产品、技术各方面还非常烂(包括我们 51offer)。

要做的事情很多,要决定不做的事情更多,要把不好变好的事情也有不少。
如果你愿意挑战既有现状,喜欢用聪明的方式把事情做好,最好再对这个教育细分领域感兴趣,让我们一起来做些有意义的事。

本人曾铭,ID: mithvv,目前负责公司移动团队。丁香园和百姓网的工作经历让我相信小而扁平的团队组织更加高效。如果前几天耗子和玉伯你站在耗子的一方,嗯,你会喜欢我们目前的合作方式。(ಥ_ಥ,想说玉伯也是我非常尊敬的前辈,他的观点适合支付宝……)

公司状况

  • 刚过 B 轮,两年内完成 5000 万美元融资,自身盈利也一直很好,生死存亡的问题你暂时不用担心
  • 我们基本不加班甚至很难让你加班,因为我们所有的加班(以小时计)都可以调休。我个人是每周三调休带孩子玩,周日上一天班 ;)
  • 公司在上海火车站边上,地铁出站过个路口就到(下雨天忘带伞问题都不大)
  • 技术团队人数目前 50+,谨慎扩张中
  • 技术语言各端不同。后端主要是 Java(大部分),搜索 C++,爬虫 Python,前端和移动各公司应该区别不大,移动这边 Swift 正在一起学习,计划 2.0 正式版出来后用到产品中

招聘要求(不罗列每个职位的技术细节了,意义不大)

  • 招 Android、Java、前端工程师,是工程师或者攻城狮,不招码农
  • 至少作为主程参与过一个产品的完整开发过程
  • 会或者可以快速学会 git,这样我们能快速在 Github 上玩起来
  • 有经验或者了解持续集成、自动化测试及其意义,这样我们在如何快速开发上比较容易达成一致
  • 能把一个技术点用自己的语言阐述清楚,我们的周技术分享期待你的 ShowTime
  • 加分项
    • 践行开源
    • 能跟产品从产品角度阐述清楚技术逻辑
    • 有稳定维护的 Blog 或 Github 帐号
    • 能自由访问国际互联网(如果不会我们教你,这点其实也必备)
    • 对技术有热情,(比如上面一些条件你并不完全满足,但你自认思路清晰,学习能力强,就是要来面一面。我们非常非常欢迎对技术有热情的你!)

薪资待遇

  • 整体 10k - 20k 之间,具体面议,牛人没法封顶

你都看到这了,简历快发给我吧: ming.z[at]51offer.com (24 小时内必有反馈哦)
有意聊天者时间你定,地点我定(工作忙,上海火车站附近即可),咖啡我请 ;)

原始链接:51offer-blog