Gowhich

Durban's Blog

关于NSNotificationCenter,跟我前面那篇文章是一样的,一直想使用这个,基于种种原因,一直没去涉及,最近因为新浪微博的登录,so,用到了,就简单记录一下吧。

第一步:简单的写个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)addObservers
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceOrientationDidChange:)
name:@"UIDeviceOrientationDidChangeNotification"
object:nil];
}

- (void)removeObservers{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"UIDeviceOrientationDidChangeNotification"
object:nil];
}

是这样的,添加一个通知,然后去调用方法

这个方法是做什么的呢

1
2
3
4
- (void)deviceOrientationDidChange:(id)object
{

}

你想做什么都可以,因为只是一个例子,你可以抽象的超长发挥的。我没用上,知道这么用就好了先。

关于UIActivityIndicatorView的简单的使用方法,最近突然就学会了,这个要归咎于,要多看别人的东西,多用用别人的东西,就能学到很多。

第一步:属性声明

1
@property (retain, nonatomic) UIActivityIndicatorView *indicatorView;

第二步:实现过程

1
2
3
4
5
6
7
8
9
indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
indicatorView.autoresizingMask =
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
| UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[self.view addSubview:indicatorView];

[indicatorView sizeToFit];
[indicatorView startAnimating];
indicatorView.center = weiboLoginWeb.center;

主配置文件,main.php的组件(components)中开启路由模式

1
2
3
4
5
6
7
8
9
10
'urlManager' => [
'urlFormat' => 'path',
'showScriptName' => false,
'urlSuffix' => '.html',
'rules' => [
'<controller:\w+>/<id:\d+>' => '<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',
'<controller:\w+>/<action:\w+>' => '<controller>/<action>',
],
],

去掉路径中index.php名称

在网站根目录下新键一个.htaccess文件

里面内容:

1
2
3
4
5
6
7
8
9
10
11
* show index.php?
* Options +FollowSymLinks
IndexIgnore *\/*
RewriteEngine on

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php
RewriteRule . index.php

关于这个分享,也是考虑到很多开发者,在使用的过程中遇到的了很多的问题,对于这种情况我是这么理解的,微博也许是做事不认真吧,也许是我们真的不懂吧,但是我觉得解决问题,能达到自己想要的效果就好了。微博官方的weiboSDK我用的是最新的版本,截止到目前这个时刻,我是用的是最新的,开始的时候,我使用的时候,使用了最初的UIWebView的登录方式,主要是因为,使用客户端的方式,至今不知道什么原理,自己还是慢慢研究吧,呵呵。后来觉得样式不符合自己,于是今天就自己使用UIViewController,在里面使用UIWebView的方式,简单的实现了,至少现在的看起来不会感觉手机的登录看起来怪怪 的。我把代码留在下面:

github代码地址:

https://github.com/zhangda89/IOS-weiboSDK

https://github.com/zhangda89/IOS-StorageData

ThirdPartyLoginController.m
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
262
263
//
// ThirdPartyLoginController.m
// xunYi7
//
// Created by david on 13-7-10.
// Copyright (c) 2013年 david. All rights reserved.
//

#import "ThirdPartyLoginController.h"
#import "UserCenter.h"


@interface ThirdPartyLoginController ()

@end

@implementation ThirdPartyLoginController

@synthesize weiboLoginWeb;
@synthesize indicatorView;
@synthesize authParams;
@synthesize appRedirectURI;
@synthesize sinaWeiboRequest;

@synthesize userID;
@synthesize accessToken;
@synthesize expirationDate;
@synthesize refreshToken;

@synthesize previousOrientation;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.title = @"微博登录";
}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];
weiboLoginWeb.delegate = self;

indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:
UIActivityIndicatorViewStyleGray];
indicatorView.autoresizingMask =
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
| UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[self.view addSubview:indicatorView];
}

-(void) viewWillAppear:(BOOL)animated{
self.authParams = [NSMutableDictionary dictionaryWithObjectsAndKeys:
kAppKey, @"client_id",
@"code", @"response_type",
kAppRedirectURI, @"redirect_uri",
@"mobile", @"display", nil];
self.appRedirectURI = kAppRedirectURI;
self.sinaWeiboRequest = [[SinaWeiboRequest alloc] init];
sinaWeiboRequest.delegate = self;

[self show];

}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

#pragma mark - SinaWeiboRequestDelegate Methods
- (void)request:(SinaWeiboRequest *)request didReceiveResponse:(NSURLResponse *)response{
NSLog(@"response = %@",response);

}
- (void)request:(SinaWeiboRequest *)request didReceiveRawData:(NSData *)data{
NSLog(@"data = %@",data);

}
- (void)request:(SinaWeiboRequest *)request didFailWithError:(NSError *)error{
NSLog(@"error = %@",error);
}

- (void)request:(SinaWeiboRequest *)request didFinishLoadingWithResult:(id)result{
NSLog(@"result = %@",result);
if([UserCenter saveWeiboAuth:result]){
[self.navigationController popViewControllerAnimated:YES];
}

}

#pragma mark - Activity Indicator
- (void)showIndicator
{
[indicatorView sizeToFit];
[indicatorView startAnimating];
indicatorView.center = weiboLoginWeb.center;
}

- (void)hideIndicator
{
[indicatorView stopAnimating];
}

#pragma mark - Show / Hide
- (void)load{
NSString *authPagePath = [SinaWeiboRequest serializeURL:kSinaWeiboWebAuthURL
params:authParams httpMethod:@"GET"];
[weiboLoginWeb loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:authPagePath]]];
}

- (void)show
{
[self load];
[self showIndicator];
[self addObservers];
}

- (void)hide
{
[self removeObservers];

[weiboLoginWeb stopLoading];

}

- (void)cancel
{
[self hide];
}

#pragma mark - UIDeviceOrientationDidChangeNotification Methods
- (void)deviceOrientationDidChange:(id)object
{

}


#pragma mark Obeservers
- (void)addObservers
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceOrientationDidChange:)
name:@"UIDeviceOrientationDidChangeNotification" object:nil];
}

- (void)removeObservers{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"UIDeviceOrientationDidChangeNotification" object:nil];
}


#pragma mark - UIWebView Delegate

- (void)webViewDidFinishLoad:(UIWebView *)aWebView{
[self hideIndicator];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
[self hideIndicator];
}


- (BOOL)webView:(UIWebView *)aWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSString *url = request.URL.absoluteString;

NSString *siteRedirectURI = [NSString stringWithFormat:@"%@%@", kSinaWeiboSDKOAuth2APIDomain, appRedirectURI];

if ([url hasPrefix:appRedirectURI] || [url hasPrefix:siteRedirectURI]){
NSString *error_code = [SinaWeiboRequest getParamValueFromUrl:url paramName:@"error_code"];

if (error_code){
NSString *error = [SinaWeiboRequest getParamValueFromUrl:url paramName:@"error"];
NSString *error_uri = [SinaWeiboRequest getParamValueFromUrl:url paramName:@"error_uri"];
NSString *error_description = [SinaWeiboRequest getParamValueFromUrl:url paramName:@"error_description"];

NSDictionary *errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
error, @"error",
error_uri, @"error_uri",
error_code, @"error_code",
error_description, @"error_description", nil];
NSLog(@"errorInfo = %@",errorInfo);

[self hide];

}else{
NSString *code = [SinaWeiboRequest getParamValueFromUrl:url paramName:@"code"];
if (code){
[self hide];

NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
kAppKey, @"client_id",
kAppSecret, @"client_secret",
@"authorization_code", @"grant_type",
self.appRedirectURI, @"redirect_uri",
code, @"code", nil];


[sinaWeiboRequest disconnect];
sinaWeiboRequest = nil;

self.sinaWeiboRequest = [SinaWeiboRequest requestWithURL:kSinaWeiboWebAccessTokenURL
httpMethod:@"POST"
params:params
delegate:self];


[sinaWeiboRequest connect];
}
}

return NO;
}

return YES;
}

/**
* @description 清空认证信息
*/
- (void)removeAuthData{
// NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
// NSArray* sinaweiboCookies = [cookies cookiesForURL:
// [NSURL URLWithString:@"https://open.weibo.cn"]];
//
// for (NSHTTPCookie* cookie in sinaweiboCookies)
// {
// [cookies deleteCookie:cookie];
// }
}
/**
* @description 判断是否登录
* @return YES为已登录;NO为未登录
*/
- (BOOL)isLoggedIn
{
return userID && accessToken && expirationDate;
}

/**
* @description 判断登录是否过期
* @return YES为已过期;NO为未为期
*/
- (BOOL)isAuthorizeExpired
{
NSDate *now = [NSDate date];
return ([now compare:expirationDate] == NSOrderedDescending);
}


/**
* @description 判断登录是否有效,当已登录并且登录未过期时为有效状态
* @return YES为有效;NO为无效
*/
- (BOOL)isAuthValid
{
return ([self isLoggedIn] && ![self isAuthorizeExpired]);
}
@end
ThirdPartyLoginController.h
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
//
// ThirdPartyLoginController.h
// xunYi7
//
// Created by david on 13-7-10.
// Copyright (c) 2013年 david. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "SinaWeiboRequest.h"
#import "SinaWeibo.h"
#import "SinaWeiboConstants.h"
#import <QuartzCore/QuartzCore.h>

#define kAppKey @"xxxxxxxxx"
#define kAppSecret @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#define kAppRedirectURI @"https://api.weibo.com/oauth2/default.html"

#ifndef kAppKey
#error
#endif

#ifndef kAppSecret
#error
#endif

#ifndef kAppRedirectURI
#error
#endif

@interface ThirdPartyLoginController : UIViewController<UIWebViewDelegate, SinaWeiboDelegate, SinaWeiboRequestDelegate>


@property (strong, nonatomic) IBOutlet UIWebView *weiboLoginWeb;
@property (retain, nonatomic) UIActivityIndicatorView *indicatorView;
@property (retain, nonatomic) NSDictionary *authParams;
@property (retain, nonatomic) NSString *appRedirectURI;
@property (retain, nonatomic) SinaWeiboRequest *sinaWeiboRequest;

@property (nonatomic, retain) NSString *userID;
@property (nonatomic, retain) NSString *accessToken;
@property (nonatomic, retain) NSDate *expirationDate;
@property (nonatomic, retain) NSString *refreshToken;

@property (nonatomic) UIInterfaceOrientation previousOrientation;

@end

还有一个简单的管理微博用户登录的信息的方法,很简单的

UserCenter.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//
// UserCenter.h
// xunYi7
//
// Created by david on 13-7-10.
// Copyright (c) 2013年 david. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface UserCenter : NSObject
+(BOOL)saveWeiboAuth:(NSDictionary *)params;
+(NSDate *)fetchWeiboExpireDate;
+(NSString *)fetchWeiboAccessToken;
+(NSString *)fetchWeiboUid;
+(BOOL)isOutDate;
+(void)removeWeiboAuthCookieData;
+(void)removeWeiboAuthData;
@end
UserCenter.m
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
//
// UserCenter.m
// xunYi7
//
// Created by david on 13-7-10.
// Copyright (c) 2013年 david. All rights reserved.
//

#import "UserCenter.h"

@implementation UserCenter

/**
* @description 存储微博登录的信息
*/
+(BOOL)saveWeiboAuth:(NSDictionary *)params{
NSDictionary *authData = [NSDictionary dictionaryWithObjectsAndKeys:
[params objectForKey:@"access_token"], @"AccessTokenKey",
[params objectForKey:@"expires_in"], @"ExpirationDateKey",
[params objectForKey:@"uid"], @"UserIDKey",
[params objectForKey:@"access_token"], @"refresh_token", nil];
[[NSUserDefaults standardUserDefaults] setObject:authData forKey:@"SinaWeiboAuthData"];
[[NSUserDefaults standardUserDefaults] synchronize];


//微博的时间戳的转换
NSUserDefaults *accountDefaults = [NSUserDefaults standardUserDefaults];
NSLog(@"ExpirationDateKey = %@",[[accountDefaults objectForKey:@"SinaWeiboAuthData"] objectForKey:@"ExpirationDateKey"]);
return YES;
}

/**
* @description 获取微博登录的过期时间
*/
+(NSDate *)fetchWeiboExpireDate{
NSUserDefaults *accountDefaults = [NSUserDefaults standardUserDefaults];
if([accountDefaults objectForKey:@"SinaWeiboAuthData"] != nil){
return [[accountDefaults objectForKey:@"SinaWeiboAuthData"] valueForKey:@"ExpirationDateKey"];
}
return nil;
}

/**
* @description 获取微博登录的AccessToken
*/
+(NSString *)fetchWeiboAccessToken{
NSUserDefaults *accountDefaults = [NSUserDefaults standardUserDefaults];
if([accountDefaults objectForKey:@"SinaWeiboAuthData"] != nil){
return [[accountDefaults objectForKey:@"SinaWeiboAuthData"] valueForKey:@"AccessTokenKey"];
}
return nil;
}

/**
* @description 获取登录的微博用户的id
*/
+(NSString *)fetchWeiboUid{
NSUserDefaults *accountDefaults = [NSUserDefaults standardUserDefaults];
if([accountDefaults objectForKey:@"SinaWeiboAuthData"] != nil){
return [[accountDefaults objectForKey:@"SinaWeiboAuthData"] valueForKey:@"UserIDKey"];
}
return nil;
}

/**
* @description 判断是否过期
*/
+(BOOL)isOutDate{
//现在时刻的时间戳
NSDate *newDate =[NSDate date];
NSString *timeStamp =[NSString stringWithFormat:@"%lu", (long)[newDate timeIntervalSince1970]];

//微博的时间戳的转换
NSUserDefaults *accountDefaults = [NSUserDefaults standardUserDefaults];


if([accountDefaults objectForKey:@"SinaWeiboAuthData"] == nil){
return YES;
}

if([[accountDefaults objectForKey:@"SinaWeiboAuthData"] objectForKey:@"ExpirationDateKey"] == nil){
return YES;
}


NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss ZZZ"];
NSDate *weiBoDate = [dateFormatter dateFromString:[NSString stringWithFormat:@"%@",[[accountDefaults objectForKey:@"SinaWeiboAuthData"] objectForKey:@"ExpirationDateKey"]]];

NSLog(@"weiBoDate ExpirationDateKey = %@",[[accountDefaults objectForKey:@"SinaWeiboAuthData"] objectForKey:@"ExpirationDateKey"]);
// NSString *weiBoDateStamp = [NSString stringWithFormat:@"%lu", (long) [weiBoDate timeIntervalSince1970]];
NSString *weiBoDateStamp = [NSString stringWithFormat:@"%@",[[accountDefaults objectForKey:@"SinaWeiboAuthData"] objectForKey:@"ExpirationDateKey"]];



NSLog(@"weiBoDateStamp=%@, timeStamp = %@",[[accountDefaults objectForKey:@"SinaWeiboAuthData"] objectForKey:@"ExpirationDateKey"],timeStamp);
if([timeStamp compare:weiBoDateStamp] < 0){//未过期
return NO;
}else{//已经过期
[self removeWeiboAuthData];
return YES;
}
return NO;
}

/**
* @description 移出微博的登录的cookie信息
*/
+(void)removeWeiboAuthCookieData{
NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* sinaweiboCookies = [cookies cookiesForURL:
[NSURL URLWithString:@"https://open.weibo.cn"]];

for (NSHTTPCookie* cookie in sinaweiboCookies)
{
[cookies deleteCookie:cookie];
}
}


/**
* @description 移出微博的登录的信息
*/
+(void)removeWeiboAuthData{
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"SinaWeiboAuthData"];
}
@end

现在时刻的时间戳

1
2
NSDate *newDate =[NSDate date];
NSString *timeStamp =[NSString stringWithFormat:@"%lu", (long)[newDate timeIntervalSince1970]];

时间转换为时间戳的转换

1
2
3
4
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss ZZZ"];//时间的格式要正确
NSDate *weiBoDate = [dateFormatter dateFromString:@"2018-07-08 08:51:20 +0000"];//与上面的时间格式对应
NSString *weiBoDateStamp = [NSString stringWithFormat:@"%lu", (long) [weiBoDate timeIntervalSince1970]];//获取时间戳

时间戳转换为时间

1
2
3
4
5
6
7
8
9
10
11
NSDate *timeStamp =[NSDate dateWithTimeIntervalSince1970:1296035591];//时间戳的起始点设置
NSLog(@"1296035591 = %@",timeStamp);
NSString *timeStampStr = [dateFormatter stringFromDate:timeStamp];

NSLog(@"confromTimespStr= %@",timeStampStr);

if([timeStamp compare:@"157679999"]){//比较时间戳是否过期
NSLog(@"时间还没有过期");
}else{
NSLog(@"时间已经过期");
}

How to fix “containing working copy admin area is missing” in SVN?

svn: Directory ‘/xxx/xxx/ios/project/xx/xx/JSONKit’ is missing

针对于这个问题:

第一步

1
svn cleanup

第二步

1
svn --force delete <directory-that-doesn't-exist-but-should>

ok解决了

在阅读和书写关于iPhone编程的代码的时候,发现有很多这样的情况:
看到很多源代码里面,使用前面带下划线变量,然后在@synthesize 语句中在用一个不带下划线的变量名。

这样做,到底有什么作用?

因为我常常是以这种方式来做的:
*.h中申明变量

1
2
3
4
5
6
7
8
9
#import <UIKit/UIKit.h>

@interface NewPlayerController : UIViewController{
NSString *test;
}

@property(nonatomic,retain) NSString *test;

@end

*.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import "NewPlayerController.h"

@implementation NewPlayerController

@synthesize test;


- (void)viewDidLoad{

[super viewDidLoad];

test=[[NSString alloc] initWithFormat:@"test"];
}
@end

但是,发现很多别人写的代码是这样子的:
*.h中申明变量

1
2
3
4
5
6
7
8
9
#import <UIKit/UIKit.h>

@interface NewPlayerController : UIViewController{
NSString* _test;
}

@property(nonatomic,retain) NSString *test;

@end

*.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import "NewPlayerController.h"

@implementation NewPlayerController

@synthesize test=_test;

- (void)viewDidLoad{

[super viewDidLoad];

_test=[[NSString alloc] initWithFormat:@"test"];

// 或者这样
self.test=[[NSString alloc] initWithFormat:@"test"];
}

@end

这两种方式到底有什么区别?用那种好?

带着这个疑问,我去网上搜索”代码风格”,“编码规范”,“下划线公约”。解释太宽泛了,根本没有一个定论。

于是只有理论+实践自己在走一回吧。

开始:
第一种方式,就是平常我们说的不带下划线的那种方式:

1
2
3
self.test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的应用计数:%d",[self.test retainCount]);
NSLog(@"test的应用计数:%d",[test retainCount]);

但是,我惊奇的发现,输出竟然这样:

1
2
2012-11-17 15:52:29.604 ArtTV[901:14303] self.test的应用计数:2
2012-11-17 15:52:33.264 ArtTV[901:14303] test的应用计数:2

self.test和test的地址是相同的,说明是同一个对象的应用,但为什么,应用计数会是2呢?

于是,代码变成这样写(仅仅是,self.test换成test):

1
2
3
test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的应用计数:%d",[self.test retainCount]);
NSLog(@"test的应用计数:%d",[test retainCount]);

输出:

1
2
2012-11-17 15:59:58.274 ArtTV[954:14303] self.test的应用计数:1
2012-11-17 15:59:59.718 ArtTV[954:14303] test的应用计数:1

这才是我们所期望的。应用计数应该为1才对,为什么会变成2呢?
恍然大悟了!原来原因出在这里:我们申明test时,用的属性修饰符retain。
当我们,使用self.test时,就使用了编译器为我们生成的setXXX方法。在该方法中retainCount被加1,所以,变为2.
这样的花,如果,我们在代码中,直接用
self.test=[[NSString alloc] initWithFormat:@"test"];
这行代码后,test变量的应用计数变成了2:
相当于:
首先,声明一个字符串对象,这时候,引用计数为1.
其次,再将test的值赋给self.test。(相当于,使用了setXXX方法,让retainCount,加1),导致应用计数变成2.
所以,在类中,如果,仅仅是指针赋值,(将一个对象的指针赋给另一个指针)尽量避免使用self.test进行赋值。这样会引起引用计数+1,容易引起释放内存泄漏。而直接用test来赋值。
也就是,在方法中,使用self.xxxx,进行赋值,就会使用编译器生成的setXXX方法,从而根据申明对象时的属性(copy,retain,assign)进行调用setXXX方法。
如果,只用属性之间来赋值(test= newValue;,就是不带self),那么,仅仅是指针之间的赋值。
第二种方式,就是带下划线的那种方式:
这样,让我们验证:
代码中,我们这么写:

1
test=[[NSString alloc] initWithFormat:@"test"];

我们会收到一个错误提示:

Object-C 声明属性为什么用下划线,代码规范和编程风格

看来,不能直接访问,test,而要用_test来代替。

不管它,那就用它推荐的方法,继续写完:

1
2
3
self.test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的应用计数:%d",[self.test retainCount]);
NSLog(@"_test的应用计数:%d",[_test retainCount]);

运行代码,输出如下:

1
2
2012-11-17 16:30:39.525 ArtTV[1042:14303] self.test的应用计数:2
2012-11-17 16:30:41.553 ArtTV[1042:14303] _test的应用计数:2

运行输出,很显然,应用计数为2,不是我们想要的结果。原因,跟我们讨论的第一种情况,是一样的。
这仅仅是一种调用的另一种方式:
默认情况,调用test,当我们申明test=_test;时候,那么在类内部使用_test
在使用_test,时候,也仅仅是指针的赋值。
使用self.test,就是要调用编译器生成的响应的getXXX,setXXX方法了。
为了证明我们的想法,我们将代码修改成这样:

1
2
3
_test=[[NSString alloc] initWithFormat:@"test"];
NSLog(@"self.test的应用计数:%d",[self.test retainCount]);
NSLog(@"_test的应用计数:%d",[_test retainCount]);

输出如下:

1
2
2012-11-17 16:36:09.089 ArtTV[1074:14303] self.test的应用计数:1
2012-11-17 16:36:10.701 ArtTV[1074:14303] _test的应用计数:1

关于,使用方式,我们就将到这里吧。亲自动手,实验一下。就明白其中的奥妙了!
但是,还没有结束!
另一个疑惑,也随之而来。
我们用那种方式好呢????
我看了一些帖子,说,苹果不提倡在我们定义的类中使用“_xxxx”,这种形式的属性声明,因为,在很多苹果提供的框架中,大量使用“_xxxx”这种属性声明。

——————————————————————————————————————
Cocoa编码指南: 编程公约:

避免使用下划线作为前缀,特别是在私有方法中。苹果公司保留并使用本公约。使用第三方可能会导致命名空间冲突;自己不知不觉中,可能会覆盖现有的私有方法,带来灾难性的后果。
——————————————————————————————————————

打开一个看看:

Object-C 声明属性为什么用下划线,代码规范和编程风格
确实,如此啊!
在这种情况下,如果,我们,继承了某个框架类,并且无意间声明的“_xxxx”属性跟父类的相同,那么可能就会覆盖掉,从而引发不可估计的后果!
但是,我看苹果帮助文档中写的许多例子也在用“_xxxx”这种方式啊!所以,我觉的可以用,但要注意!Object-C 声明属性为什么用下划线,代码规范和编程风格
为什么是可以用呢?

因为个人感觉:“_xxxx”确实很好用!
1.从风格上表明类的内部变量。
2.意在指明这个变量是内部变量(类外部不会使用。。。)
3.外部访问用obj.xxxxx, 避免对类变量的直接访问。
4.这样的话,要是需要直接引用变量就用_xxxx,当需要用get,set方法时,就用self.xxx。
汇成一句话:
下划线和非下划线的使用,可以说是一种习惯问题吧。不用太过于纠结!
默认情况下,@synthesize name;编译器为我们生成的get,set方法中所使用的变量名称,跟我们申明的变量名称时一样的(仅仅用self.name和name来区分确实不够理想)。
但是,当我们用@synthesize name=_name;时,就为属性取了一个别名,那样的话,指针变量,跟编译器生成的get,set方法为属性赋值时就容易区分了!
文章参考:http://blog.csdn.net/qq515383106/article/details/8508611

首先要从一个我遇到的问题谈起,一个基于NavigationBar的App,开始时我有一个UITableViewController,其中每个UITableViewCell点击后都会push另一个ViewController,每次点击Cell的时候,Cell都会被选中,当从push的ViewController返回的时候选中的Cell便会自动取消选中(有动画效果)。后来由于某些原因我把这个UITableViewController改成了UIViewController,之后就产生了一个问题:每次返回到TableView的时候,之前选中的Cell不能自动取消选中,经过查找得知:

UITableViewController有一个clearsSelectionOnViewWillAppear的property,

而当把UITableViewController修改成UIViewController后,这个属性自然就不存在了,因此我们必须手动添加取消选中的功能,方法很简单,在viewWillAppear方法中加入:

1
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];

即可,估计UITableViewController也是用类似的方法来实现取消选中的功能的。

rules类验证器方法,这个感觉蛮重要的,自己找资料的时候无意间发现了,感觉挺全的,记录一下好了。

解说代码如下:

1
2
3
4
5
6
7
8
9
10
public function rules()
{
return [
['project_id, type_id, status_id, owner_id, requester_id,', 'numerical', 'integerOnly' => true],
['name', 'length', 'max' => 256],
['description', 'length', 'max' => 2000],
['create_time,create_user_id,update_user_id, update_time', 'safe'],
['id, name, description, project_id, type_id, status_id, owner_id', 'on' => 'search'],
];
}
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
//required: 必填
['title,content', 'required'],

//match: 正则表达式验证
['birthday', 'match', 'pattern' => '%^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$%', 'allowEmpty' => true, 'message' => '生日必须是年-月-日格式'],

//email:邮箱格式验证
['user_mail', 'email'],

//url:URL格式验证
['user', 'url'],

//unique:唯一性验证
['username', 'unique', 'caseSensitive' => false, 'className' => 'user', 'message' => '用户名"{value}"已经被注册,请更换'],
//caseSensitive 定义大小写是否敏感

//compare:一致性验证
['repassword', 'compare', 'compareAttribute' => 'password', 'message' => '两处输入的密码并不一致'],

//length:长度验证

//in: 验证此属性值在列表之中(通过range指定)。

//numerical: 验证此属性的值是一个数字

//captcha: 验证属性值和验证码中显示的一致
['verifyCode', 'captcha'],

//type: 验证属性的类型是否为type所指定的类型.

//file: 验证一个属性是否接收到一个有效的上传文件

//default: 属性指定默认值

//exist: 验证属性值在数据库中是否存在

//boolean: 验证布尔属性值

//date: 检验此属性是否描述了一个日期、时间或日期时间

//safe: 属性标志为在批量赋值时是安全的。

//unsafe: 标志为不安全,所以他们不能被批量赋值。
0%