iOS 三种录制视频方式
随着每一代 iPhone 处理能力和相机硬件配置的提高,使用它来捕获视频也变得更加有意思。它们小巧,轻便,低调,而且与专业摄像机之间的差距已经变得非常小,小到在某些情况下,iPhone 可以真正替代它们。
这篇文章讨论了关于如何配置视频捕获管线 (pipeline) 和最大限度地利用硬件性能的一些不同选择。 这里有个使用了不同管线的样例 app,可以在 GitHub 查看。
UIImagePickerController
目前,将视频捕获集成到你的应用中的最简单的方法是使用 UIImagePickerController。这是一个封装了完整视频捕获管线和相机 UI 的 view controller。
在实例化相机之前,首先要检查设备是否支持相机录制:
Objective-C
1
2
3
4
5
6
7
8
|
if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
NSArray *availableMediaTypes = [UIImagePickerController
availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
if ([availableMediaTypes containsObject:(NSString *)kUTTypeMovie]) {
// 支持视频录制
}
}
|
然后创建一个 UIImagePickerController 对象,设置好代理便于进一步处理录制好的视频 (比如存到相册) 以及对于用户关闭相机作出响应:
Objective-C
1
2
3
4
|
UIImagePickerController *camera = [UIImagePickerController new];
camera.sourceType = UIImagePickerControllerSourceTypeCamera;
camera.mediaTypes = @[(NSString *)kUTTypeMovie];
camera.delegate = self;
|
这是你实现一个功能完善的摄像机所需要写的所有代码。
相机配置
UIImagePickerController 提供了额外的配置选项。
通过设置 cameraDevice 属性可以选择一个特定的相机。这是一个 UIImagePickerControllerCameraDevice 枚举,默认情况下是 UIImagePickerControllerCameraDeviceRear,你也可以把它设置为 UIImagePickerControllerCameraDeviceFront。每次都应事先确认你想要设置的相机是可用的:
Objective-C
1
2
3
4
|
UIImagePickerController *camera = …
if ([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]) {
[camera setCameraDevice:UIImagePickerControllerCameraDeviceFront];
}
|
videoQuality 属性用于控制录制视频的质量。它允许你设置一个特定的编码预设,从而改变视频的比特率和分辨率。以下是六种预设:
Objective-C
1
2
3
4
5
6
7
8
9
|
enum {
UIImagePickerControllerQualityTypeHigh = 0,
UIImagePickerControllerQualityTypeMedium = 1, // default value
UIImagePickerControllerQualityTypeLow = 2,
UIImagePickerControllerQualityType640x480 = 3,
UIImagePickerControllerQualityTypeIFrame1280x720 = 4,
UIImagePickerControllerQualityTypeIFrame960x540 = 5
};
typedef NSUInteger UIImagePickerControllerQualityType;
|
前三种为相对预设 (low, medium, high)。这些预设的编码配置会因设备不同而不同。如果选择 high,那么你选定的相机会提供给你该设备所能支持的最高画质。后面三种是特定分辨率的预设 (640×480 VGA, 960×540 iFrame, 和 1280×720 iFrame)。
自定义 UI
就像上面提到的,UIImagePickerController 自带一套相机 UI,可以直接使用。然而,你也可以自定义相机的控件,通过隐藏默认控件,然后创建带有控件的自定义视图,并覆盖在相机预览图层上面:
Objective-C
1
2
3
|
UIView *cameraOverlay = …
picker.showsCameraControls = NO;
picker.cameraOverlayView = cameraOverlay;
|
然后你需要将你覆盖层上的控件关联上 UIImagePickerController 的控制方法 (比如,startVideoCapture 和 stopVideoCapture)。
AVFoundation
如果你想要更多关于处理捕获视频的方法,而这些方法是 UIImagePickerController 所不能提供的,那么你需要使用 AVFoundation。
AVFoundation 中关于视频捕获的主要的类是 AVCaptureSession。它负责调配影音输入与输出之间的数据流:
- AVCaptureSession setup
使用一个 capture session,你需要先实例化,添加输入与输出,接着启动从输入到输出之间的数据流:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
AVCaptureSession *captureSession = [AVCaptureSession new];
AVCaptureDeviceInput *cameraDeviceInput = …
AVCaptureDeviceInput *micDeviceInput = …
AVCaptureMovieFileOutput *movieFileOutput = …
if ([captureSession canAddInput:cameraDeviceInput]) {
[captureSession addInput:cameraDeviceInput];
}
if ([captureSession canAddInput:micDeviceInput]) {
[captureSession addInput:micDeviceInput];
}
if ([captureSession canAddOutput:movieFileOutput]) {
[captureSession addOutput:movieFileOutput];
}
[captureSession startRunning];
|
(为了简单起见,调度队列 (dispatch queue) 的相关代码已经从上面那段代码中省略了。所有对 capture session 的调用都是阻塞的,因此建议将它们分配到后台串行队列中。)
capture session 可以通过一个 sessionPreset
来进一步配置,这可以用来指定输出质量的等级。有 11 种不同的预设模式:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
|
NSString *const AVCaptureSessionPresetPhoto;
NSString *const AVCaptureSessionPresetHigh;
NSString *const AVCaptureSessionPresetMedium;
NSString *const AVCaptureSessionPresetLow;
NSString *const AVCaptureSessionPreset352x288;
NSString *const AVCaptureSessionPreset640x480;
NSString *const AVCaptureSessionPreset1280x720;
NSString *const AVCaptureSessionPreset1920x1080;
NSString *const AVCaptureSessionPresetiFrame960x540;
NSString *const AVCaptureSessionPresetiFrame1280x720;
NSString *const AVCaptureSessionPresetInputPriority;
|
第一个代表高像素图片输出。 接下来的九个和之前我们在设置 UIImagePickerController 的 videoQuality 时看到过的 UIImagePickerControllerQualityType 选项非常相似,不同的是,这里有一些额外可用于 capture session 的预设。 最后一个 (AVCaptureSessionPresetInputPriority) 代表 capture session 不去控制音频与视频输出设置。而是通过已连接的捕获设备的 activeFormat 来反过来控制 capture session 的输出质量等级。在下一节,我们将会看到更多关于设备和设备格式的细节。
- 输入
AVCaptureSession 的输入其实就是一个或多个的 AVCaptureDevice 对象,这些对象通过 AVCaptureDeviceInput 连接上 capture session。
我们可以使用 [AVCaptureDevice devices] 来寻找可用的捕获设备。以 iPhone 6 为例:
Objective-C
1
2
3
4
5
6
7
8
9
|
(
“<AVCaptureFigVideoDevice: 0x136514db0 [Back Camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>”,
“<AVCaptureFigVideoDevice: 0x13660be80 [Front Camera][com.apple.avfoundation.avcapturedevice.built-in_video:1]>”,
“<AVCaptureFigAudioDevice: 0x174265e80 [iPhone Microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]>”
)
|
- 视频输入
配置相机输入,需要实例化一个 AVCaptureDeviceInput 对象,参数是你期望的相机设备,然后把它添加到 capture session:
Objective-C
1
2
3
4
5
6
7
8
9
|
AVCaptureSession *captureSession = …
AVCaptureDevice *cameraDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error;
AVCaptureDeviceInput *cameraDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:cameraDevice error:&error];
if ([captureSession canAddInput:input]) {
[captureSession addInput:cameraDeviceInput];
}
|
如果上面提到的 capture session 预设列表里能满足你的需求,那你就不需要做更多的事情了。如果不够,比如你想要高的帧率,你将需要配置具体的设备格式。一个视频捕获设备有许多设备格式,每个都带有特定的属性和功能。下面是对于 iPhone6 的后置摄像头的一些例子 (一共有 22 种可用格式):
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
格式 分辨率 FPS HRSI FOV VIS 最大放大比例 Upscales AF ISO SS HDR
420v 1280x720 5 - 240 1280x720 54.626 YES 49.12 1.09 1 29.0 - 928 0.000003-0.200000 NO
420f 1280x720 5 - 240 1280x720 54.626 YES 49.12 1.09 1 29.0 - 928 0.000003-0.200000 NO
420v 1920x1080 2 - 30 3264x1836 58.040 YES 95.62 1.55 2 29.0 - 464 0.000013-0.500000 YES
420f 1920x1080 2 - 30 3264x1836 58.040 YES 95.62 1.55 2 29.0 - 464 0.000013-0.500000 YES
420v 1920x1080 2 - 60 3264x1836 58.040 YES 95.62 1.55 2 29.0 - 464 0.000008-0.500000 YES
420f 1920x1080 2 - 60 3264x1836 58.040 YES 95.62 1.55 2 29.0 - 464 0.000008-0.500000 YES
格式 = 像素格式
FPS = 支持帧数范围
HRSI = 高像素静态图片尺寸
FOV = 视角
VIS = 该格式支持视频防抖
Upscales = 加入数字 upscaling 时的放大比例
AF = 自动对焦系统(1 是反差对焦,2 是相位对焦)
ISO = 支持感光度范围
SS = 支持曝光时间范围
HDR = 支持高动态范围图像
|
通过上面的那些格式,你会发现如果要录制 240 帧每秒的视频的话,可以根据想要的像素格式选用第一个或第二个格式。另外若是要捕获 1920×1080 的分辨率的视频的话,是不支持 240 帧每秒的。
配置一个具体设备格式,你首先需要调用 lockForConfiguration: 来获取设备的配置属性的独占访问权限。接着你简单地使用 setActiveFormat: 来设置设备的捕获格式。这将会自动把 capture session 的预设设置为 AVCaptureSessionPresetInputPriority。
一旦你设置了预想的设备格式,你就可以在这种设备格式的约束参数范围内进行进一步的配置了。
对于视频捕获的对焦,曝光和白平衡的设置,与图像捕获时一样,具体可参考第 21 期“iOS 上的相机捕捉”。除了那些,这里还有一些视频特有的配置选项。
你可以用捕获设备的 activeVideoMinFrameDuration 和 activeVideoMaxFrameDuration 属性设置帧速率,一帧的时长是帧速率的倒数。设置帧速率之前,要先确认它是否在设备格式所支持的范围内,然后锁住捕获设备来进行配置。为了确保帧速率恒定,可以将最小与最大的帧时长设置成一样的值:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
NSError *error;
CMTime frameDuration = CMTimeMake(1, 60);
NSArray *supportedFrameRateRanges = [device.activeFormat videoSupportedFrameRateRanges];
BOOL frameRateSupported = NO;
for (AVFrameRateRange *range in supportedFrameRateRanges) {
if (CMTIME_COMPARE_INLINE(frameDuration, >=, range.minFrameDuration) &&
CMTIME_COMPARE_INLINE(frameDuration, <=, range.maxFrameDuration)) {
frameRateSupported = YES;
}
}
if (frameRateSupported && [device lockForConfiguration:&error]) {
[device setActiveVideoMaxFrameDuration:frameDuration];
[device setActiveVideoMinFrameDuration:frameDuration];
[device unlockForConfiguration];
}
|
视频防抖 是在 iOS 6 和 iPhone 4S 发布时引入的功能。到了 iPhone 6,增加了更强劲和流畅的防抖模式,被称为影院级的视频防抖动。相关的 API 也有所改动 (目前为止并没有在文档中反映出来,不过可以查看头文件)。防抖并不是在捕获设备上配置的,而是在 AVCaptureConnection 上设置。由于不是所有的设备格式都支持全部的防抖模式,所以在实际应用中应事先确认具体的防抖模式是否支持:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
|
AVCaptureDevice *device = ...;
AVCaptureConnection *connection = ...;
AVCaptureVideoStabilizationMode stabilizationMode = AVCaptureVideoStabilizationModeCinematic;
if ([device.activeFormat isVideoStabilizationModeSupported:stabilizationMode]) {
[connection setPreferredVideoStabilizationMode:stabilizationMode];
}
|
iPhone 6 的另一个新特性就是视频 HDR (高动态范围图像),它是“高动态范围的视频流,与传统的将不同曝光度的静态图像合成成一张高动态范围图像的方法完全不同”,它是内建在传感器中的。有两种方法可以配置视频 HDR:直接将 capture device 的 videoHDREnabled 设置为启用或禁用,或者使用 automaticallyAdjustsVideoHDREnabled 属性来留给系统处理。
技术参考:iPhone 6 和 iPhone Plus 的新 AV Foundation 相机特性
- 音频输入
之前展示的捕获设备列表里面只有一个音频设备,你可能觉得奇怪,毕竟 iPhone 6 有 3 个麦克风。然而因为有时会放在一起使用,便于优化性能,因此可能被当做一个设备来使用。例如在 iPhone 5 及以上的手机录制视频时,会同时使用前置和后置麦克风,用于定向降噪。
Technical Q&A: AVAudioSession – Microphone Selection
大多数情况下,设置成默认的麦克风配置即可。后置麦克风会自动搭配后置摄像头使用 (前置麦克风则用于降噪),前置麦克风和前置摄像头也是一样。
然而想要访问和配置单独的麦克风也是可行的。例如,当用户正在使用后置摄像头捕获场景的时候,使用前置麦克风来录制解说也应是可能的。这就要依赖于 AVAudioSession。 为了变更要访问的音频,audio session 首先需要设置为支持这样做的类别。然后我们需要遍历 audio session 的输入端口和端口数据来源,来找到我们想要的麦克风:
Objective-C
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
|
// 配置 audio session
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[audioSession setActive:YES error:nil];
// 寻找期望的输入端口
NSArray* inputs = [audioSession availableInputs];
AVAudioSessionPortDescription *builtInMic = nil;
for (AVAudioSessionPortDescription* port in inputs) {
if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
builtInMic = port;
break;
}
}
// 寻找期望的麦克风
for (AVAudioSessionDataSourceDescription* source in builtInMic.dataSources) {
if ([source.orientation isEqual:AVAudioSessionOrientationFront]) {
[builtInMic setPreferredDataSource:source error:nil];
[audioSession setPreferredInput:builtInMic error:&error];
break;
}
}
|
除了设置非默认的麦克风配置,你也可以使用 AVAudioSession 来配置其他音频设置,比如音频增益和采样率等。
- 访问权限
有件事你需要记住,访问相机和麦克风需要先获得用户授权。当你给视频或音频创建第一个 AVCaptureDeviceInput 对象时,iOS 会自动弹出一次对话框,请求用户授权,但你最好还是自己实现下。之后你就可以在还没有被授权的时候,使用相同的代码来提示用户进行授权。当用户未授权时,对于录制视频或音频的尝试,得到的将是黑色画面和无声。
- 输出
输入配置完了,现在把我们的注意力转向 capture session 的输出。
AVCaptureMovieFileOutput
将视频写入文件,最简单的选择就是使用 AVCaptureMovieFileOutput 对象。把它作为输出添加到 capture session 中,就可以将视频和音频写入 QuickTime 文件,这只需很少的配置。
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
|
AVCaptureMovieFileOutput *movieFileOutput = [AVCaptureMovieFileOutput new];
if([captureSession canAddOutput:movieFileOutput]){
[captureSession addOutput:movieFileOutput];
}
// 开始录制
NSURL *outputURL = …
[movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
|
当实际的录制开始或停止时,想要接收回调的话就必须要一个录制代理。当录制停止时,输出通常还在写入数据,等它完成之后会调用代理方法。
AVCaptureMovieFileOutput 有一些其他的配置选项,比如在某段时间后,在达到某个指定的文件尺寸时,或者当设备的最小磁盘剩余空间达到某个阈值时停止录制。如果你还需要更多设置,比如自定义视频音频的压缩率,或者你想要在写入文件之前,处理视频音频的样本,那么你需要一些更复杂的操作。
AVCaptureDataOutput 和 AVAssetWriter
如果你想要对影音输出有更多的操作,你可以使用 AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput 而不是我们上节讨论的 AVCaptureMovieFileOutput。
这些输出将会各自捕获视频和音频的样本缓存,接着发送到它们的代理。代理要么对采样缓冲进行处理 (比如给视频加滤镜),要么保持原样传送。使用 AVAssetWriter 对象可以将样本缓存写入文件:
Using an AVAssetWriter
配置一个 asset writer 需要定义一个输出 URL 和文件格式,并添加一个或多个输入来接收采样的缓冲。我们还需要将输入的 expectsMediaInRealTime属性设置为 YES,因为它们需要从 capture session 实时获得数据。
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
NSURL *url = …;
AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeMPEG4 error:nil];
AVAssetWriterInput *videoInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:nil];
videoInput.expectsMediaDataInRealTime = YES;
AVAssetWriterInput *audioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:nil];
audioInput.expectsMediaDataInRealTime = YES;
if ([assetWriter canAddInput:videoInput]) {
[assetWriter addInput:videoInput];
}
if ([assetWriter canAddInput:audioInput]) {
[assetWriter addInput:audioInput];
}
|
(这里推荐将 asset writer 派送到后台串行队列中调用。)
在上面的示例代码中,我们将 asset writer 的 outputSettings 设置为 nil。这就意味着附加上来的样本不会再被重新编码。如果你确实想要重新编码这些样本,那么需要提供一个包含具体输出参数的字典。关于音频输出设置的键值被定义在这里, 关于视频输出设置的键值定义在这里。
为了更简单点,AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput 分别带有 recommendedVideoSettingsForAssetWriterWithOutputFileType: 和 recommendedAudioSettingsForAssetWriterWithOutputFileType: 方法,可以生成与 asset writer 兼容的带有全部键值对的字典。所以你可以通过在这个字典里调整你想要重写的属性,来简单地定义你自己的输出设置。比如,增加视频比特率来提高视频质量等。
或者,你也可以使用 AVOutputSettingsAssistant 来配置输出设置的字典,但是从我的经验来看,使用上面的方法会更好,它们会提供更实用的输出设置,比如视频比特率。另外,AVOutputSettingsAssistant 似乎存在一些缺点,例如,当你改变希望的视频的帧速率时,视频的比特率并不会改变。
实时预览
当使用 AVFoundation 来做图像捕获时,我们必须提供一套自定义的用户界面。其中一个关键的相机交互组件是实时预览图。最简单的实现方式是通过把 AVCaptureVideoPreviewLayer 对象作为一个 sublayer 加到相机图层上去:
Objective-C
1
2
3
4
5
|
AVCaptureSession *captureSession = ...;
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
UIView *cameraView = ...;
previewLayer.frame = cameraView.bounds;
[cameraView.layer addSublayer:previewLayer];
|
如果你想要更进一步操作,比如,在实时预览图加滤镜,你需要将 AVCaptureVideoDataOutput 对象加到 capture session,并且使用 OpenGL 展示画面,具体可查看该文“iOS 上的相机捕捉”
总结
有许多不同的方法可以给 iOS 上的视频捕获配置管线,从最直接的 UIImagePickerController,到精密配合的 AVCaptureSession 与 AVAssetWriter。如何抉择取决于你的项目要求,比如期望的视频质量和压缩率,或者是你想要展示给用户的相机控件。
iOS 三种录制视频方式的更多相关文章
- 根据分析查看相关知识点分析iOS 三种录制视频方式
这篇文章讨论了关于如何配置视频捕获管线 (pipeline) 和最大限度地利用硬件性能的一些不同选择. 这里有个使用了不同管线的样例 app,可以在 GitHub 查看. 第一种:UIImagePic ...
- IOS三种归档(NSKeyArchieve)的总结
IOS三种归档(NSKeyArchieve)的总结 归档是一种IOS中常用来存储文件的一种方法,在面向对象的语言中,归档也就实际上可以将一切对象存储在文件中,以下是IOS开发中常见的三种文件归档方式, ...
- VMware的三种网络连接方式区别
关于VMware的三种网络连接方式,NAT,Bridged,Host-Only ,在刚接触的时候通常会遇到主机Ping不通虚拟机而虚拟机能Ping得通主机:主机与虚拟机互不相通等等网络问题.本文就这三 ...
- .NET中的三种接口实现方式
摘自:http://www.cnblogs.com/zhangronghua/archive/2009/11/25/1610713.html 一般来说.NET提供了三种不同的接口实现方式,分别为隐式接 ...
- Apache Spark探秘:三种分布式部署方式比较
转自:链接地址: http://dongxicheng.org/framework-on-yarn/apache-spark-comparing-three-deploying-ways/ 目 ...
- [转]详述DHCP服务器的三种IP分配方式
DHCP就是动态主机配置协议(Dynamic Host Configuration Protocol),它的目的就是为了减轻TCP/IP网络的规划.管理和维护的负担,解决IP地址空间缺乏问题.这种网络 ...
- Binding 中 Elementname,Source,RelativeSource 三种绑定的方式
在WPF应用的开发过程中Binding是一个非常重要的部分. 在实际开发过程中Binding的不同种写法达到的效果相同但事实是存在很大区别的. 这里将实际中碰到过的问题做下汇总记录和理解. 1. so ...
- windows phone 三种数据共享的方式(8)
原文:windows phone 三种数据共享的方式(8) 本节实现的内容是数据共享,实现的效果描述:首先是建立两个页面,当页面MainPage通过事件导航到页面SecondPage是,我们需要将Ma ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十二 || 三种跨域方式比较,DTOs(数据传输对象)初探
更新反馈 1.博友@落幕残情童鞋说到了,Nginx反向代理实现跨域,因为我目前还没有使用到,给忽略了,这次记录下,为下次补充.此坑已填 2.提示:跨域的姊妹篇——<三十三║ ⅖ 种方法实现完美跨 ...
随机推荐
- Windows 调色板
目录 第1章调色板 1 1.1 为什么要使用调色板 1 1.2 使用调色板 2 1.2.1 创建逻辑调色板 2 1.2.2 使用 3 1.2.3 销毁逻辑调色板 4 ...
- linux笔记:linux常用命令-目录和文件处理命令
命令格式: 命令 [-选项] [参数] 例: ls -la /etc 注意:个别命令的使用不遵循此格式. 目录处理命令:ls(列目录) 更多选项: -h 以kb等利于人阅读的方式取代字节显示文件大小 ...
- sass less
CSS 预处理器技术已经非常的成熟,而且也涌现出了越来越多的 CSS 的预处理器框架.本文向你介绍使用最为普遍的三款 CSS 预处理器框架,分别是 Sass.Less CSS.Stylus. 首先我们 ...
- 5.4.1 Selenium2启动空浏览器
在Web自动化测试中,必须考虑不同浏览器对网站的兼容性测试,所以我们首先介绍如何用webDriver代码打开不同的浏览器. 本节介绍的是在Selenium2启动浏览器时,启动一个干净的没有任务插件及c ...
- python 练习 23
python 编程中 while 语句用于循环执行程序,即在某条件下,循环执行某段程序,以处理需要重复处理的相同任务.其基本形式为: while 判断条件: 执行语句…… 执行语句可以是单个语句或语句 ...
- SSH基础(2)
linux下配置 ssh运行的参数详解:
- Same Tree [LeetCode]
Problem Description: http://oj.leetcode.com/problems/same-tree/ class Solution { public: bool isSame ...
- Spring使用RowMapper将数据中的每一行封装成用户定义的类
1.dao public interface MapperSelecteAllEmpDao { public List<Emp> all(); } 2.实现类 public class M ...
- 批次更新BAPI_OBJCL_CHANGE
FORM frm_edit_batch TABLES pt_field STRUCTURE dfies USING ps_batch TYPE ty_batch CHANGING ps_rturn T ...
- 关于C#迭代器
>1 IEnumerator与IEnumerable IEnumerator与IEnumerable两个接口是用于实现迭代器的接品只要实现了IEnumerable就可以用foreach,linq ...